浏览代码

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

TonyKang 5 年之前
父节点
当前提交
33c75bc678
共有 100 个文件被更改,包括 2565 次插入1182 次删除
  1. 31 0
      .github/workflows/nodejs.yml
  2. 7 7
      app/const/deal_pay.js
  3. 10 1
      app/const/setting.js
  4. 18 1
      app/const/spread.js
  5. 4 0
      app/const/tender_info.js
  6. 1 0
      app/controller/ledger_audit_controller.js
  7. 142 20
      app/controller/material_controller.js
  8. 14 5
      app/controller/measure_controller.js
  9. 42 0
      app/controller/setting_controller.js
  10. 249 174
      app/controller/stage_controller.js
  11. 31 13
      app/controller/tender_controller.js
  12. 65 0
      app/controller/wechat_controller.js
  13. 121 101
      app/extend/helper.js
  14. 6 1
      app/lib/bills_pos_convert.js
  15. 2 0
      app/lib/ledger.js
  16. 28 3
      app/lib/rpt_data_analysis.js
  17. 39 8
      app/lib/stage_im.js
  18. 18 14
      app/middleware/session_auth.js
  19. 2 1
      app/public/js/gcl_gather.js
  20. 16 1
      app/public/js/ledger.js
  21. 4 0
      app/public/js/ledger_audit.js
  22. 4 0
      app/public/js/ledger_bwtz.js
  23. 14 7
      app/public/js/ledger_gather.js
  24. 130 0
      app/public/js/material_file.js
  25. 6 0
      app/public/js/measure_compare.js
  26. 8 0
      app/public/js/path_tree.js
  27. 19 0
      app/public/js/revise.js
  28. 5 0
      app/public/js/revise_history.js
  29. 24 0
      app/public/js/setting.js
  30. 21 52
      app/public/js/shares/cs_tools.js
  31. 16 2
      app/public/js/shares/sjs_setting.js
  32. 36 11
      app/public/js/spreadjs_rela/spreadjs_zh.js
  33. 173 87
      app/public/js/stage.js
  34. 4 0
      app/public/js/stage_bwtz.js
  35. 3 0
      app/public/js/stage_change.js
  36. 4 0
      app/public/js/stage_compare.js
  37. 3 0
      app/public/js/stage_gather.js
  38. 111 11
      app/public/js/stage_im.js
  39. 2 0
      app/public/js/stage_pay.js
  40. 11 3
      app/public/js/tender_list_progress.js
  41. 17 0
      app/router.js
  42. 2 1
      app/service/deal_bills.js
  43. 12 8
      app/service/material.js
  44. 6 0
      app/service/material_audit.js
  45. 84 0
      app/service/material_file.js
  46. 1 1
      app/service/material_list.js
  47. 3 3
      app/service/pos.js
  48. 5 4
      app/service/report_memory.js
  49. 1 1
      app/service/rpt_gather_memory.js
  50. 72 0
      app/service/setting_show.js
  51. 2 2
      app/service/stage.js
  52. 4 4
      app/service/stage_att.js
  53. 57 0
      app/service/stage_audit.js
  54. 2 2
      app/service/stage_bills.js
  55. 4 2
      app/service/stage_bonus.js
  56. 1 0
      app/service/stage_detail.js
  57. 9 10
      app/service/stage_jgcl.js
  58. 8 6
      app/service/stage_other.js
  59. 13 6
      app/service/stage_pos.js
  60. 8 11
      app/service/tender.js
  61. 1 1
      app/view/layout/menu.ejs
  62. 1 0
      app/view/ledger/audit.ejs
  63. 1 0
      app/view/ledger/bwtz.ejs
  64. 1 0
      app/view/ledger/explode.ejs
  65. 2 1
      app/view/ledger/gather.ejs
  66. 51 0
      app/view/material/file.ejs
  67. 54 0
      app/view/material/file_modal.ejs
  68. 7 0
      app/view/material/material_sub_menu.ejs
  69. 4 1
      app/view/measure/compare.ejs
  70. 23 13
      app/view/measure/stage.ejs
  71. 1 0
      app/view/revise/history.ejs
  72. 1 0
      app/view/revise/info.ejs
  73. 49 0
      app/view/setting/show.ejs
  74. 1 1
      app/view/stage/audit_btn.ejs
  75. 1 1
      app/view/stage/audit_modal.ejs
  76. 3 0
      app/view/stage/bwtz.ejs
  77. 1 0
      app/view/stage/compare.ejs
  78. 2 1
      app/view/stage/gather.ejs
  79. 18 14
      app/view/stage/index.ejs
  80. 12 3
      app/view/stage/manager.ejs
  81. 37 9
      app/view/stage/manager_modal.ejs
  82. 9 0
      app/view/stage/modal.ejs
  83. 2 1
      app/view/stage/pay.ejs
  84. 9 0
      app/view/stage/stage_sub_menu.ejs
  85. 9 0
      app/view/stage/stage_sub_mini_menu.ejs
  86. 1 1
      app/view/stage_extra/sub_menu.ejs
  87. 46 4
      app/view/tender/detail.ejs
  88. 13 1
      app/view/tender/detail_modal.ejs
  89. 1 0
      app/view/tender/index.ejs
  90. 1 0
      app/view/tender/progress.ejs
  91. 9 5
      builder_report_index_define.js
  92. 19 0
      config/config.default.js
  93. 6 0
      config/menu.js
  94. 5 1
      config/plugin.js
  95. 386 360
      config/web.js
  96. 1 0
      package.json
  97. 12 0
      sql/update.sql
  98. 10 1
      test/app/extend/helper.test.js
  99. 0 179
      test/app/service/report_memory.test.js
  100. 0 0
      test/app/service/rpt_gather_memory.test.js

+ 31 - 0
.github/workflows/nodejs.yml

@@ -0,0 +1,31 @@
+# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
+# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
+
+name: Node.js CI
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+    branches: [ master ]
+
+jobs:
+  build:
+
+    runs-on: ${{ matrix.os }}
+
+    strategy:
+      matrix:
+        node-version: [6.x, 8.x]
+        os: [ubuntu-latest, windows-latest, macos-latest]
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Use Node.js ${{ matrix.node-version }}
+      uses: actions/setup-node@v1
+      with:
+        node-version: ${{ matrix.node-version }}
+    - run: npm i -g npminstall && npminstall
+    - run: npm run ci
+      env:
+        CI: true

+ 7 - 7
app/const/deal_pay.js

@@ -57,13 +57,13 @@ const deadlineType = {
 };
 
 const chapterDetail = [
-    {name: '清单 第100章 总则', cType: 1, serialNo: 1, filter: '^1[0-9]{2}-'},
-    {name: '清单 第200章 路基', cType: 1, serialNo: 2, filter: '^2[0-9]{2}-'},
-    {name: '清单 第300章 路面', cType: 1, serialNo: 3, filter: '^3[0-9]{2}-'},
-    {name: '清单 第400章 桥梁、涵洞', cType: 1, serialNo: 4, filter: '^4[0-9]{2}-'},
-    {name: '清单 第500章 隧道', cType: 1, serialNo: 5, filter: '^5[0-9]{2}-'},
-    {name: '清单 第600章 安全设施及预埋管线', cType: 1, serialNo: 6, filter: '^6[0-9]{2}-'},
-    {name: '清单 第700章 绿化及环境保护', cType: 1, serialNo: 7, filter: '^7[0-9]{2}-'},
+    {name: '清单 第100章 总则', cType: 1, serialNo: 1, filter: '^[^0-9]*1[0-9]{2}-'},
+    {name: '清单 第200章 路基', cType: 1, serialNo: 2, filter: '^[^0-9]*2[0-9]{2}-'},
+    {name: '清单 第300章 路面', cType: 1, serialNo: 3, filter: '^[^0-9]*3[0-9]{2}-'},
+    {name: '清单 第400章 桥梁、涵洞', cType: 1, serialNo: 4, filter: '^[^0-9]*4[0-9]{2}-'},
+    {name: '清单 第500章 隧道', cType: 1, serialNo: 5, filter: '^[^0-9]*5[0-9]{2}-'},
+    {name: '清单 第600章 安全设施及预埋管线', cType: 1, serialNo: 6, filter: '^[^0-9]*6[0-9]{2}-'},
+    {name: '清单 第700章 绿化及环境保护', cType: 1, serialNo: 7, filter: '^[^0-9]*7[0-9]{2}-'},
     {name: '未计入章节清单合计', cType: 21, serialNo: 8},
     {name: '清单小计(A)', cType: 11, serialNo: 9},
     {name: '非清单项费用(B)', cType: 31, serialNo: 10},

+ 10 - 1
app/const/setting.js

@@ -16,9 +16,18 @@ const cTypeStr = [];
 cTypeStr[cType.dropDown] = '下拉菜单';
 // cTypeStr[cType.radio] = '单选框';
 
+// 显示设置-路由列表
+const listPath = [
+    { label_name: '计量进度', path: '/list/progress', is_default: false }, // 计量进度
+    { label_name: '标段列表', path: '/list', is_default: false }, // 标段列表
+    { label_name: '金额概况', path: '/list/info', is_default: false }, // 金额概况
+];
+
+
 module.exports = {
     cType: {
         key: cType,
         text: cTypeStr,
     },
-}
+    listPath,
+};

+ 18 - 1
app/const/spread.js

@@ -12,6 +12,7 @@ const tzWithoutCols = ['deal_qty', 'deal_tp'];
 const dgnCols = ['dgn_qty1', 'dgn_qty2', 'dgn_price'];
 const clCols = ['sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp', 'quantity', 'total_price'];
 const stageDgnCols = ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2', 'final_dgn_price'];
+const realCompleteCols = ['real_qty', 'estimate_qty'];
 
 const withCl = {
     ledger: {
@@ -55,6 +56,7 @@ const withCl = {
         emptyRows: 3,
         headRows: 2,
         headRowHeight: [25, 25],
+        headColWidth: [30],
         defaultRowHeight: 21,
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
@@ -93,6 +95,7 @@ const withoutCl = {
         emptyRows: 3,
         headRows: 1,
         headRowHeight: [32],
+        headColWidth: [30],
         defaultRowHeight: 21,
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
@@ -203,6 +206,8 @@ const stageTz = {
             {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 180, formatter: '@', readOnly: true},
             {title: '位置', colSpan: '1', rowSpan: '2', field: 'position', hAlign: 0, width: 60, formatter: '@', readOnly: true},
             {title: '台账数量', colSpan: '1', rowSpan: '2', field: 'quantity', hAlign: 2, width: 60, formatter: '@', readOnly: true},
+            {title: '现场实际数量', colSpan: '1', rowSpan: '2', field: 'real_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '预计变更数量', colSpan: '1', rowSpan: '2', field: 'estimate_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             {title: '本期计量|合同', colSpan: '3|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: 'gather_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
@@ -215,6 +220,7 @@ const stageTz = {
         emptyRows: 3,
         headRows: 2,
         headRowHeight: [25, 25],
+        headColWidth: [30],
         defaultRowHeight: 21,
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
@@ -271,6 +277,8 @@ const stageCl = {
             {title: '|设计错漏增减', colSpan: '1', rowSpan: '|1', field: 'sjcl_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|其他错漏增减', colSpan: '1', rowSpan: '|1', field: 'qtcl_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|小计', colSpan: '1', rowSpan: '|1', field: 'quantity', hAlign: 2, width: 60, readOnly: true},
+            {title: '现场实际数量', colSpan: '1', rowSpan: '2', field: 'real_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '预计变更数量', colSpan: '1', rowSpan: '2', field: 'estimate_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             {title: '本期计量|合同', colSpan: '3|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: 'gather_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
@@ -284,6 +292,7 @@ const stageCl = {
         emptyRows: 20,
         headRows: 2,
         headRowHeight: [25, 25],
+        headColWidth: [30],
         defaultRowHeight: 21,
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
@@ -336,6 +345,8 @@ const stageNoCl = {
             {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 180, formatter: '@'},
             {title: '位置', colSpan: '1', rowSpan: '2', field: 'position', hAlign: 0, width: 60, formatter: '@'},
             {title: '设计量', colSpan: '1', rowSpan: '2', field: 'sgfh_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '现场实际数量', colSpan: '1', rowSpan: '2', field: 'real_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '预计变更数量', colSpan: '1', rowSpan: '2', field: 'estimate_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             {title: '本期计量|合同', colSpan: '3|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: 'gather_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
@@ -349,6 +360,7 @@ const stageNoCl = {
         emptyRows: 20,
         headRows: 2,
         headRowHeight: [25, 25],
+        headColWidth: [30],
         defaultRowHeight: 21,
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
@@ -385,6 +397,7 @@ const stageGather = {
         emptyRows: 0,
         headRows: 2,
         headRowHeight: [25, 25],
+        headColWidth: [30],
         defaultRowHeight: 21,
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
@@ -411,6 +424,7 @@ const stageGather = {
         emptyRows: 0,
         headRows: 2,
         headRowHeight: [25, 25],
+        headColWidth: [30],
         defaultRowHeight: 21,
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
@@ -449,6 +463,7 @@ const stageCompare = {
         emptyRows: 3,
         headRows: 1,
         headRowHeight: [32],
+        headColWidth: [30],
         defaultRowHeight: 21,
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
@@ -472,6 +487,7 @@ measure.gather.billsSpread = {
     emptyRows: 0,
     headRows: 1,
     headRowHeight: [32],
+    headColWidth: [30],
     defaultRowHeight: 21,
     headerFont: '12px 微软雅黑',
     font: '12px 微软雅黑',
@@ -498,6 +514,7 @@ measure.compare.pos = {
     emptyRows: 3,
     headRows: 1,
     headRowHeight: [32],
+    headColWidth: [30],
     defaultRowHeight: 21,
     headerFont: '12px 微软雅黑',
     font: '12px 微软雅黑',
@@ -524,7 +541,7 @@ module.exports = {
     stageNoCl,
     stageGather,
     stageCompare,
-    filterCols: { tzWithoutCols, dgnCols, clCols, stageDgnCols},
+    filterCols: { tzWithoutCols, dgnCols, clCols, stageDgnCols, realCompleteCols},
     measure,
     blank,
 };

+ 4 - 0
app/const/tender_info.js

@@ -92,6 +92,10 @@ const defaultInfo = {
             dgnQty: false,
             clQty: false,
         },
+        thousandth: false,
+        stage: {
+            realComplete: false,
+        }
     },
     chapter: [
         {code: '100', name: '总则'},

+ 1 - 0
app/controller/ledger_audit_controller.js

@@ -62,6 +62,7 @@ module.exports = app => {
             try {
                 const renderData = {
                     tender: ctx.tender.data,
+                    tenderInfo: ctx.tender.info,
                     tenderMenu: this.menu.tenderMenu,
                     preUrl: '/tender/' + ctx.tender.id,
                     measureType,

+ 142 - 20
app/controller/material_controller.js

@@ -16,7 +16,11 @@ const measureType = tenderConst.measureType;
 const accountGroup = require('../const/account_group').group;
 const materialConst = require('../const/material');
 const MaterialCalculator = require('../lib/material_calc');
+const sendToWormhole = require('stream-wormhole');
+const fs = require('fs');
+const path = require('path');
 const _ = require('lodash');
+const { typeOf } = require('mathjs');
 
 module.exports = app => {
     class MaterialController extends app.BaseController {
@@ -36,7 +40,7 @@ module.exports = app => {
         /**
          * 期列表(Get)
          * @param ctx
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async index(ctx) {
             try {
@@ -73,7 +77,7 @@ module.exports = app => {
         /**
          * 期审批流程(Get)
          * @param ctx
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async materialAuditors(ctx) {
             try {
@@ -107,7 +111,7 @@ module.exports = app => {
         /**
          * 新增期(Post)
          * @param ctx
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async add(ctx) {
             try {
@@ -132,7 +136,7 @@ module.exports = app => {
         /**
          * 删除期(Post)
          * @param ctx
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async delete(ctx) {
             try {
@@ -160,7 +164,7 @@ module.exports = app => {
         /**
          * 获取通用的renderData(用于layout, Menu, subMenu部分)
          * @param ctx
-         * @returns {{tender, tenderMenu, auditConst}}
+         * @return {{tender, tenderMenu, auditConst}}
          * @private
          */
         async _getDefaultRenderData(ctx) {
@@ -192,7 +196,7 @@ module.exports = app => {
         /**
          * 获取审批界面所需的 原报、审批人数据等
          * @param ctx
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          * @private
          */
         async _getMaterialAuditViewData(ctx) {
@@ -214,7 +218,7 @@ module.exports = app => {
         /**
          * 调差工料页面 (Get)
          * @param {Object} ctx - egg全局变量
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async info(ctx) {
             try {
@@ -275,7 +279,7 @@ module.exports = app => {
         /**
          * 调差清单页面 (Get)
          * @param {Object} ctx - egg全局变量
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async list(ctx) {
             try {
@@ -309,9 +313,30 @@ module.exports = app => {
         }
 
         /**
+         * 附件页面 (Get)
+         * @param {Object} ctx - egg全局变量
+         * @return {Promise<void>}
+         */
+        async file(ctx) {
+            try {
+                await this._getMaterialAuditViewData(ctx);
+                const renderData = await this._getDefaultRenderData(ctx);
+                // 获取当前标段所有附件
+                // const searchsql = { tid: ctx.tender.id };
+                renderData.fileList = await ctx.service.materialFile.getAllMaterialFiles(ctx.tender.id, ctx.material.id);
+                renderData.auditors = ctx.material.auditors.map(audit => audit.aid);
+                renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.material.file);
+                await this.layout('material/file.ejs', renderData, 'material/file_modal.ejs');
+            } catch (err) {
+                this.log(err);
+                ctx.redirect('/tender/' + ctx.tender.id + '/measure/material');
+            }
+        }
+
+        /**
          * 调差清单 - 工料操作 (Ajax)
          * @param ctx
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async saveListsData(ctx) {
             try {
@@ -365,7 +390,7 @@ module.exports = app => {
         /**
          * 调差工料 - 编辑工料项 (Ajax)
          * @param ctx
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async saveBillsData(ctx) {
             try {
@@ -446,7 +471,7 @@ module.exports = app => {
         /**
          * 添加审批人
          * @param ctx
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async addAudit(ctx) {
             try {
@@ -465,7 +490,7 @@ module.exports = app => {
 
                 ctx.material.auditorList = await ctx.service.materialAudit.getAuditors(ctx.material.id, ctx.material.times);
                 // 检查审核人是否已存在
-                const exist = this.app._.find(ctx.material.auditorList, {aid: id});
+                const exist = this.app._.find(ctx.material.auditorList, { aid: id });
                 if (exist) {
                     throw '该审核人已存在,请勿重复添加';
                 }
@@ -476,16 +501,16 @@ module.exports = app => {
                 }
 
                 const audit = await ctx.service.materialAudit.getAuditor(ctx.material.id, id, ctx.material.times);
-                ctx.body = {err: 0, msg: '', data: audit};
+                ctx.body = { err: 0, msg: '', data: audit };
             } catch (err) {
                 this.log(err);
-                ctx.body = {err: 1, msg: err.toString(), data: null};
+                ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
         /**
          * 移除审批人
          * @param ctx
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async deleteAudit(ctx) {
             try {
@@ -501,15 +526,15 @@ module.exports = app => {
                 }
 
                 const auditors = await ctx.service.materialAudit.getAuditors(ctx.material.id, ctx.material.times);
-                ctx.body = {err: 0, msg: '', data: auditors};
+                ctx.body = { err: 0, msg: '', data: auditors };
             } catch (err) {
-                ctx.body = {err: 1, msg: err.toString(), data: null};
+                ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
         /**
          * 上报和重新上报
          * @param ctx
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async startAudit(ctx) {
             try {
@@ -536,7 +561,7 @@ module.exports = app => {
         /**
          * 审批
          * @param ctx
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async checkAudit(ctx) {
             try {
@@ -563,12 +588,109 @@ module.exports = app => {
 
                 ctx.redirect(ctx.request.header.referer);
             } catch (err) {
-                console.log(err);
                 this.log(err);
                 ctx.session.postError = err.toString();
                 ctx.redirect(ctx.request.header.referer);
             }
         }
+
+        /**
+         * 上传附件
+         * @param {*} ctx 上下文
+         */
+        async upload(ctx) {
+            let stream;
+            try {
+                const parts = this.ctx.multipart({
+                    autoFields: true,
+                });
+                const files = [];
+                const create_time = Date.parse(new Date()) / 1000;
+                while ((stream = await parts()) != null) {
+                    if (!stream.filename) {
+                        // 如果没有传入直接返回
+                        return;
+                    }
+                    const fileInfo = path.parse(stream.filename);
+                    const filepath = path.join('public/upload', this.ctx.tender.id.toString(), 'tc', 'fujian_' + create_time + fileInfo.ext);
+                    await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, 'app', filepath));
+                    files.push({ filepath, name: stream.filename });
+                    // 将上传的文件流消费掉,防止浏览器卡死
+                    stream && (await sendToWormhole(stream));
+                }
+                const upload_time = this.ctx.helper.dateTran(new Date());
+                const payload = files.map(file => {
+                    let idx;
+                    if (Array.isArray(parts.field.name)) {
+                        idx = parts.field.name.findIndex(name => name === file.name);
+                    } else {
+                        idx = 'isString';
+                    }
+                    const newFile = {
+                        tid: ctx.tender.id,
+                        user_id: ctx.session.sessionUser.accountId,
+                        mid: ctx.material.id,
+                        upload_time,
+                        filepath: file.filepath,
+                        file_size: ctx.helper.bytesToSize(idx === 'isString' ? parts.field.size : parts.field.size[idx]),
+                        file_name: file.name,
+                    };
+                    return newFile;
+                });
+                // 执行文件信息写入数据库
+                await ctx.service.materialFile.saveFileMsgToDb(payload);
+                // 将最新的当前标段的所有文件信息返回
+                const data = await ctx.service.materialFile.getAllMaterialFiles(ctx.tender.id, ctx.material.id);
+                ctx.body = { err: 0, msg: '', data };
+            } catch (err) {
+                stream && (await sendToWormhole(stream));
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+        /**
+         * 查看当前标段以往所有期的附件
+         * @param {Object} ctx 上下文
+         */
+        async getCurMatericalFiles(ctx) {
+            try {
+                const { isCheck } = JSON.parse(ctx.request.body.data);
+                let data;
+                if (isCheck) {
+                    data = await ctx.service.materialFile.getAllMaterialFiles(ctx.tender.id);
+                } else {
+                    data = await ctx.service.materialFile.getAllMaterialFiles(ctx.tender.id, ctx.material.id);
+                }
+                ctx.body = { err: 0, msg: '', data };
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
+        /**
+         * 删除附件
+         * @param {Ojbect} ctx 上下文
+         */
+        async deleteFile(ctx) {
+            try {
+                const { data } = ctx.request.body;
+                const { id } = JSON.parse(data);
+                const fileInfo = await ctx.service.materialFile.getMaterialFileById(id);
+                if (fileInfo) {
+                    // 先删除文件
+                    await fs.unlinkSync(path.join(this.app.baseDir, './app', fileInfo.filepath));
+                    // 再删除数据库
+                    await ctx.service.materialFile.delete(id);
+                } else {
+                    throw '不存在该文件';
+                }
+                ctx.body = { err: 0, msg: '请求成功' };
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
     }
 
     return MaterialController;

+ 14 - 5
app/controller/measure_controller.js

@@ -243,14 +243,23 @@ module.exports = app => {
                 const stage_highOrder = await ctx.service.stage.count({
                     tid: ctx.tender.id,
                 });
-                if (stageInfo === undefined || ctx.session.sessionUser.accountId !== stageInfo.user_id || stage_highOrder !== stageInfo.order) {
+
+                if (ctx.request.body.confirm !== undefined && ctx.request.body.confirm !== '确认删除本期') {
+                    throw '请输入正确的文本信息';
+                }
+                if (stageInfo && (ctx.session.sessionUser.accountId !== stageInfo.user_id || (ctx.session.sessionUser.is_admin && ctx.request.body.confirm === '确认删除本期')) && stage_highOrder === stageInfo.order) {
+                    const result = await ctx.service.stage.deleteStage(stage_id);
+                    if (!result) {
+                        throw '删除计量期失败,请重试';
+                    }
+                } else {
                     throw '您无权删除计量期';
                 }
-                const result = await ctx.service.stage.deleteStage(stage_id);
-                if (!result) {
-                    throw '删除计量期失败,请重试';
+                if (ctx.request.body.stage_order !== undefined && parseInt(ctx.request.body.stage_order) !== stage_highOrder) {
+                    ctx.redirect(ctx.request.header.referer);
+                } else {
+                    ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage/');
                 }
-                ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage/');
             } catch (err) {
                 this.log(err);
                 console.log(err);

+ 42 - 0
app/controller/setting_controller.js

@@ -590,6 +590,48 @@ module.exports = app => {
                 ctx.body = { err: 1, msg: error.toString(), data: null };
             }
         }
+
+        /**
+         * 显示设置(Get)
+         * @param {Object} ctx -egg全局变量
+         * @return {void}
+         */
+        async show(ctx) {
+            try {
+                // 获取项目数据
+                const projectId = ctx.session.sessionProject.id;
+                const projectData = await ctx.service.project.getDataById(projectId);
+                if (!projectData) {
+                    throw '没有对应的项目数据';
+                }
+                const showList = await ctx.service.settingShow.getList(projectData.page_path);
+                const renderData = { projectData, showList };
+                await this.layout('setting/show.ejs', renderData);
+            } catch (error) {
+                this.log(error);
+                ctx.redirect('/dashboard');
+            }
+        }
+
+        /**
+         * 更新显示设置列表
+         * @param {Object} ctx -egg全局变量
+         * @return {void}
+         */
+        async showListUpdate(ctx) {
+            try {
+                // 获取项目id
+                const projectId = ctx.session.sessionProject.id;
+                const { data } = ctx.request.body;
+                const { id } = JSON.parse(data);
+                const result = await ctx.service.settingShow.setDefaultLabel(id, projectId);
+                const responseData = { err: 0, msg: '', data: result };
+                ctx.body = responseData;
+            } catch (error) {
+                this.log(error);
+                ctx.body = { err: 1, msg: error.toString(), data: null };
+            }
+        }
     }
 
     return SettingController;

文件差异内容过多而无法显示
+ 249 - 174
app/controller/stage_controller.js


+ 31 - 13
app/controller/tender_controller.js

@@ -35,14 +35,15 @@ module.exports = app => {
             try {
                 // 获取用户新建标段权利
                 const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
-                const userPermission = accountInfo !== undefined && accountInfo.permission !== '' ? JSON.parse(accountInfo.permission) : null;
+                const userPermission = accountInfo !== undefined && accountInfo.permission !== ''
+                    ? JSON.parse(accountInfo.permission) : null;
 
                 const tenderList = await this.ctx.service.tender.getList('', userPermission);
 
                 for (const t of tenderList) {
                     if (t.user_id === this.ctx.session.sessionUser.accountId && (
                         t.ledger_status === auditConst.ledger.status.checkNo || t.ledger_status === auditConst.ledger.status.uncheck)) {
-                        const sum = await this.ctx.service.ledger.addUp({tender_id: t.id/*, is_leaf: true*/});
+                        const sum = await this.ctx.service.ledger.addUp({ tender_id: t.id/* , is_leaf: true*/ });
                         t.total_price = sum.total_price;
                         t.deal_tp = sum.deal_tp;
                     }
@@ -50,12 +51,28 @@ module.exports = app => {
                         t.lastStage = await this.ctx.service.stage.getLastestStage(t.id, true);
 
                         if (!t.lastStage) continue;
-                        if (t.lastStage.status === auditConst.stage.status.uncheck && t.lastStage.user_id !== this.ctx.session.sessionUser.accountId)
+                        if (t.lastStage.status === auditConst.stage.status.uncheck &&
+                            t.lastStage.user_id !== this.ctx.session.sessionUser.accountId) {
                             t.lastStage = await this.ctx.service.stage.getLastestStage(t.id);
+                        }
 
                         if (!t.lastStage) continue;
                         await this.ctx.service.stage.checkStageGatherData(t.lastStage);
                     }
+                    if (t.lastStage) {
+                        if (t.lastStage.status === auditConst.stage.status.uncheck) {
+                            const status_name = await this.ctx.service.projectAccount.getAccountInfoById(t.lastStage.user_id);
+                            t.status_users = status_name ? status_name.name : '';
+                        } else {
+                            const status_name = await this.ctx.service.stageAudit.getAuditorByStatus(t.lastStage.id, t.lastStage.status, t.lastStage.times);
+                            t.status_users = status_name ? status_name.name : '';
+                        }
+                    } else {
+                        if (t.ledger_status !== auditConst.ledger.status.uncheck) {
+                            const status_name = await this.ctx.service.ledgerAudit.getStatusName(t.id, t.ledger_times);
+                            t.status_users = status_name ? status_name.name : '';
+                        }
+                    }
                 }
                 const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
                 const valuations = await this.ctx.service.valuation.getProjectValidValuation(this.ctx.session.sessionProject.id);
@@ -118,7 +135,7 @@ module.exports = app => {
             } catch (err) {
                 console.log('error', err);
                 this.log(err);
-                this.ctx.redirect('/dashboard')
+                this.ctx.redirect('/dashboard');
             }
         }
 
@@ -269,7 +286,8 @@ module.exports = app => {
          */
         async deleteTender(ctx) {
             try {
-                const data = JSON.parse(ctx.request.body.data), result = [];
+                const data = JSON.parse(ctx.request.body.data),
+                    result = [];
                 if (!(data instanceof Array) && (data.length === 0)) {
                     throw '提交数据有误';
                 }
@@ -278,9 +296,9 @@ module.exports = app => {
                         result.push(id);
                     }
                 }
-                ctx.body = {err: 0, msg: '', data: result};
+                ctx.body = { err: 0, msg: '', data: result };
             } catch (err) {
-                ctx.body = {err: 1, msg: err.toString(), data: []}
+                ctx.body = { err: 1, msg: err.toString(), data: [] };
             }
         }
 
@@ -295,12 +313,12 @@ module.exports = app => {
                 const tender = ctx.tender.data;
                 if (tender.user_id === this.ctx.session.sessionUser.accountId && (
                     tender.ledger_status === auditConst.ledger.status.checkNo || tender.ledger_status === auditConst.ledger.status.uncheck)) {
-                    const sum = await this.ctx.service.ledger.addUp({tender_id: tender.id/*, is_leaf: true*/});
+                    const sum = await this.ctx.service.ledger.addUp({ tender_id: tender.id/* , is_leaf: true*/ });
                     tender.total_price = sum.total_price;
                     tender.deal_tp = sum.deal_tp;
                 }
                 const stages = await ctx.service.stage.getValidStages(ctx.tender.id);
-                const lastStage = stages.length > 0 ? stages[0] : null; //await ctx.service.stage.getLastestStage(ctx.tender.id);
+                const lastStage = stages.length > 0 ? stages[0] : null; // await ctx.service.stage.getLastestStage(ctx.tender.id);
                 if (lastStage) {
                     await this.ctx.service.stage.checkStageGatherData(lastStage);
                     tender.gather_tp = ctx.helper.add(lastStage.contract_tp, lastStage.qc_tp);
@@ -319,17 +337,17 @@ module.exports = app => {
                 const monthProgress = [];
                 for (const s of stages) {
                     if (s.s_time) {
-                        let progress = monthProgress.find(function (x) {
+                        let progress = monthProgress.find(function(x) {
                             return x.month === s.s_time;
                         });
                         if (!progress) {
-                            progress = {month: s.s_time};
+                            progress = { month: s.s_time };
                             monthProgress.push(progress);
                         }
                         progress.tp = ctx.helper.add(ctx.helper.add(progress.tp, s.contract_tp), s.qc_tp);
                     }
                 }
-                monthProgress.sort(function (x, y) {
+                monthProgress.sort(function(x, y) {
                     return Date.parse(x.month) - Date.parse(y.month);
                 });
                 let sum = 0;
@@ -340,7 +358,7 @@ module.exports = app => {
                     p.end_ratio = ctx.helper.mul(ctx.helper.div(p.end_tp, tender.sum, 4), 100);
                 }
                 const renderData = {
-                    tender: tender,
+                    tender,
                     tenderInfo: ctx.tender.info,
                     tenderMenu: this.menu.tenderMenu,
                     preUrl: '/tender/' + ctx.tender.id,

+ 65 - 0
app/controller/wechat_controller.js

@@ -0,0 +1,65 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Ellisran
+ * @date 2020/7/2
+ * @version
+ */
+
+const moment = require('moment');
+const Controller = require('egg').Controller;
+const crypto = require('crypto');
+
+module.exports = app => {
+    class WechatController extends Controller {
+        /**
+         * 接入微信
+         *
+         * @param {Object} ctx - egg全局页面
+         * @return {void}
+         */
+        async index(ctx) {
+            try {
+                const signature = ctx.query.signature;
+                const timestamp = ctx.query.timestamp;
+                const nonce = ctx.query.nonce;
+                const echostr = ctx.query.echostr;
+                const array = [ctx.app.config.wechatAll.token, timestamp, nonce];
+                array.sort();
+                const tempStr = array.join('');
+                const hashCode = crypto.createHash('sha1');
+                const resultCode = hashCode.update(tempStr, 'utf8').digest('hex');
+                if (resultCode === signature) {
+                    ctx.body = echostr;
+                    // res.send(echostr);
+                } else {
+                    throw '验证失败';
+                }
+            } catch (e) {
+                console.log(e);
+            }
+        }
+        /**
+         * 登录验证
+         *
+         * @param {Object} ctx - egg全局页面
+         * @return {void}
+         */
+        async oauth(ctx) {
+            console.log('hello');
+            const url = await app.wechat.oauth.getAuthorizeURL('http://jluat.smartcost.com.cn/wx/hello', 'hello world', 'snsapi_userinfo');
+            console.log(url);
+            // const token = await app.wechat.oauth.getAccessToken(ctx.query.code);
+            // const accessToken = token.data.access_token;
+            // const openid = token.data.openid;
+            // console.log(token);
+        }
+        async hello(ctx) {
+            console.log('hello world');
+        }
+    }
+
+    return WechatController;
+};

+ 121 - 101
app/extend/helper.js

@@ -19,7 +19,7 @@ Decimal.set({ precision: 50, defaults: true });
 const SMS = require('../lib/sms');
 
 module.exports = {
-    _: _,
+    _,
 
     /**
      * 生成随机字符串
@@ -143,7 +143,7 @@ module.exports = {
             if (decimals < 1) {
                 val = (Math.round(val)).toString();
             } else {
-                let num = val.toString();
+                const num = val.toString();
                 if (num.lastIndexOf('.') === -1) {
                     // num += '.';
                     // num += this.makezero(decimals);
@@ -155,7 +155,7 @@ module.exports = {
                         val = num;
                     } else if (parseInt(valdecimals) > parseInt(decimals)) {
                         val = parseFloat(val) !== 0 ? Math.round(this.accMul(val, this.makemultiple(decimals))) / this.makemultiple(decimals) : this.makedecimalzero(decimals);
-                        let num = val.toString();
+                        const num = val.toString();
                         if (num.lastIndexOf('.') === -1) {
                             // num += '.';
                             // num += this.makezero(decimals);
@@ -320,7 +320,7 @@ module.exports = {
      * obj = {a: 1, b: 2}, sObj = {a: 0, c: 3}, 返回{a: 0, b: 2, c: 3}
      * @param obj
      * @param sObj
-     * @returns {any}
+     * @return {any}
      */
     updateObj(obj, sObj) {
         if (!obj) {
@@ -340,7 +340,7 @@ module.exports = {
      * @param {Array} arr
      * @param name -
      * @param value
-     * @returns {*}
+     * @return {*}
      */
     findData(arr, name, value) {
         if (!arr instanceof Array) {
@@ -368,14 +368,14 @@ module.exports = {
      * 检查数字是否相等
      * @param {Number} value1
      * @param {Number} value2
-     * @returns {boolean}
+     * @return {boolean}
      */
     checkNumberEqual(value1, value2) {
         if (value1 && value2) {
             return Math.abs(value2 - value1) > zeroRange;
-        } else {
-            return (!value1 && !value2)
         }
+        return (!value1 && !value2);
+
     },
 
     /**
@@ -383,7 +383,7 @@ module.exports = {
      * @param str1
      * @param str2
      * @param symbol
-     * @returns {number}
+     * @return {number}
      */
     compareCode(str1, str2, symbol = '-') {
         if (!str1) {
@@ -396,19 +396,20 @@ module.exports = {
             if (numReg.test(code1)) {
                 if (numReg.test(code2)) {
                     return parseInt(code1) - parseInt(code2);
-                } else {
-                    return -1
-                }
-            } else {
-                if (numReg.test(code2)) {
-                    return 1;
-                } else {
-                    return code1 === code2 ? 0 : (code1 < code2 ? -1 : 1); //code1.localeCompare(code2);
                 }
+                return -1;
+
+            }
+            if (numReg.test(code2)) {
+                return 1;
             }
+            return code1 === code2 ? 0 : (code1 < code2 ? -1 : 1); // code1.localeCompare(code2);
+
+
         }
         const numReg = /^[0-9]+$/;
-        const aCodes = str1.split(symbol), bCodes = str2.split(symbol);
+        const aCodes = str1.split(symbol),
+            bCodes = str2.split(symbol);
         for (let i = 0, iLength = Math.min(aCodes.length, bCodes.length); i < iLength; ++i) {
             const iCompare = compareSubCode(aCodes[i], bCodes[i]);
             if (iCompare !== 0) {
@@ -422,18 +423,20 @@ module.exports = {
      * 根据 清单编号 获取 章级编号
      * @param code
      * @param symbol
-     * @returns {string}
+     * @return {string}
      */
     getChapterCode(code, symbol = '-') {
         if (!code || code === '') return '';
         const codePath = code.split(symbol);
-        const reg = /^[0-9]*$/;
+        const reg = /^[^0-9]*[0-9]{3,4}$/;
         if (reg.test(codePath[0])) {
-            const num = parseInt(codePath[0]);
+            const numReg = /[0-9]{3,4}$/;
+            const result = codePath[0].match(numReg);
+            const num = parseInt(result[0]);
             return this.mul(this.div(num, 100, 0), 100) + '';
-        } else {
-            return '10000';
         }
+        return '10000';
+
     },
 
     /**
@@ -442,9 +445,9 @@ module.exports = {
      * @param idField
      * @param pidField
      */
-    sortTreeNodes (treeNodes, idField, pidField) {
+    sortTreeNodes(treeNodes, idField, pidField) {
         const result = [];
-        const getFirstLevel = function (nodes) {
+        const getFirstLevel = function(nodes) {
             let result;
             for (const node of nodes) {
                 if (!result || result > node.level) {
@@ -453,26 +456,26 @@ module.exports = {
             }
             return result;
         };
-        const getLevelNodes = function (nodes, level) {
-            const children = nodes.filter(function (a) {
+        const getLevelNodes = function(nodes, level) {
+            const children = nodes.filter(function(a) {
                 return a.level = level;
             });
-            children.sort(function (a, b) {
+            children.sort(function(a, b) {
                 return a.order - b.order;
             });
             return children;
         };
-        const getChildren = function (nodes, node) {
-            const children = nodes.filter(function (a) {
+        const getChildren = function(nodes, node) {
+            const children = nodes.filter(function(a) {
                 return a[pidField] = node[idField];
             });
-            children.sort(function (a, b) {
+            children.sort(function(a, b) {
                 return a.order - b.order;
             });
             return children;
         };
-        const addSortNodes = function (nodes) {
-            for (let i = 0; i< nodes.length; i++) {
+        const addSortNodes = function(nodes) {
+            for (let i = 0; i < nodes.length; i++) {
                 result.push(nodes[i]);
                 addSortNodes(getChildren(nodes[i]));
             }
@@ -522,7 +525,7 @@ module.exports = {
     /**
      * 递归创建文件夹(fs.mkdirSync需要上一层文件夹已存在)
      * @param pathName
-     * @returns {Promise<void>}
+     * @return {Promise<void>}
      */
     async recursiveMkdirSync(pathName) {
         if (!fs.existsSync(pathName)) {
@@ -538,7 +541,7 @@ module.exports = {
      * 字节 保存至 本地文件
      * @param buffer - 字节
      * @param fileName - 文件名
-     * @returns {Promise<void>}
+     * @return {Promise<void>}
      */
     async saveBufferFile(buffer, fileName) {
         // 检查文件夹是否存在,不存在则直接创建文件夹
@@ -553,7 +556,7 @@ module.exports = {
      * 将文件流的数据保存至本地文件
      * @param stream
      * @param fileName
-     * @returns {Promise<void>}
+     * @return {Promise<void>}
      */
     async saveStreamFile(stream, fileName) {
         // 读取字节流
@@ -567,7 +570,7 @@ module.exports = {
     /**
      * 检查code是否是指标模板数据
      * @param {String} code
-     * @returns {boolean}
+     * @return {boolean}
      */
     validBillsCode(code) {
         const reg1 = /(^[0-9]+)([a-z0-9\-]*)/i;
@@ -577,11 +580,11 @@ module.exports = {
 
     getNumberFormatter(decimal) {
         if (decimal <= 0) {
-            return "0";
+            return '0';
         }
-        let pre = "0.";
+        let pre = '0.';
         for (let i = 0; i < decimal; i++) {
-            pre += "#"
+            pre += '#';
         }
         return pre;
     },
@@ -590,7 +593,7 @@ module.exports = {
      * 根据单位查找对应的清单精度
      * @param {tenderInfo.precision} list - 清单精度列表
      * @param {String} unit - 单位
-     * @returns {number}
+     * @return {number}
      */
     findPrecision(list, unit) {
         if (unit) {
@@ -635,9 +638,9 @@ module.exports = {
                 }
             }
             return result;
-        } else {
-            return data;
         }
+        return data;
+
     },
 
     // 加减乘除方法,为方便调用,兼容num为空的情况
@@ -646,16 +649,16 @@ module.exports = {
      * 加法 num1 + num2
      * @param num1
      * @param num2
-     * @returns {number}
+     * @return {number}
      */
     add(num1, num2) {
-        return bc.add(num1 ? num1 : 0, num2 ? num2: 0);
+        return bc.add(num1 ? num1 : 0, num2 ? num2 : 0);
     },
     /**
      * 减法 num1 - num2
      * @param num1
      * @param num2
-     * @returns {number}
+     * @return {number}
      */
     sub(num1, num2) {
         return bc.sub(num1 ? num1 : 0, num2 ? num2 : 0);
@@ -664,7 +667,7 @@ module.exports = {
      * 乘法 num1 * num2
      * @param num1
      * @param num2
-     * @returns {*}
+     * @return {*}
      */
     mul(num1, num2, digit = 6) {
         if (num1 === '' || num1 === null || num2 === '' || num2 === null) {
@@ -676,29 +679,29 @@ module.exports = {
      * 除法 num1 / num2
      * @param num1 - 被除数
      * @param num2 - 除数
-     * @returns {*}
+     * @return {*}
      */
     div(num1, num2, digit = 6) {
         if (num2 && !this.checkZero(num2)) {
-            return Decimal.div(num1 ? num1: 0, num2).toDecimalPlaces(digit).toNumber();
-        } else {
-            return null;
+            return Decimal.div(num1 ? num1 : 0, num2).toDecimalPlaces(digit).toNumber();
         }
+        return null;
+
     },
     /**
      * 四舍五入(统一,方便以后万一需要置换)
      * @param {Number} value - 舍入的数字
      * @param {Number} decimal - 要保留的小数位数
-     * @returns {*}
+     * @return {*}
      */
     round(value, decimal) {
-        //return value ? bc.round(value, decimal) : null;
+        // return value ? bc.round(value, decimal) : null;
         return value ? new Decimal(value).toDecimalPlaces(decimal).toNumber() : null;
     },
     /**
      * 汇总
      * @param array
-     * @returns {number}
+     * @return {number}
      */
     sum(array) {
         let result = 0;
@@ -713,7 +716,7 @@ module.exports = {
      * @param str
      * @param reg
      * @param subStr
-     * @returns {*}
+     * @return {*}
      */
     replaceStr(str, reg, subStr) {
         return str ? str.replace(reg, subStr) : str;
@@ -721,7 +724,7 @@ module.exports = {
     /**
      * 替换字符串中的 换行符回车符
      * @param str
-     * @returns {*}
+     * @return {*}
      */
     replaceReturn(str) {
         // return str
@@ -735,7 +738,7 @@ module.exports = {
     /**
      * 替换字符串中的 换行符回车符为换行符<br>
      * @param str
-     * @returns {*}
+     * @return {*}
      */
     replaceRntoBr(str) {
         // return str
@@ -750,13 +753,13 @@ module.exports = {
      * 获取 字符串 数组的 mysql 筛选条件
      *
      * @param arr
-     * @returns {*}
+     * @return {*}
      */
     getInArrStrSqlFilter(arr) {
         let result = '';
         for (const a of arr) {
             if (result !== '') {
-                result = result + ','
+                result = result + ',';
             }
             result = result + this.ctx.app.mysql.escape(a);
         }
@@ -769,8 +772,9 @@ module.exports = {
      * @param {Array[]}rela - 相关数据 {data, fields, prefix, relaId}
      */
     assignRelaData(main, rela) {
-        const index = {}, indexPre = 'id_';
-        const loadFields = function (datas, fields, prefix, relaId) {
+        const index = {},
+            indexPre = 'id_';
+        const loadFields = function(datas, fields, prefix, relaId) {
             for (const d of datas) {
                 const key = indexPre + d[relaId];
                 const m = index[key];
@@ -791,7 +795,7 @@ module.exports = {
         }
     },
 
-    whereSql (where, as) {
+    whereSql(where, as) {
         if (!where) {
             return '';
         }
@@ -815,31 +819,31 @@ module.exports = {
     },
     formatMoney(s, dot = ',') {
         if (!s) return '0.00';
-        s = parseFloat((s + "").replace(/[^\d\.-]/g, "")).toFixed(2) + "";
-        var l = s.split(".")[0].split("").reverse(),
-            r = s.split(".")[1];
-        let t = "";
-        for(let i = 0; i < l.length; i ++ )   {
-            t += l[i] + ((i + 1) % 3 == 0 && (i + 1) != l.length ? dot : "");
+        s = parseFloat((s + '').replace(/[^\d\.-]/g, '')).toFixed(2) + '';
+        let l = s.split('.')[0].split('').reverse(),
+            r = s.split('.')[1];
+        let t = '';
+        for (let i = 0; i < l.length; i++) {
+            t += l[i] + ((i + 1) % 3 == 0 && (i + 1) != l.length ? dot : '');
         }
-        return t.split("").reverse().join("") + "." + r;
+        return t.split('').reverse().join('') + '.' + r;
     },
     transFormToChinese(num) {
         const changeNum = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
-        const unit = ["", "十", "百", "千", "万"];
+        const unit = ['', '十', '百', '千', '万'];
         num = parseInt(num);
-        let getWan = (temp) => {
-            let strArr = temp.toString().split("").reverse();
-            let newNum = "";
-            for (var i = 0; i < strArr.length; i++) {
-                newNum = (i == 0 && strArr[i] == 0 ? "" : (i > 0 && strArr[i] == 0 && strArr[i - 1] == 0 ? "" : changeNum[strArr[i]] + (strArr[i] == 0 ? unit[0] : unit[i]))) + newNum;
+        const getWan = temp => {
+            const strArr = temp.toString().split('').reverse();
+            let newNum = '';
+            for (let i = 0; i < strArr.length; i++) {
+                newNum = (i == 0 && strArr[i] == 0 ? '' : (i > 0 && strArr[i] == 0 && strArr[i - 1] == 0 ? '' : changeNum[strArr[i]] + (strArr[i] == 0 ? unit[0] : unit[i]))) + newNum;
             }
-            return strArr.length === 2 && newNum.indexOf("一十") !== -1 ? newNum.replace('一十', '十') : newNum;
-        }
-        let overWan = Math.floor(num / 10000);
+            return strArr.length === 2 && newNum.indexOf('一十') !== -1 ? newNum.replace('一十', '十') : newNum;
+        };
+        const overWan = Math.floor(num / 10000);
         let noWan = num % 10000;
-        if (noWan.toString().length < 4) noWan = "0" + noWan;
-        return overWan ? getWan(overWan) + "万" + getWan(noWan) : getWan(num);
+        if (noWan.toString().length < 4) noWan = '0' + noWan;
+        return overWan ? getWan(overWan) + '万' + getWan(noWan) : getWan(num);
     },
 
     dateTran(time) {
@@ -924,20 +928,20 @@ module.exports = {
             alignment: { horizontal: 'center' },
         };
         const sHeader = setting.header
-            .map((v, i) => Object.assign({}, { v: v, s: headerStyle, position: String.fromCharCode(65+i) + 1 }))
-            .reduce((prev, next) => Object.assign({}, prev, {[next.position]: {v: next.v, s: next.s}}), {});
+            .map((v, i) => Object.assign({}, { v, s: headerStyle, position: String.fromCharCode(65 + i) + 1 }))
+            .reduce((prev, next) => Object.assign({}, prev, { [next.position]: { v: next.v, s: next.s } }), {});
         const sData = data
             .map((v, i) => v.map((k, j) => Object.assign({}, {
                 v: k ? k : '',
-                s: { font: { sz: 10 }, alignment: {horizontal: setting.hAlign[j]}},
-                position: String.fromCharCode(65+j) + (i+2) })))
+                s: { font: { sz: 10 }, alignment: { horizontal: setting.hAlign[j] } },
+                position: String.fromCharCode(65 + j) + (i + 2) })))
             .reduce((prev, next) => prev.concat(next))
-            .reduce((prev, next) => Object.assign({}, prev, {[next.position]: {v: next.v, s: next.s}}), {});
+            .reduce((prev, next) => Object.assign({}, prev, { [next.position]: { v: next.v, s: next.s } }), {});
         const output = Object.assign({}, sHeader, sData);
         const outputPos = Object.keys(output);
         const result = Object.assign({}, output,
-            {'!ref': outputPos[0] + ':' + outputPos[outputPos.length - 1]},
-            {'!cols': setting.width.map((w) => Object.assign({}, {wpx: w}))});
+            { '!ref': outputPos[0] + ':' + outputPos[outputPos.length - 1] },
+            { '!cols': setting.width.map(w => Object.assign({}, { wpx: w })) });
         return result;
     },
 
@@ -946,7 +950,7 @@ module.exports = {
             this.ctx.logger.error(error);
         } else {
             this.ctx.getLogger('fail').info(JSON.stringify({
-                error: error,
+                error,
                 project: this.ctx.session.sessionProject,
                 user: this.ctx.session.sessionUser,
                 body: this.ctx.session.body,
@@ -975,13 +979,13 @@ module.exports = {
     /**
      * 深拷贝
      * @param obj
-     * @returns {*}
+     * @return {*}
      */
-    clone: function (obj) {
+    clone(obj) {
         if (obj === null) return null;
-        var o = obj instanceof Array ? [] : {};
-        for (var i in obj) {
-            o[i] = (obj[i] instanceof Date) ? new Date(obj[i].getTime()) : (typeof obj[i] === "object" ? this.clone(obj[i]) : obj[i]);
+        const o = obj instanceof Array ? [] : {};
+        for (const i in obj) {
+            o[i] = (obj[i] instanceof Date) ? new Date(obj[i].getTime()) : (typeof obj[i] === 'object' ? this.clone(obj[i]) : obj[i]);
         }
         return o;
     },
@@ -989,7 +993,7 @@ module.exports = {
     /**
      * 短链接生成
      * @param url
-     * @returns {*}
+     * @return {*}
      */
     async urlToShort(url) {
         const apiUrl = 'http://scn.ink/api/shorturl';
@@ -1003,7 +1007,7 @@ module.exports = {
     /**
      * 判断是否wap访问
      * @param request
-     * @returns {*}
+     * @return {*}
      */
     isWap(request) {
         return request.url.indexOf('/wap/') !== -1;
@@ -1015,11 +1019,12 @@ module.exports = {
             source: {
                 bills: [],
                 pos: [],
-            }
+            },
         };
         for (const b of bills) {
-            const pr = _.remove(pos, {lid: b.id});
-            const checkData = {}, calcData = {};
+            const pr = _.remove(pos, { lid: b.id });
+            const checkData = {},
+                calcData = {};
             if (pr && pr.length > 0) {
                 for (const field of fields) {
                     checkData[field] = b[field] ? b[field] : 0;
@@ -1034,7 +1039,7 @@ module.exports = {
                         ledger_id: b.ledger_id,
                         b_code: b.b_code,
                         name: b.name,
-                        error: {checkData: checkData, calcData: calcData}
+                        error: { checkData, calcData },
                     });
                     result.source.bills.push(b);
                     for (const p of pr) {
@@ -1056,9 +1061,24 @@ module.exports = {
     /**
      * 判断是否是移动端访问
      * @param request
-     * @returns {*}
+     * @return {*}
      */
     isMobile(agent) {
         return agent.match(/(iphone|ipod|android)/i);
     },
-}
+
+    /**
+     * 删除文件
+     * @param {Array} fileList 文件数组(格式为数据库查询出来的结果集,且文件字段必须为filepath)
+     * @return {void}
+     */
+    async delFiles(fileList) {
+        if (fileList.length) {
+            for (const att of fileList) {
+                if (att.filepath && fs.existsSync(path.join(this.app.baseDir, att.filepath))) {
+                    await fs.unlinkSync(path.join(this.app.baseDir, att.filepath));
+                }
+            }
+        }
+    },
+};

+ 6 - 1
app/lib/bills_pos_convert.js

@@ -39,7 +39,7 @@ class BillsPosConvert {
             id: 'id', ledgerId: 'lid',
             updateFields: ['contract_qty', 'qc_qty'],
         });
-        this.bpcChange = []
+        this.bpcChange = [];
         // reusltData
         this.resultTreeSetting = {
             id: 'ledger_id',
@@ -95,6 +95,11 @@ class BillsPosConvert {
         node.pre_qc_qty = this.ctx.helper.add(node.pre_qc_qty, data.pre_qc_qty);
         node.pre_qc_tp = this.ctx.helper.add(node.pre_qc_tp, this.ctx.helper.mul(node.unit_price, data.pre_qc_qty, tpDecimal));
         node.pre_gather_qty = this.ctx.helper.add(node.pre_gather_qty, data.pre_gather_qty);
+
+        node.real_qty = this.ctx.helper.add(node.real_qty, data.real_qty);
+        node.estimate_qty = this.ctx.helper.checkZero(node.real_qty)
+            ? this.ctx.helper.sub(this.ctx.helper.sub(node.real_qty, node.quantity), node.end_qc_qty)
+            : null;
     }
     _loadBillsCalcFields(node, data) {
         node.quantity = this.ctx.helper.add(node.quantity, data.quantity);

+ 2 - 0
app/lib/ledger.js

@@ -182,6 +182,7 @@ class baseTree {
         const datas = [];
         for (const node of this.nodes) {
             if (node.b_code && node.b_code !== '') node.chapter = this.ctx.helper.getChapterCode(node.b_code);
+            node.is_leaf = !node.children || node.children.length === 0;
             const data = {};
             for (const field of fields) {
                 data[field] = node[field];
@@ -202,6 +203,7 @@ class baseTree {
                 continue;
             }
             if (node.b_code && node.b_code !== '') node.chapter = this.ctx.helper.getChapterCode(node.b_code);
+            node.is_leaf = !node.children || node.children.length === 0;
             const data = {};
             for (const field in node) {
                 if (fields.indexOf(field) === -1) {

+ 28 - 3
app/lib/rpt_data_analysis.js

@@ -308,7 +308,7 @@ const gatherChapter = {
         for (const c of chapter) {
             const cc = { code: c.code, name: c.name, cType: 1 };
             cc.serialNo = serialNo++;
-            cc.filter = '^' + c.code.substr(0, c.code.length - 2) + '[0-9]{2}-';
+            cc.filter = '^[\\D]*' + c.code.substr(0, c.code.length - 2) + '[0-9]{2}-';
             //cc.visible = true;
             gclChapter.push(cc);
 
@@ -370,6 +370,15 @@ const gatherChapter = {
         }
         return [gclChapter, otherChapter, customChapter];
     },
+    _completeGatherInfo: function (chapters, source) {
+        for (const f in source) {
+            if (/^t_[0-9]*_(id|name)$/.test(f)) {
+                for (const chapter of chapters) {
+                    chapter[f] = source[f];
+                }
+            }
+        }
+    },
     _getGclChapter: function (chapter, data, field) {
         for (const c of chapter) {
             if (c.filter) {
@@ -472,14 +481,15 @@ const gatherChapter = {
             if (!chapter) return;
 
             for (const f in data) {
-                if (data[f] && (f.indexOf('tp') >= 0 || f === 'total_price')) {
+                if (!data[f]) continue;
+
+                if ((f.indexOf('tp') >= 0 || f === 'total_price')) {
                     chapter[f] = ctx.helper.add(chapter[f], data[f]);
                     if (calcFields.indexOf(f) === -1) calcFields.push(f);
                 }
             }
         };
 
-        const [gclChapter, otherChapter, customChapter] = this._getCalcChapter(data.tender_info.chapter, options ? options : this.defaultSetting);
         const fields = ctx.helper._.map(fieldsKey, 'field');
         const needFields = ['b_code', 'is_leaf'];
         for (const nf of needFields) {
@@ -487,6 +497,12 @@ const gatherChapter = {
         }
         const sourceData = data[fieldsKey[0].table];
         if (!sourceData) return;
+        const [gclChapter, otherChapter, customChapter] = this._getCalcChapter(data.tender_info.chapter, options ? options : this.defaultSetting);
+        if (fieldsKey[0].table.indexOf('mem_gather') >= 0) {
+            this._completeGatherInfo(gclChapter, sourceData[0]);
+            this._completeGatherInfo(otherChapter, sourceData[0]);
+            this._completeGatherInfo(customChapter, sourceData[0]);
+        }
         const filter = [];
         for (const d of sourceData) {
             for (const c of customChapter) {
@@ -796,6 +812,13 @@ const gatherStagePay = {
             }
         }
     },
+    _completeGatherInfo(d, source) {
+        for (const f in source) {
+            if (/^t_[0-9]*_(id|name)$/.test(f)) {
+                d[f] = source[f];
+            }
+        }
+    },
     fun: function (ctx, data, fields, options) {
         if (!options || !options.table || !options.custom) return;
 
@@ -815,6 +838,7 @@ const gatherStagePay = {
                 if (!cData.order_calc && cData.empty !== 1) {
                     this._gatherFields(ctx, cData, gatherData, calcFields);
                 }
+                if (options.table.indexOf('mem_gather') >= 0) this._completeGatherInfo(cData, gatherData[0]);
                 result.push(cData);
             }
         }
@@ -1111,6 +1135,7 @@ const gatherSelectConverse = {
                 case 'mem_gather_stage_bills':
                 case 'mem_gather_stage_pay':
                 case 'mem_gather_deal_bills':
+                case 'mem_union_data':
                     data[t] = this._commonConverse(ctx.helper, data[t], count);
                     break;
             }

+ 39 - 8
app/lib/stage_im.js

@@ -61,7 +61,7 @@ class StageIm {
         this.ImData = [];
         this.ImBillsData = [];
         //
-        this.imFields = ['uuid', 'doc_code', 'peg', 'bw', 'xm', 'drawing_code', 'calc_memo', 'calc_img', 'position', 'jldy'];
+        this.imFields = ['uuid', 'doc_code', 'peg', 'bw', 'xm', 'drawing_code', 'calc_memo', 'calc_memo_org', 'calc_img_remark', 'calc_img', 'position', 'jldy'];
         this.splitChar = '-';
     }
 
@@ -316,7 +316,7 @@ class StageIm {
         const posRange = this.pos.getLedgerPos(node.id);
         if (!posRange) { return; }
         for (const p of posRange) {
-            if (!p.gather_qty || this.ctx.helper.checkZero(p.gather_qty)) { continue; }
+            if (this.ctx.helper.checkZero(p.contract_qty) && this.ctx.helper.checkZero(p.contract_qty)) { continue; }
             let lp = this._.find(gclBills.pos, { name: p.name });
             if (!lp) {
                 lp = { name: p.name };
@@ -335,7 +335,8 @@ class StageIm {
                 if ((!p.b_code || p.b_code === '') || (p.children && p.children.length > 0)) {
                     continue;
                 }
-                if ((!p.contract_tp || p.contract_tp === 0) && (!p.qc_tp || p.qc_tp === 0)) {
+                if (!this.ctx.helper.checkZero(p.contract_qty) || !this.ctx.helper.checkZero(p.contract_tp) ||
+                    !this.ctx.helper.checkZero(p.qc_qty) || !this.ctx.helper.checkZero(p.qc_tp)) {
                     continue;
                 }
                 let b = this._.find(im.gclBills, { bid: p.id });
@@ -412,12 +413,41 @@ class StageIm {
             }
         }
     }
+
+    _checkUsed(node) {
+        const helper = this.ctx.helper;
+        if (node.children && node.children.length > 0) {
+            const posterity = this.billsTree.getPosterity(node);
+            for (const p of posterity) {
+                if (!p.children || p.children.length === 0) continue;
+                if (!helper.checkZero(p.contract_qty) || !helper.checkZero(p.contract_tp) ||
+                    !helper.checkZero(p.qc_qty) || !helper.checkZero(p.qc_tp))
+                    return true;
+                const pPos = this.pos.getLedgerPos(p.id);
+                if (!pPos || pPos.length === 0) continue;
+                for (const pp of pPos) {
+                    if (!helper.checkZero(pp.contract_qty) || !helper.checkZero(pp.qc_qty)) return true;
+                }
+            }
+            return false;
+        } else {
+            if (!helper.checkZero(node.contract_qty) || !helper.checkZero(node.contract_tp) ||
+                !helper.checkZero(node.qc_qty) || !helper.checkZero(node.qc_tp))
+                return true;
+            const pPos = this.pos.getLedgerPos(node.id);
+            if (!pPos || pPos.length === 0) return false;
+            for (const pp of pPos) {
+                if (!helper.checkZero(pp.contract_qty) || !helper.checkZero(pp.qc_qty)) return true;
+            }
+        }
+    }
+
     /**
      * 生成 0号台账 中间计量数据
      * @param {Object} node - 生成中间计量表的节点
      */
     _generateTzImData(node) {
-        if (node.gather_tp) {
+        if (this._checkUsed(node)) {
             const nodeIndex = this.billsTree.getNodeSerialNo(node);
             const peg = this._getPegNode(node);
             const im = {
@@ -559,7 +589,7 @@ class StageIm {
         return im;
     }
     _generateBwBillsImData (node) {
-        if (!node.gather_tp && !node.contract_tp && !node.qc_tp) return;
+        if (!this._checkUsed(node)) return;
 
         const nodeIndex = this.billsTree.getNodeSerialNo(node);
         const peg = this._getPegNode(node);
@@ -569,7 +599,7 @@ class StageIm {
         for (const p of posterity) {
             if (p.children && p.children.length > 0) continue;
             if (!p.b_code || p.b_code === '') continue;
-            if (!p.gather_tp || p.gather_tp === 0) continue;
+            if (!this._checkUsed(p)) continue;
 
             const pPos = this.pos.getLedgerPos(p.id);
             if (pPos && pPos.length > 0) {
@@ -598,11 +628,12 @@ class StageIm {
 
                 imDefault.contract_jl = this.ctx.helper.add(imDefault.contract_jl, p.contract_qty);
                 imDefault.qc_jl = this.ctx.helper.add(imDefault.qc_jl, p.qc_qty);
+                imDefault.used = true;
 
                 this._addBwBillsGclBills(imDefault, p);
             }
         }
-        if (imDefault.contract_jl || imDefault.qc_jl) {
+        if (imDefault.used) {
             imDefault.jl = this.ctx.helper.add(imDefault.contract_jl, imDefault.qc_jl);
             imDefault.id = this.ImData.length + 1;
             this.ImData.push(imDefault);
@@ -694,7 +725,7 @@ class StageIm {
         for (const p of posterity) {
             if (p.children && p.children.length > 0) { continue; }
             if (!p.b_code || p.b_code === '') { continue; }
-            if (!p.gather_tp || p.gather_tp === 0) { continue; }
+            if (!this._checkUsed(p)) { continue; }
             // if (this.ctx.helper.checkZero(p.contract_qty) && this.ctx.helper.checkZero(p.qc_qty)) { continue; }
             let im = nodeImData.find(function(d) {
                 return d.lid === node.id &&

+ 18 - 14
app/middleware/session_auth.js

@@ -41,6 +41,11 @@ module.exports = options => {
             if (this.session === null) {
                 throw '系统维护中~';
             }
+
+            // 对sub_menu项目默认打开页进行配置
+            const path = yield this.service.settingShow.getDefaultPath(this.session.sessionProject.id);
+            path && (this.curListUrl = path);
+
         } catch (error) {
             console.log(error);
             if (this.helper.isAjax(this.request)) {
@@ -53,22 +58,21 @@ module.exports = options => {
                 if (this.helper.isWap(this.request)) {
                     this.session.wapTenderID = this.params.id ? this.params.id : null;
                     return this.redirect('/wap/login?referer=' + this.url);
-                } else {
-                    return this.redirect('/login?referer=' + this.url);
-                }
-            } else {
-                if (this.helper.isWap(this.request)) {
-                    this.session.wapTenderID = this.params.id ? this.params.id : null;
-                    return this.redirect('/wap/login?referer=' + this.url);
-                } else {
-                    this.session.message = {
-                        type: messageType.ERROR,
-                        icon: 'exclamation-circle',
-                        message: '登录信息异常,请重新登录',
-                    };
-                    return this.redirect('/login?referer=' + this.url);
                 }
+                return this.redirect('/login?referer=' + this.url);
+            }
+            if (this.helper.isWap(this.request)) {
+                this.session.wapTenderID = this.params.id ? this.params.id : null;
+                return this.redirect('/wap/login?referer=' + this.url);
             }
+            this.session.message = {
+                type: messageType.ERROR,
+                icon: 'exclamation-circle',
+                message: '登录信息异常,请重新登录',
+            };
+            return this.redirect('/login?referer=' + this.url);
+
+
         }
         yield next;
     };

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

@@ -306,6 +306,7 @@ const gclGatherModel = (function () {
         if (deal && deal.length > 0) {
             for (const node of deal) {
                 node.b_code = node.code;
+                if (!node.quantity || !node.unit_price) continue;
                 const gcl = getGclNode(node);
                 gcl.deal_bills_qty = node.quantity;
                 gcl.deal_bills_tp = node.total_price;
@@ -414,7 +415,7 @@ const gclGatherModel = (function () {
         for (const c of chapter) {
             const cc = { code: c.code, name: c.name, cType: 1 };
             cc.serialNo = serialNo++;
-            cc.filter = '^' + c.code.substr(0, c.code.length - 2) + '[0-9]{2}-';
+            cc.filter = '^[\\D]*' + c.code.substr(0, c.code.length - 2) + '[0-9]{2}-';
             gclChapter.push(cc);
         }
         gclChapter.push({ name: '未计入章节清单合计', cType: 21, serialNo: serialNo++, });

+ 16 - 1
app/public/js/ledger.js

@@ -815,7 +815,6 @@ $(document).ready(function() {
         selectionChanged: function (e, info) {
             if (!info.oldSelections || !info.oldSelections[0] || info.newSelections[0].row !== info.oldSelections[0].row) {
                 posOperationObj.loadCurPosData();
-                SpreadJsObj.resetTopAndSelect(posSpread.getActiveSheet());
                 posSearch.search($('#pos-keyword').val());
             }
             SpreadJsObj.saveTopAndSelect(info.sheet, ckBillsSpread);
@@ -940,6 +939,7 @@ $(document).ready(function() {
         },
     };
     sjsSettingObj.setFxTreeStyle(ledgerSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(ledgerSpreadSetting);
     ledgerTreeCol.initSpreadSetting(ledgerSpreadSetting);
     SpreadJsObj.initSheet(ledgerSpread.getActiveSheet(), ledgerSpreadSetting);
     SpreadJsObj.selChangedRefreshBackColor(ledgerSpread.getActiveSheet());
@@ -1439,6 +1439,7 @@ $(document).ready(function() {
                 posSpread.refresh();
             }
         });
+        sjsSettingObj.setGridSelectStyle(posSpreadSetting);
         SpreadJsObj.initSheet(posSpread.getActiveSheet(), posSpreadSetting);
     }
     //绑定计量单元编辑事件
@@ -1524,6 +1525,7 @@ $(document).ready(function() {
         loadCurPosData: function () {
             //spreadJsObj.reinitSheet(posSpread.getActiveSheet());
             const node = treeOperationObj.getSelectNode(ledgerSpread.getActiveSheet());
+            SpreadJsObj.resetTopAndSelect(posSpread.getActiveSheet());
             if (node) {
                 const posData = pos.ledgerPos[itemsPre + node.id] || [];
                 SpreadJsObj.loadSheetData(posSpread.getActiveSheet(), 'data', posData);
@@ -2182,6 +2184,7 @@ $(document).ready(function() {
                             emptyRows: 0,
                             headRows: 1,
                             headRowHeight: [32],
+                            headColWidth: [30],
                             defaultRowHeight: 21,
                             headerFont: '12px 微软雅黑',
                             font: '12px 微软雅黑',
@@ -2475,6 +2478,17 @@ $(document).ready(function() {
                     }
                 }
             });
+            this.posSpread.bind(spreadNS.Events.ClipboardPasted, function (e, info) {
+                const billsCount = info.sheet.getColumnCount() - 2, qdSheet = self.qdSpread.getActiveSheet();
+                const count = qdSheet.getRowCount();
+                if (billsCount > count) {
+                    qdSheet.setRowCount(billsCount);
+                    for (let i = count + 1; i <= billsCount; i++) {
+                        qdSheet.getCell(i - 1, 0, spreadNS.SheetArea.rowHeader).text('清单' + i);
+                        info.sheet.getCell(0, i + 2 - 1, spreadNS.SheetArea.colHeader).text('清单' + i);
+                    }
+                }
+            });
             this.qdSpread.bind(spreadNS.Events.EditEnded, function (e, info) {
                 if (info.col === 0) {
                     const dealBills = self.dealSpread.getActiveSheet().zh_data;
@@ -2513,6 +2527,7 @@ $(document).ready(function() {
                             sheet.setSelection(result.create[0].index, sel.col, sel.rowCount, sel.colCount);
                             SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, result.create[0].index]);
                             treeOperationObj.refreshOperationValid(sheet);
+                            posOperationObj.loadCurPosData();
                             self.obj.modal('hide');
                         }, null, true);
                     } else {

+ 4 - 0
app/public/js/ledger_audit.js

@@ -36,6 +36,7 @@ $(document).ready(() => {
     }
     const ledgerTree = createNewPathTree('fx', treeSetting);
     sjsSettingObj.setFxTreeStyle(ledgerSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(ledgerSpreadSetting);
     SpreadJsObj.initSheet(ledgerSpread.getActiveSheet(), ledgerSpreadSetting);
 
     // 初始化 部位明细
@@ -66,6 +67,7 @@ $(document).ready(() => {
 
     const loadCurPosData = function () {
         const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
+        SpreadJsObj.resetTopAndSelect(posSpread.getActiveSheet());
         if (node) {
             const posData = pos.ledgerPos[itemsPre + node.id] || [];
             SpreadJsObj.loadSheetData(posSpread.getActiveSheet(), 'data', posData);
@@ -84,6 +86,7 @@ $(document).ready(() => {
                 posSpread.refresh();
             }
         });
+        sjsSettingObj.setGridSelectStyle(posSpreadSetting);
         SpreadJsObj.initSheet(posSpread.getActiveSheet(), posSpreadSetting);
     }
 
@@ -189,6 +192,7 @@ $(document).ready(() => {
                             emptyRows: 0,
                             headRows: 1,
                             headRowHeight: [32],
+                            headColWidth: [30],
                             defaultRowHeight: 21,
                             headerFont: '12px 微软雅黑',
                             font: '12px 微软雅黑',

+ 4 - 0
app/public/js/ledger_bwtz.js

@@ -14,15 +14,18 @@ $(document).ready(() => {
     const xmjSpread = SpreadJsObj.createNewSpread($('#xmj-spread')[0]);
     const xmjSheet = xmjSpread.getActiveSheet();
     sjsSettingObj.setFxTreeStyle(xmjSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(xmjSpreadSetting);
     SpreadJsObj.initSheet(xmjSheet, xmjSpreadSetting);
 
     const unitSpread = SpreadJsObj.createNewSpread($('#unit-spread')[0]);
     const unitSheet = unitSpread.getActiveSheet();
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(xmjSpreadSetting);
     SpreadJsObj.initSheet(unitSheet, unitSpreadSetting);
 
     const unitTreeObj = {
         loadCurUnitData: function () {
             const node = SpreadJsObj.getSelectObject(xmjSheet);
+            SpreadJsObj.resetTopAndSelect(unitSheet);
             if (node && node.unitTree) {
                 SpreadJsObj.loadSheetData(unitSheet, SpreadJsObj.DataType.Tree, node.unitTree);
             } else {
@@ -144,6 +147,7 @@ $(document).ready(() => {
                             emptyRows: 0,
                             headRows: 1,
                             headRowHeight: [32],
+                            headColWidth: [30],
                             defaultRowHeight: 21,
                             headerFont: '12px 微软雅黑',
                             font: '12px 微软雅黑',

+ 14 - 7
app/public/js/ledger_gather.js

@@ -11,7 +11,7 @@
 $(document).ready(() => {
     autoFlashHeight();
     const gclSpread = SpreadJsObj.createNewSpread($('#gcl-spread')[0]);
-    SpreadJsObj.initSheet(gclSpread.getActiveSheet(), {
+    const gclSpreadSetting = {
         cols: [
             {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 120, formatter: '@'},
             {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@'},
@@ -27,6 +27,7 @@ $(document).ready(() => {
         emptyRows: 0,
         headRows: 2,
         headRowHeight: [25, 25],
+        headColWidth: [30],
         defaultRowHeight: 21,
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
@@ -36,17 +37,19 @@ $(document).ready(() => {
             colWidth: true,
         },
         getColor: function (sheet, data, row, col, defaultColor) {
-            return data 
-                ? $('#compare-tag')[0].checked && data.compare_differ 
-                    ? '#f8d7da' 
+            return data
+                ? $('#compare-tag')[0].checked && data.compare_differ
+                    ? '#f8d7da'
                     : (data.differ ? '#FFE699' : defaultColor)
                 : defaultColor;
             //return data && data.differ ? '#FFE699' : defaultColor;
         }
-    });
+    };
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(gclSpreadSetting);
+    SpreadJsObj.initSheet(gclSpread.getActiveSheet(), gclSpreadSetting);
     const gclSheet = gclSpread.getActiveSheet();
     const leafXmjSpread = SpreadJsObj.createNewSpread($('#leaf-xmj-spread')[0]);
-    SpreadJsObj.initSheet(leafXmjSpread.getActiveSheet(), {
+    const leafXmjSpreadSetting = {
         cols: [
             {title: '项目节编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 120, formatter: '@'},
             {title: '台账数量', colSpan: '1', rowSpan: '1', field: 'quantity', hAlign: 2, width: 80, type: 'Number'},
@@ -60,6 +63,7 @@ $(document).ready(() => {
         emptyRows: 0,
         headRows: 1,
         headRowHeight: [32],
+        headColWidth: [30],
         defaultRowHeight: 21,
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
@@ -68,13 +72,16 @@ $(document).ready(() => {
             key: 'ledger-gather-leafXmj',
             colWidth: true,
         },
-    });
+    };
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(leafXmjSpreadSetting);
+    SpreadJsObj.initSheet(leafXmjSpread.getActiveSheet(), leafXmjSpreadSetting);
     const leafXmjSheet = leafXmjSpread.getActiveSheet();
 
     let gclGatherData;
     // 获取项目节数据
     function loadLeafXmjData(iGclRow) {
         const gcl = gclGatherData[iGclRow];
+        SpreadJsObj.resetTopAndSelect(leafXmjSheet);
         if (gcl) {
             SpreadJsObj.loadSheetData(leafXmjSheet, SpreadJsObj.DataType.Data, gcl.leafXmjs);
         } else {

+ 130 - 0
app/public/js/material_file.js

@@ -0,0 +1,130 @@
+'use strict';
+
+/**
+ * 材料调差 - 附件
+ * @author LanJianRong
+ * @date 2020/06/30
+ * @version
+ */
+
+$(document).ready(function () {
+    $('#upload-file-ok').click(function () {
+        const files = Array.from($('#upload-fujian-file')[0].files)
+        const valiData = files.map(v => {
+            const ext = v.name.substring(v.name.lastIndexOf('.') + 1)
+            return {
+                size: v.size,
+                ext
+            }
+        })
+        if (validateFiles(valiData)) {
+            if (files.length) {
+                const formData = new FormData()
+                files.forEach(file => {
+                    formData.append('file', file)
+                    formData.append('name', file.name)
+                    formData.append('size', file.size)
+                })
+                postDataWithFile(window.location.pathname + '/upload', formData, function (result) {
+                    $('#addfujian').modal('hide');
+                    let html = '';
+                    result.forEach((fileInfo, idx) => {
+                        html += `<tr>
+                        <td>${idx + 1}</td>
+                        <td><a href="/${fileInfo.filepath}">${fileInfo.file_name}</a></td>
+                        <td>${fileInfo.file_size}</td>
+                        <td>${fileInfo.upload_time}</td>`
+                    if (user_id == fileInfo.user_id && checked) {
+                        html += `<td>
+                        <a class="btn btn-light btn-sm delete-file" data-attid="${fileInfo.id}" title="删除附件">
+                        <span class="fa fa-trash text-danger"></span>
+                        </a>
+                        </td></tr>`
+                    } else {
+                        html += `<td></td></tr>`
+                    }
+                    })
+                    $('#file-list').empty();
+                    $('#file-list').append(html);
+                    console.log(result)
+                });
+            }
+        }
+    })
+    $('#file-checkbox').click(function() {
+        let isCheck = false
+        if($(this).is(':checked')) {
+            isCheck = true
+        }
+        postData(window.location.pathname + '/find', {isCheck}, function(result) {
+            let html = '';
+            result.forEach((fileInfo, idx) => {
+                html += `<tr>
+                <td>${idx + 1 }</td>
+                <td><a href="/${fileInfo.filepath}">${fileInfo.file_name}</a></td>
+                <td>${fileInfo.file_size}</td>
+                <td>${fileInfo.upload_time}</td>`
+                if (isCheck) {
+                    // 所有期
+                    if (fileInfo.pre_delete && user_id == fileInfo.user_id) {
+                        html += `<td>
+                        <a class="btn btn-light btn-sm delete-file" data-attid="${fileInfo.id}" title="删除附件">
+                        <span class="fa fa-trash text-danger"></span>
+                        </a>
+                        </td></tr>`
+                    } else {
+                        html += `<td></td></tr>`
+                    }
+                } else {
+                    // 当前期
+                    if (user_id == fileInfo.user_id && checked) {
+                        html += `<td>
+                        <a class="btn btn-light btn-sm delete-file" data-attid="${fileInfo.id}" title="删除附件">
+                        <span class="fa fa-trash text-danger"></span>
+                        </a>
+                        </td></tr>`
+                    } else {
+                        html += `<td></td></tr>`
+                    }
+                }
+
+            })
+            $('#file-list').empty();
+            $('#file-list').append(html);
+        })
+    })
+    // 删除附件
+    $('body').on('click', '.delete-file', function () {
+        let attid = $(this).data('attid');
+        let self = $(this);
+        const data = {id: attid};
+        postData('/tender/measure/material/file/delete', data, function (result) {
+            self.parents('tr').remove();
+            // 重新排序
+            let newsort = 1;
+            $('#file-list tr').each(function(){
+                $(this).children('td').eq(0).text(newsort);
+                newsort++;
+            });
+        });
+    });
+});
+
+/**
+ * 校验文件大小、格式
+ * @param {Array} files 文件数组
+ */
+function validateFiles(files) {
+    const reg = /(doc|docx|excel|xlsx|xls|txt|zip|jpg|jpeg|png|bmp|BMP|JPG|PNG|JPEG)$/;
+    return files.every(file => {
+        if (file.size > 1024 * 1024 * 10) {
+            toastr.error('文件大小限制为10MB');
+            return false
+        }
+        if (!reg.test(file.ext)) {
+            toastr.error('请上传正确的格式文件');
+            return false
+        }
+        return true
+    })
+}

+ 6 - 0
app/public/js/measure_compare.js

@@ -29,6 +29,7 @@ const billsSpreadSetting = {
     font: '12px 微软雅黑',
     defaultRowHeight: 21,
     readOnly: true,
+    selectedBackColor: '#fffacd',
 };
 const posSpreadSetting = {
     baseCols: [
@@ -41,10 +42,12 @@ const posSpreadSetting = {
     emptyRows: 3,
     headRows: 1,
     headRowHeight: [32],
+    headColWidth: [30],
     defaultRowHeight: 21,
     headerFont: '12px 微软雅黑',
     font: '12px 微软雅黑',
     readOnly: true,
+    selectedBackColor: '#fffacd',
 };
 function initSpreadSettingWithRoles(compareRoles) {
     function setSpreadSettingCols(setting, fieldSufs, Roles) {
@@ -95,9 +98,11 @@ $(document).ready(() => {
     const billsSpread = SpreadJsObj.createNewSpread($('#bills-spread')[0]);
     const billsSheet = billsSpread.getActiveSheet();
     sjsSettingObj.setFxTreeStyle(billsSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(billsSpreadSetting);
     SpreadJsObj.initSheet(billsSheet, billsSpreadSetting);
     const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
     const posSheet = posSpread.getActiveSheet();
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(posSpreadSetting);
     SpreadJsObj.initSheet(posSheet, posSpreadSetting);
 
     $.subMenu({
@@ -155,6 +160,7 @@ $(document).ready(() => {
     }, null, true);
     function loadPosData(iRow) {
         const node = iRow ? billsSheet.zh_tree.nodes[iRow] : SpreadJsObj.getSelectObject(billsSheet);
+        SpreadJsObj.resetTopAndSelect(posSheet);
         if (node) {
             SpreadJsObj.loadSheetData(posSheet, SpreadJsObj.DataType.Data, cPos.getLedgerPos(node.id));
         } else {

+ 8 - 0
app/public/js/path_tree.js

@@ -83,6 +83,14 @@ class PosData {
                 resort.push(masterKey);
             }
         }
+        if (this.setting.calcFun) {
+            for (const u of result.update) {
+                this.setting.calcFun(u);
+            }
+            for (const c of result.create) {
+                this.setting.calcFun(c);
+            }
+        }
         for (const s of resort) {
             this.resortLedgerPos(this.ledgerPos[s]);
         }

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

@@ -35,9 +35,12 @@ $(document).ready(() => {
     const billsSpread = SpreadJsObj.createNewSpread($('#bills-spread')[0]);
     const billsSheet = billsSpread.getActiveSheet();
     sjsSettingObj.setFxTreeStyle(billsSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(billsSpreadSetting);
     SpreadJsObj.initSheet(billsSheet, billsSpreadSetting);
     const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
     const posSheet = posSpread.getActiveSheet();
+    sjsSettingObj.setGridSelectStyle(posSpreadSetting);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(SpreadSetting);
     SpreadJsObj.initSheet(posSheet, posSpreadSetting);
 
     const posSearch = $.posSearch({selector: '#pos-search', searchSpread: posSpread});
@@ -1163,6 +1166,7 @@ $(document).ready(() => {
          */
         loadCurPosData: function () {
             const node = SpreadJsObj.getSelectObject(billsSheet);
+            SpreadJsObj.resetTopAndSelect(posSheet);
             if (node) {
                 const posData = pos.getLedgerPos(node.id) || [];
                 SpreadJsObj.loadSheetData(posSheet, 'data', posData);
@@ -1914,6 +1918,17 @@ $(document).ready(() => {
                     }
                 }
             });
+            this.posSpread.bind(spreadNS.Events.ClipboardPasted, function (e, info) {
+                const billsCount = info.sheet.getColumnCount() - 2, qdSheet = self.qdSpread.getActiveSheet();
+                const count = qdSheet.getRowCount();
+                if (billsCount > count) {
+                    qdSheet.setRowCount(billsCount);
+                    for (let i = count + 1; i <= billsCount; i++) {
+                        qdSheet.getCell(i - 1, 0, spreadNS.SheetArea.rowHeader).text('清单' + i);
+                        info.sheet.getCell(0, i + 2 - 1, spreadNS.SheetArea.colHeader).text('清单' + i);
+                    }
+                }
+            });
             this.qdSpread.bind(spreadNS.Events.EditEnded, function (e, info) {
                 if (info.col === 0) {
                     const dealBills = self.dealSpread.getActiveSheet().zh_data;
@@ -1953,6 +1968,7 @@ $(document).ready(() => {
                             const result = billsTree.loadPostData(data.ledger);
                             billsTreeSpreadObj.refreshTree(billsSheet, result);
                             billsTreeSpreadObj.refreshOperationValid(billsSheet, selection);
+                            posSpreadObj.loadCurPosData();
                             self.obj.modal('hide');
                         }, null, true);
                     } else {
@@ -2075,6 +2091,7 @@ $(document).ready(() => {
         emptyRows: 0,
         headRows: 1,
         headRowHeight: [32],
+        headColWidth: [30],
         defaultRowHeight: 21,
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
@@ -2092,6 +2109,7 @@ $(document).ready(() => {
         emptyRows: 0,
         headRows: 1,
         headRowHeight: [32],
+        headColWidth: [30],
         defaultRowHeight: 21,
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
@@ -2321,6 +2339,7 @@ $(document).ready(() => {
                             emptyRows: 0,
                             headRows: 1,
                             headRowHeight: [32],
+                            headColWidth: [30],
                             defaultRowHeight: 21,
                             headerFont: '12px 微软雅黑',
                             font: '12px 微软雅黑',

+ 5 - 0
app/public/js/revise_history.js

@@ -15,9 +15,12 @@ $(document).ready(() => {
     const billsSpread = SpreadJsObj.createNewSpread($('#bills-spread')[0]);
     const billsSheet = billsSpread.getActiveSheet();
     sjsSettingObj.setFxTreeStyle(billsSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(billsSpreadSetting);
     SpreadJsObj.initSheet(billsSheet, billsSpreadSetting);
     const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
     const posSheet = posSpread.getActiveSheet();
+    sjsSettingObj.setGridSelectStyle(posSpreadSetting);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(posSpreadSetting);
     SpreadJsObj.initSheet(posSheet, posSpreadSetting);
 
     const posSearch = isTz ? $.posSearch({selector: '#pos-search', searchSpread: posSpread}) : null;
@@ -140,6 +143,7 @@ $(document).ready(() => {
          */
         loadCurPosData: function () {
             const node = SpreadJsObj.getSelectObject(billsSheet);
+            SpreadJsObj.resetTopAndSelect(posSheet);
             if (node) {
                 const posData = pos.getLedgerPos(node.id) || [];
                 SpreadJsObj.loadSheetData(posSheet, 'data', posData);
@@ -236,6 +240,7 @@ $(document).ready(() => {
                             emptyRows: 0,
                             headRows: 1,
                             headRowHeight: [32],
+                            headColWidth: [30],
                             defaultRowHeight: 21,
                             headerFont: '12px 微软雅黑',
                             font: '12px 微软雅黑',

+ 24 - 0
app/public/js/setting.js

@@ -169,6 +169,30 @@ $(document).ready(() => {
         $('#bind_account').text(accountData.name + ' ' + accountData.mobile);
         $('#account_id').val(id);
     })
+
+    // 设置显示默认
+    $('body').on('click', '#set-default', function () {
+        const attid = $(this).data('attid');
+        const data = {id: attid};
+        postData('/setting/show/update', data, function (result) {
+           let html = ''
+            result.forEach((item, idx) => {
+                html += `<li class="list-group-item">${item.label_name}`
+                html+= item.is_default ? `<span class="pull-right">默认</span></li>`:`<a href="javascript:void(0)" id="set-default" class="btn btn-primary btn-sm pull-right" data-attid="${idx}">设为默认</a></li>`
+            })
+            // <li class="list-group-item">
+            // 标段列表<a href="#" class="btn btn-primary btn-sm pull-right">设为默认</a>
+            // </li>
+            // <li class="list-group-item">金额概况<span class="pull-right">默认</span></li>
+            // <li class="list-group-item">
+            //     计量进度<a href="#" class="btn btn-primary btn-sm pull-right">设为默认</a>
+            // </li>
+            $('.list-group').empty()
+            $('.list-group').append(html)
+            const item = result.find((i, idx) => idx=== attid)
+            $('#nav_tender').attr('href', item.path)
+        });
+    });
 });
 
 function checkPasswordForm() {

+ 21 - 52
app/public/js/shares/cs_tools.js

@@ -220,6 +220,7 @@ const showSideTools = function (show) {
                         sheet.setSelection(pos.index, sel ? sel.col : 0, 1, 1);
                         sheet.getParent().focus();
                         sheet.showRow(pos.index, spreadNS.VerticalPosition.center);
+                        SpreadJsObj.reloadRowsBackColor(sheet, [pos.index, curRow]);
                     }
                 }
             };
@@ -235,6 +236,7 @@ const showSideTools = function (show) {
                         sheet.setSelection(next.index, sel ? sel.col : 0, 1, 1);
                         sheet.getParent().focus();
                         sheet.showRow(next.index, spreadNS.VerticalPosition.center);
+                        SpreadJsObj.reloadRowsBackColor(sheet, [next.index, curRow]);
                     }
                 }
             };
@@ -250,6 +252,7 @@ const showSideTools = function (show) {
                         sheet.setSelection(next.index, sel ? sel.col : 0, 1, 1);
                         sheet.getParent().focus();
                         sheet.showRow(next.index, spreadNS.VerticalPosition.center);
+                        SpreadJsObj.reloadRowsBackColor(sheet, [next.index, curRow]);
                     }
                 }
             };
@@ -278,8 +281,11 @@ const showSideTools = function (show) {
         if (setting.searchOver || setting.searchEmpty) {
             filter.push('<select class="form-control form-control-sm" id="search-filter">');
             filter.push('<option value="">台账</option>');
-            if (setting.searchOver) filter.push('<option value="over">超计</option>');
-            if (setting.searchEmpty) filter.push('<option value="less">漏计</option>');
+            if (setting.customSearch) {
+                for (const cs of setting.customSearch) {
+                    if (cs.valid) filter.push('<option value="' + cs.key + '">' + cs.title + '</option>');
+                }
+            }
             filter.push('</select>');
             // filter.push('<div class="input-group-prepend">');
             // filter.push('<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">台帐</button>');
@@ -313,10 +319,8 @@ const showSideTools = function (show) {
         let searchResult = [];
         const search = function () {
             const filter = $('#search-filter').val();
-            if (filter === 'over') {
-                searchOver();
-            } else if (filter === 'less') {
-                searchLess();
+            if (filter) {
+                searchCustom(filter);
             } else {
                 searchBills();
             }
@@ -336,20 +340,18 @@ const showSideTools = function (show) {
             }
             SpreadJsObj.loadSheetData(resultSpread.getActiveSheet(), 'data', searchResult);
         };
-        const calculateCompletePercent = function (searchResult) {
-            if (!searchResult) return;
-            for (const sr of searchResult) {
-                const base = ZhCalc.add(sr.total_price, sr.end_qc_tp);
-                sr.complete_percent = base !== 0 ? ZhCalc.mul(ZhCalc.div(sr.end_gather_tp, base), 100, 2) : 0;
-            }
+        const getCheckFun = function (key) {
+            const cs = setting.customSearch.find(function (x) {return x.key === key});
+            return cs ? cs.check : null;
         };
-        const searchOver = function () {
+        const searchCustom = function (key) {
             const keyword = $('#searchKeyword', obj).val();
+            const checkFun = getCheckFun(key);
             searchResult = [];
             const sortData = SpreadJsObj.getSortData(searchSheet);
             for (const node of sortData) {
                 if (node.children && node.children.length > 0) continue;
-                if (setting.checkOver && setting.checkOver(node)) {
+                if (checkFun && checkFun(node)) {
                     if (!keyword ||
                         (node.code && node.code.indexOf(keyword) > -1) ||
                         (node.b_code && node.b_code.indexOf(keyword) > -1) ||
@@ -363,45 +365,12 @@ const showSideTools = function (show) {
             calculateCompletePercent(searchResult);
             SpreadJsObj.loadSheetData(resultSpread.getActiveSheet(), 'data', searchResult);
         };
-        const searchEmpty = function () {
-            const keyword = $('#searchKeyword', obj).val();
-            searchResult = [];
-            const sortData = SpreadJsObj.getSortData(searchSheet);
-            for (const node of sortData) {
-                if (node.children && node.children.length > 0) continue;
-                if (setting.checkEmpty && setting.checkEmpty(node)) {
-                    if (!keyword ||
-                        (node.code && node.code.indexOf(keyword) > -1) ||
-                        (node.b_code && node.b_code.indexOf(keyword) > -1) ||
-                        (node.name && node.name.indexOf(keyword) > -1)) {
-                        const data = JSON.parse(JSON.stringify(node));
-                        data.visible = true;
-                        searchResult.push(data);
-                    }
-                }
-            }
-            calculateCompletePercent(searchResult);
-            SpreadJsObj.loadSheetData(resultSpread.getActiveSheet(), 'data', searchResult);
-        };
-        const searchLess = function () {
-            const keyword = $('#searchKeyword', obj).val();
-            searchResult = [];
-            const sortData = SpreadJsObj.getSortData(searchSheet);
-            for (const node of sortData) {
-                if (node.children && node.children.length > 0) continue;
-                if (setting.checkLess && setting.checkLess(node)) {
-                    if (!keyword ||
-                        (node.code && node.code.indexOf(keyword) > -1) ||
-                        (node.b_code && node.b_code.indexOf(keyword) > -1) ||
-                        (node.name && node.name.indexOf(keyword) > -1)) {
-                        const data = JSON.parse(JSON.stringify(node));
-                        data.visible = true;
-                        searchResult.push(data);
-                    }
-                }
+        const calculateCompletePercent = function (searchResult) {
+            if (!searchResult) return;
+            for (const sr of searchResult) {
+                const base = ZhCalc.add(sr.total_price, sr.end_qc_tp);
+                sr.complete_percent = base !== 0 ? ZhCalc.mul(ZhCalc.div(sr.end_gather_tp, base), 100, 2) : 0;
             }
-            calculateCompletePercent(searchResult);
-            SpreadJsObj.loadSheetData(resultSpread.getActiveSheet(), 'data', searchResult);
         };
 
         $('input', obj).bind('keydown', function (e) {

+ 16 - 2
app/public/js/shares/sjs_setting.js

@@ -1,7 +1,7 @@
 const sjsSettingObj = (function () {
     const FxTreeStyle = {
         jz: 'jianzhu',
-    }
+    };
     const setJzFxTreeStyle = function (setting) {
         setting.selectedBackColor = '#fffacd';
         setting.tree = {
@@ -38,5 +38,19 @@ const sjsSettingObj = (function () {
     const setGridSelectStyle = function (setting) {
         setting.selectedBackColor = '#fffacd';
     };
-    return {setFxTreeStyle, FxTreeStyle, setGridSelectStyle};
+    const setTpThousandthFormat = function (setting, fields = []) {
+        for (const col of setting.cols) {
+            if (col.field === 'total_price' || col.field.indexOf('tp') >= 0 || fields.indexOf(col.field) >= 0)
+                col.formatter = '0.000';
+        }
+    };
+    const setThousandthFormat = function (setting, fields) {
+        for (const f of fields) {
+            const col = setting.cols.find(function (x) {
+                return x.field === f
+            });
+            if (col) col.formatter = '0.000';
+        }
+    };
+    return {setFxTreeStyle, FxTreeStyle, setGridSelectStyle, setTpThousandthFormat, setThousandthFormat};
 })();

+ 36 - 11
app/public/js/spreadjs_rela/spreadjs_zh.js

@@ -478,6 +478,15 @@ const SpreadJsObj = {
                 cell.locked(col.readOnly || sheet.zh_setting.readOnly || false).vAlign(1).hAlign(col.hAlign);
             }
 
+            // if(col.type === 'Number') {
+            //     if (col.formatter) {
+            //         cell.formatter(SpreadJsObj.Formatter.getNumberFormatter(col.formatter))
+            //     } else {
+            //         cell.formatter(SpreadJsObj.Formatter.getNumberFormatter('0.######'));
+            //     }
+            // } else if (col.formatter) {
+            //     cell.formatter(col.formatter);
+            // }
             if (col.formatter) {
                 cell.formatter(col.formatter);
             } else if (col.type === 'Number') {
@@ -856,6 +865,15 @@ const SpreadJsObj = {
         sheet.getParent().focus();
         sheet.showRow(index, spreadNS.VerticalPosition.center);
     },
+    locateData: function (sheet, data) {
+        if (!sheet.zh_data) { return }
+        const index = sheet.zh_data.indexOf(data);
+        const sels = sheet.getSelections();
+        sheet.setSelection(index, sels[0].col, 1, 1);
+        SpreadJsObj.reloadRowsBackColor(sheet, [index, sels[0].row]);
+        sheet.getParent().focus();
+        sheet.showRow(index, spreadNS.VerticalPosition.center);
+    },
     saveTopAndSelect: function (sheet, cacheKey) {
         const sel = sheet.getSelections()[0];
         const top = sheet.getViewportTopRow(1);
@@ -1684,16 +1702,20 @@ const SpreadJsObj = {
                 }
 
             };
-            proto.paint = function (canvas, value, x, y, w, h, style, options) {
-                const col = options.sheet.zh_setting.cols[options.col];
+            proto.getActiveImage = function(sheet, iRow, iCol) {
+                const col = sheet.zh_setting.cols[iCol];
 
-                const sortData = SpreadJsObj.getSortData(options.sheet);
-                const data = sortData ? sortData[options.row] : null;
+                const sortData = SpreadJsObj.getSortData(sheet);
+                const data = sortData ? sortData[iRow] : null;
                 let showImage = true;
                 if (col.showImage && Object.prototype.toString.apply(col.showImage) === "[object Function]") {
                     showImage = col.showImage(data);
                 }
-                const img = showImage ? this.getImage(options.sheet, options.row, options.col) : null;
+                const img = showImage ? this.getImage(sheet, iRow, iCol) : null;
+                return [col, img];
+            };
+            proto.paint = function (canvas, value, x, y, w, h, style, options) {
+                const [col, img] = this.getActiveImage(options.sheet, options.row, options.col);
 
                 const indent = col.indent ? col.indent : 10;
                 if (style.hAlign === spreadNS.HorizontalAlign.right) {
@@ -1793,8 +1815,9 @@ const SpreadJsObj = {
              注释部分以进入鼠标进入图片,点击图片为基准更新图片,鼠标快速移动时,可能失效
               */
             proto.processMouseDown = function (hitinfo) {
-                const col = hitinfo.sheet.zh_setting.cols[hitinfo.col];
-                const img = this.getImage(hitinfo.sheet, hitinfo.row, hitinfo.col);
+                const [col, img] = this.getActiveImage(hitinfo.sheet, hitinfo.row, hitinfo.col);
+                if (!img) return;
+
                 const halfX = img.width / 2, halfY = img.height / 2;
                 const indent = col.indent ? col.indent : 10;
                 const centerX = hitinfo.cellStyle.hAlign === spreadNS.HorizontalAlign.right
@@ -1809,8 +1832,9 @@ const SpreadJsObj = {
                 }
             };
             proto.processMouseUp = function (hitinfo) {
-                const col = hitinfo.sheet.zh_setting.cols[hitinfo.col];
-                const img = this.getImage(hitinfo.sheet, hitinfo.row, hitinfo.col);
+                const [col, img] = this.getActiveImage(hitinfo.sheet, hitinfo.row, hitinfo.col);
+                if (!img) return;
+
                 const halfX = img.width / 2, halfY = img.height / 2;
                 const indent = col.indent ? col.indent : 10;
                 const centerX = hitinfo.cellStyle.hAlign === spreadNS.HorizontalAlign.right
@@ -1831,8 +1855,9 @@ const SpreadJsObj = {
                 }
             };
             proto.processMouseMove = function (hitinfo) {
-                const col = hitinfo.sheet.zh_setting.cols[hitinfo.col];
-                const img = this.getImage(hitinfo.sheet, hitinfo.row, hitinfo.col);
+                const [col, img] = this.getActiveImage(hitinfo.sheet, hitinfo.row, hitinfo.col);
+                if (!img) return;
+
                 const halfX = img.width / 2, halfY = img.height / 2;
                 const indent = col.indent ? col.indent : 10;
                 const centerX = hitinfo.cellStyle.hAlign === spreadNS.HorizontalAlign.right

+ 173 - 87
app/public/js/stage.js

@@ -179,6 +179,9 @@ $(document).ready(() => {
         pos.end_gather_qty = ZhCalc.add(pos.pre_gather_qty, pos.gather_qty);
         pos.sum = ZhCalc.add(pos.end_qc_qty, pos.quantity);
         pos.end_gather_percent = ZhCalc.mul(ZhCalc.div(pos.end_gather_qty, pos.sum), 100, 2);
+        pos.estimate_qty = !checkZero(pos.real_qty)
+            ? ZhCalc.sub(ZhCalc.sub(pos.real_qty, pos.quantity), pos.end_qc_qty)
+            : null;
     };
     const stagePos = new StagePosData(stagePosSetting);
 
@@ -278,6 +281,7 @@ $(document).ready(() => {
                     }
                 }
             });
+
             // 过滤可变更数量为0
             $('#filterEmpty').click(function () {
                 self._filterChange(!this.checked, $('#matchPos')[0].checked);
@@ -491,6 +495,7 @@ $(document).ready(() => {
         }
     };
     sjsSettingObj.setFxTreeStyle(ledgerSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(ledgerSpreadSetting);
     SpreadJsObj.initSheet(slSpread.getActiveSheet(), ledgerSpreadSetting);
     slSpread.getActiveSheet().frozenColumnCount(5);
     slSpread.getActiveSheet().options.frozenlineColor = '#93b5e4';
@@ -515,6 +520,8 @@ $(document).ready(() => {
     posSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
         return data && data.end_contract_qty > data.quantity ? '#f8d7da' : defaultColor;
     };
+    sjsSettingObj.setGridSelectStyle(posSpreadSetting);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(posSpreadSetting);
     SpreadJsObj.initSheet(spSpread.getActiveSheet(), posSpreadSetting);
 
     const errorList = $.cs_errorList({
@@ -1040,6 +1047,47 @@ $(document).ready(() => {
         });
     }
     stageTreeSpreadObj.loadExprToInput(slSpread.getActiveSheet());
+    $.contextMenu({
+        selector: '#stage-ledger',
+        build: function ($trigger, e) {
+            const target = SpreadJsObj.safeRightClickSelection($trigger, e, slSpread);
+            return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
+        },
+        items: {
+            'locateZjjl': {
+                name: '定位至中间计量',
+                icon: 'fa-sign-in',
+                callback: function (key, opt) {
+                    if (!detail) {
+                        detail = new Detail($('#detail-spread'));
+                    }
+                    const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
+                    const [leafUsedBills, usedPos] = stageIm.getFirstUsed(node);
+                    if (leafUsedBills) {
+                        if (!$('#zhongjian').hasClass('active')) {
+                            const tab = $('#zhongjian'), tabPanel = $(tab.attr('content'));
+                            $('a', '.side-menu').removeClass('active');
+                            $('.tab-content .tab-select-show').removeClass('active');
+                            tab.addClass('active');
+                            tabPanel.addClass('active');
+                            showSideTools(tab.hasClass('active'));
+                            slSpread.refresh();
+                            spSpread.refresh();
+                        }
+                        const relaXmj = stageIm.getRelaXmj(leafUsedBills);
+                        const im = stageIm.getRelaImData(relaXmj, leafUsedBills, usedPos);
+                        SpreadJsObj.locateData(detail.sheet, im);
+                        detail.spread.refresh();
+                        $('#zhongjian .sjs-bottom').height('400px');
+                        $('.zhongjian-msg').height($('#zhongjian .sjs-bottom').height());
+                        detail.reLoadDetailData();
+                    } else {
+                        toastr.error('无可定位中间计量');
+                    }
+                },
+            },
+        }
+    });
 
     const stagePosSpreadObj = {
         loadExprToInput(sheet) {
@@ -1069,6 +1117,7 @@ $(document).ready(() => {
          */
         loadCurPosData: function () {
             const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
+            SpreadJsObj.resetTopAndSelect(spSpread.getActiveSheet());
             if (node) {
                 const posData = stagePos.ledgerPos[itemsPre + node.id] || [];
                 SpreadJsObj.loadSheetData(spSpread.getActiveSheet(), 'data', posData);
@@ -1207,7 +1256,7 @@ $(document).ready(() => {
                 const range = info.cellRange;
                 const validField = ['contract_qty', 'qc_qty', 'postil'];
                 if (!checkTzMeasureType()) {
-                    validField.push('name', 'sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'position', 'drawing_code');
+                    validField.push('name', 'sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'real_qty', 'position', 'drawing_code');
                 }
                 for (let iCol = range.col; iCol < range.col + range.colCount; iCol++) {
                     const col = info.sheet.zh_setting.cols[iCol];
@@ -1422,6 +1471,7 @@ $(document).ready(() => {
         },
         selectionChanged: function (e, info) {
             stagePosSpreadObj.loadExprToInput(info.sheet);
+            console.log(SpreadJsObj.getSelectObject(info.sheet));
         },
         addPegs: function (pegs) {
             if (!pegs || pegs.length <= 0) return;
@@ -1685,6 +1735,7 @@ $(document).ready(() => {
                 if (pos.index !== curRow) {
                     sheet.setSelection(pos.index, sel ? sel.col : 0, 1, 1);
                     sheet.showRow(pos.index, spreadNS.VerticalPosition.center);
+                    SpreadJsObj.reloadRowsBackColor(sheet, [pos.index, curRow]);
                 }
             }
         };
@@ -1700,6 +1751,7 @@ $(document).ready(() => {
                 if (next.index !== curRow) {
                     sheet.setSelection(next.index, sel ? sel.col : 0, 1, 1);
                     sheet.showRow(next.index, spreadNS.VerticalPosition.center);
+                    SpreadJsObj.reloadRowsBackColor(sheet, [next.index, curRow]);
                 }
             }
         };
@@ -1715,6 +1767,7 @@ $(document).ready(() => {
                 if (next.index !== curRow) {
                     sheet.setSelection(next.index, sel ? sel.col : 0, 1, 1);
                     sheet.showRow(next.index, spreadNS.VerticalPosition.center);
+                    SpreadJsObj.reloadRowsBackColor(sheet, [next.index, curRow]);
                 }
             }
         };
@@ -1829,6 +1882,7 @@ $(document).ready(() => {
                 headRows: 1,
                 emptyRows: 0,
                 headRowHeight: [32],
+                headColWidth: [30],
                 defaultRowHeight: 21,
                 headerFont: '12px 微软雅黑',
                 font: '12px 微软雅黑',
@@ -2022,7 +2076,7 @@ $(document).ready(() => {
             this._initModifyDetail();
             this._initLocateRela();
             // 草图相关
-            // this._initImageRela();
+            this._initImageRela();
             this.reBuildImData();
         }
         _initImTypeSetRela() {
@@ -2334,7 +2388,6 @@ $(document).ready(() => {
         }
         _initModifyDetail() {
             const self = this;
-            let updateImageData;
             // 编辑
             $('#edit-detail').click(function () {
                 $(this).hide();
@@ -2342,7 +2395,7 @@ $(document).ready(() => {
                 $('#cancel-detail').show();
                 $('#detail-show').hide();
                 $('#detail-edit').show();
-                makeImageItem();
+                self.updateImageData = null;
             });
             // 保存
             $('#save-detail').click(() => {
@@ -2381,21 +2434,20 @@ $(document).ready(() => {
                 check('drawing_code', $('#drawing-code'), data, updateData);
                 check('calc_memo', $('#calc-memo'), data, updateData);
                 updateData.custom_define = updateData.custom_define.join(',');
-                // updateData.bw = $('#bw-name').val();
-                // updateData.peg = $('#peg').val();
-                // updateData.xm = $('#xm-name').val();
-                // updateData.position = $('#position').val();
-                // updateData.jldy = $('#jldy').val();
-                // updateData.drawing_code = $('#drawing-code').val();
-                // updateData.calc_memo = $('#calc-memo').val();
                 postData(window.location.pathname + '/detail/save', updateData, function (result) {
                     stageIm.loadUpdateDetailData(result);
                     self.reLoadDetailData();
+                    if (self.updateImageData && !self.updateImageData.uuid) {
+                        self.updateImageData.uuid = result.uuid;
+                        postData(window.location.pathname + '/detail/merge-img', self.updateImageData, function (result) {
+                            stageIm.loadUpdateDetailData(result);
+                            self.reLoadDetailData();
+                        });
+                    }
                 });
-                if (updateImageData) {
-                    postData(window.location.pathname + '/detail/merge-img', updateImageData, function (result) {
+                if (self.updateImageData && self.updateImageData.uuid) {
+                    postData(window.location.pathname + '/detail/merge-img', self.updateImageData, function (result) {
                         stageIm.loadUpdateDetailData(result);
-                        updateImageData = '';
                         self.reLoadDetailData();
                     });
                 }
@@ -2404,9 +2456,12 @@ $(document).ready(() => {
             $('#cancel-detail').click(() => {
                 self.reLoadDetailData();
             });
-        // }
-        // _initImageRela() {
-        //     const self = this;
+        }
+        _initImageRela() {
+            const self = this;
+            $('#edit-detail').click(function () {
+                loadImageItem();
+            });
             function setdraggrable(){
                 $( ".img-item" ).draggable({ containment: "parent" },{stop: function( event, ui ) {
                     }}).resizable({ containment: "parent" },{ handles: 'n, e, s, w, ne, se, sw, nw' },{ maxWidth: parseFloat($('#imgwidth').val())},{maxHeight: parseFloat($('#imgheight').val())},{
@@ -2452,24 +2507,8 @@ $(document).ready(() => {
                 $(this).parent().remove();
             };
             // 加载草图组成
-            // $('#edit-img').on('show.bs.modal', function () {
-            //     const data = SpreadJsObj.getSelectObject(self.spread.getActiveSheet());
-            //     const items = data.calc_img_org ? JSON.parse(data.calc_img_org) : [];
-            //     const html = [];
-            //     for (const item of items) {
-            //         const itemStyle = 'top:' + item.top + ';' + 'left:' + item.left + ';' + 'width:' + item.width + ';' + 'height:' + item.height + ';';
-            //         html.push('<div class="img-item" style="' + itemStyle + '">');
-            //         html.push('<div class="img-bar">');
-            //         html.push('<a href="javascript: void(0);" class="text-danger" title="删除"><i class="fa fa-remove" style="font-size: 24px"></i></a>');
-            //         html.push('</div>');
-            //         html.push('<div class="focus" style="width:100%; height:100%"><img src="', item.src, '" id="draggable" style="width:100%; height:100%"></div>');
-            //         html.push('</div>');
-            //     }
-            //     $('.img-view').html(html.join(''));
-            //     $('.img-bar').click(removeImageItem);
-            //     setdraggrable();
-            // });
-            function makeImageItem () {
+            const loadImageItem = function () {
+                self.updateImageData = null;
                 const data = SpreadJsObj.getSelectObject(self.spread.getActiveSheet());
                 const items = data.calc_img_org ? JSON.parse(data.calc_img_org) : [];
                 const html = [];
@@ -2521,6 +2560,7 @@ $(document).ready(() => {
             $('#edit-img-ok').click(function () {
                 // 记录上传的图片的信息
                 const items = $('.img-item');
+                const img_remark = $('#text-edit').val()
                 const data = SpreadJsObj.getSelectObject(self.spread.getActiveSheet());
                 if (items.length > 0) {
                     const itemInfo = [];
@@ -2551,13 +2591,6 @@ $(document).ready(() => {
                     const updateData = {updateType: 'update', lid: data.lid, pid: data.pid};
                     if (data.uuid) {
                         updateData.uuid = data.uuid;
-                    } else {
-                        updateData.code = data.code;
-                        updateData.name = data.name;
-                        updateData.unit = data.unit;
-                        updateData.unit_price = data.unit_price;
-                        updateData.pid = data.pid;
-                        updateData.pos_name = data.pos_name;
                     }
                     if (data.custom_define.indexOf('calc_img') === -1) {
                         updateData.custom_define = data.custom_define;
@@ -2566,11 +2599,13 @@ $(document).ready(() => {
                     }
                     updateData.img = canvas.toDataURL('image/png');
                     updateData.imgInfo = itemInfo;
-
-                    updateImageData = updateData;
+                    updateData.calc_img_remark = img_remark;
+                    self.updateImageData = updateData;
+                    console.log(updateData)
                     $('#calc-img').attr('src', updateData.img);
                     $('#view-calc-img').attr('src', updateData.img);
                     $('#show-calc-img').attr('src', updateData.img);
+                    $('#view-calc-remark').text(img_remark);
                     $('#edit-img').modal('hide');
                     // updateImageData = updateData;
                     // $('#edit-img').modal('hide');
@@ -2585,7 +2620,7 @@ $(document).ready(() => {
                     //     $('#edit-img').modal('hide');
                     // });
                 } else if (data.calc_img) {
-                    updateImageData = {updateType: 'clear', lid: data.lid, pid: data.pid, uuid: data.uuid};
+                    self.updateImageData = {updateType: 'clear', lid: data.lid, pid: data.pid, uuid: data.uuid};
                     $('#show-calc-img').attr('src', '');
                     $('#calc-img').attr('src', '');
                     $('#view-calc-img').attr('src', '');
@@ -2601,6 +2636,7 @@ $(document).ready(() => {
                     //     $('#edit-img').modal('hide');
                     // });
                 } else {
+                    self.updateImageData = null;
                     $('#show-calc-img').attr('src', '');
                     $('#calc-img').attr('src', '');
                     $('#view-calc-img').attr('src', '');
@@ -2640,7 +2676,6 @@ $(document).ready(() => {
         }
         reLoadDetailData() {
             const data = SpreadJsObj.getSelectObject(this.spread.getActiveSheet());
-
             if (data) {
                 $('#edit-detail').show();
                 $('#modify-img').show();
@@ -2702,6 +2737,12 @@ $(document).ready(() => {
             $('#show-calc-img').attr('src', calcImgSrc);
             $('#calc-img').attr('src', calcImgSrc);
             $('#view-calc-img').attr('src', calcImgSrc);
+
+            const calcImgRemark = data && data.calc_img_remark || '';
+            $('#view-calc-remark').text(calcImgRemark);
+            $("#view-calc-remark").attr('readonly', true);
+            // 处理 编辑 -> 添加草图中textarea多余的空格
+            $('#text-edit').val(calcImgRemark)
         }
     }
     class CheckedChange {
@@ -2717,6 +2758,7 @@ $(document).ready(() => {
                 emptyRows: 0,
                 headRows: 1,
                 headRowHeight: [32],
+                headColWidth: [30],
                 defaultRowHeight: 21,
                 headerFont: '12px 微软雅黑',
                 font: '12px 微软雅黑',
@@ -2841,6 +2883,7 @@ $(document).ready(() => {
                         emptyRows: 0,
                         headRows: 1,
                         headRowHeight: [32],
+                        headColWidth: [30],
                         defaultRowHeight: 21,
                         headerFont: '12px 微软雅黑',
                         font: '12px 微软雅黑',
@@ -2849,33 +2892,55 @@ $(document).ready(() => {
                     afterLocated: function () {
                         stagePosSpreadObj.loadCurPosData();
                     },
-                    checkLess: function (node) {
-                        if (node.quantity) {
-                            return ZhCalc.sub(ZhCalc.add(node.quantity, node.end_qc_qty), node.end_gather_qty) > 0;
-                        } else if (node.total_price) {
-                            return ZhCalc.sub(ZhCalc.add(node.total_price, node.end_qc_tp), node.end_gather_tp) > 0;
-                        }
-                    },
-                    checkOver: function (node) {
-                        const posRange = stagePos.ledgerPos[itemsPre + node.id] || [];
-                        if (posRange.length > 0) {
-                            for (const p of posRange) {
-                                if (p.end_contract_qty > p.quantity) return true;
+                    customSearch: [
+                        {
+                            key: 'less', title: '漏计', valid: true,
+                            check: function (node) {
+                                if (node.quantity) {
+                                    return ZhCalc.sub(ZhCalc.add(node.quantity, node.end_qc_qty), node.end_gather_qty) > 0;
+                                } else if (node.total_price) {
+                                    return ZhCalc.sub(ZhCalc.add(node.total_price, node.end_qc_tp), node.end_gather_tp) > 0;
+                                }
+                            }
+                        }, {
+                            key: 'over', title: '超计', valid: true,
+                            check: function (node) {
+                                const posRange = stagePos.ledgerPos[itemsPre + node.id] || [];
+                                if (posRange.length > 0) {
+                                    for (const p of posRange) {
+                                        if (p.end_contract_qty > p.quantity) return true;
+                                    }
+                                    return false;
+                                } else if (node.end_gather_qty) {
+                                    return !node.quantity || Math.abs(node.end_gather_qty) > Math.abs(ZhCalc.add(node.quantity, node.end_qc_qty));
+                                } else if (node.end_gather_tp) {
+                                    return !node.total_price || Math.abs(node.end_gather_tp) > Math.abs(ZhCalc.add(node.total_price, node.end_qc_tp));
+                                }
+                            }
+                        }, {
+                            key: 'empty', title: '漏计', valid: false,
+                            check: function (node) {
+                                if (node.quantity) {
+                                    return !node.end_gather_qty || checkZero(node.end_gather_qty);
+                                } else if (node.total_price) {
+                                    return !node.end_gather_tp || checkZero(node.end_gather_tp);
+                                }
+                            }
+                        }, {
+                            key: 'estimate', title: '预计变更', valid: true,
+                            check: function (node) {
+                                const posRange = stagePos.ledgerPos[itemsPre + node.id] || [];
+                                if (posRange.length > 0) {
+                                    for (const p of posRange) {
+                                        if (!checkZero(p.estimate_qty)) return true;
+                                    }
+                                    return false;
+                                } else {
+                                    return false;
+                                }
                             }
-                            return false;
-                        } else if (node.end_gather_qty) {
-                            return !node.quantity || Math.abs(node.end_gather_qty) > Math.abs(ZhCalc.add(node.quantity, node.end_qc_qty));
-                        } else if (node.end_gather_tp) {
-                            return !node.total_price || Math.abs(node.end_gather_tp) > Math.abs(ZhCalc.add(node.total_price, node.end_qc_tp));
-                        }
-                    },
-                    checkEmpty: function (node) {
-                        if (node.quantity) {
-                            return !node.end_gather_qty || checkZero(node.end_gather_qty);
-                        } else if (node.total_price) {
-                            return !node.end_gather_tp || checkZero(node.end_gather_tp);
                         }
-                    },
+                    ],
                 });
                 searchLedger.spread.refresh();
             }
@@ -2982,22 +3047,30 @@ $(document).ready(() => {
             $('#show-att tr').eq(0).children('td').text(att.filename + att.fileext);
             const name = att.code !== null && att.code !== '' ? att.code : (att.b_code !== null ? att.b_code : '');
             $('#show-att tr').eq(1).children('td').text($.trim(name + ' ' + att.lname));
-            $('#show-att tr').eq(2).find('a').attr('href', '/tender/' + tender.id + '/measure/stage/' + stage.order + '/download/file/' + att.id);
+            // $('#show-att tr').eq(2).find('a').attr('href', '/tender/' + tender.id + '/measure/stage/' + stage.order + '/download/file/' + att.id);
+            $('#show-att tr').eq(2).find('a').attr('href', att.filepath);
             $('#show-att tr').eq(3).children('td').eq(0).text(att.username);
             $('#show-att tr').eq(3).children('td').eq(1).text(att.in_time);
             $('#show-att tr').eq(4).children('td').text(att.remark);
             if (parseInt(userID) === att.uid) {
                 $('#btn-att').show();
-                $('#btn-att a').eq(1).show();
-                $('#btn-att a').eq(0).hide();
+                if (stage.status === auditConst.status.checked) {
+                    if (parseInt(att.re_upload) === 1) {
+                        $('#btn-att a').eq(3).show();
+                    } else {
+                        $('#btn-att a').eq(3).hide();
+                    }
+                }
+                // $('#btn-att a').eq(3).show();
                 $('#btn-att a').eq(2).hide();
-                $('#btn-att a').eq(3).hide();
+                $('#btn-att a').eq(4).hide();
+                $('#btn-att a').eq(5).hide();
             } else {
                 $('#btn-att').hide();
-                $('#btn-att a').eq(1).hide();
-                $('#btn-att a').eq(0).hide();
-                $('#btn-att a').eq(2).hide();
                 $('#btn-att a').eq(3).hide();
+                $('#btn-att a').eq(2).hide();
+                $('#btn-att a').eq(4).hide();
+                $('#btn-att a').eq(5).hide();
             }
             $('#showAttachment').attr('file-id', fid);
             $('#showAttachment').show();
@@ -3012,10 +3085,10 @@ $(document).ready(() => {
         const fid = $('#showAttachment').attr('file-id');
         const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
         if (content === 'edit') {
-            $('#btn-att a').eq(1).hide();
-            $('#btn-att a').eq(0).show();
+            $('#btn-att a').eq(3).hide();
             $('#btn-att a').eq(2).show();
-            $('#btn-att a').eq(3).show();
+            $('#btn-att a').eq(4).show();
+            $('#btn-att a').eq(5).show();
             $('#show-att').hide();
             $('#edit-att').show();
             const att = attData.find(function (item) {
@@ -3030,10 +3103,10 @@ $(document).ready(() => {
         } else if (content === 'cancel') {
             $('#show-att').show();
             $('#edit-att').hide();
-            $('#btn-att a').eq(1).show();
-            $('#btn-att a').eq(0).hide();
+            $('#btn-att a').eq(3).show();
             $('#btn-att a').eq(2).hide();
-            $('#btn-att a').eq(3).hide();
+            $('#btn-att a').eq(4).hide();
+            $('#btn-att a').eq(5).hide();
         } else if (content === 'save') {
             const formData = new FormData();
             formData.append('id', fid);
@@ -3064,10 +3137,10 @@ $(document).ready(() => {
                 $('#show-att tr').eq(3).children('td').eq(0).text(data.username);
                 $('#show-att tr').eq(3).children('td').eq(1).text(data.in_time);
                 $('#show-att tr').eq(4).children('td').text(data.remark);
-                $('#btn-att a').eq(1).show();
-                $('#btn-att a').eq(0).hide();
+                $('#btn-att a').eq(3).show();
                 $('#btn-att a').eq(2).hide();
-                $('#btn-att a').eq(3).hide();
+                $('#btn-att a').eq(4).hide();
+                $('#btn-att a').eq(5).hide();
             }, function () {
                 toastr.error('附件上传失败');
             });
@@ -3086,6 +3159,19 @@ $(document).ready(() => {
                 $('#showAttachment').hide();
                 $('#showAttachment').attr('file-id', '');
             });
+        } else if (content === 'view') {
+            const data = {id: fid};
+            postData('/tender/' + tender.id + '/measure/stage/' + stage.order + '/check/file', data, function (result) {
+              const { filepath } = result
+              $('#show-att tr').eq(2).find('a').attr('href', filepath)
+              $('#show-att tr').eq(2).find('a').children('span').eq(0).trigger('click')
+            });
+        } else if (content === 'location') {
+            const att = attData.find(item => item.id === parseInt(fid));
+            if (Object.keys(att).length) {
+                SpreadJsObj.locateTreeNode(slSpread.getActiveSheet(), att.ledger_id, true);
+                stagePosSpreadObj.loadCurPosData();
+            }
         }
     });
 

+ 4 - 0
app/public/js/stage_bwtz.js

@@ -17,15 +17,18 @@ $(document).ready(() => {
     const xmjSpread = SpreadJsObj.createNewSpread($('#xmj-spread')[0]);
     const xmjSheet = xmjSpread.getActiveSheet();
     sjsSettingObj.setFxTreeStyle(xmjSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(xmjSpreadSetting);
     SpreadJsObj.initSheet(xmjSheet, xmjSpreadSetting);
 
     const unitSpread = SpreadJsObj.createNewSpread($('#unit-spread')[0]);
     const unitSheet = unitSpread.getActiveSheet();
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(xmjSpreadSetting);
     SpreadJsObj.initSheet(unitSheet, unitSpreadSetting);
 
     const unitTreeObj = {
         loadCurUnitData: function () {
             const node = SpreadJsObj.getSelectObject(xmjSheet);
+            SpreadJsObj.resetTopAndSelect(unitSheet);
             if (node && node.unitTree) {
                 SpreadJsObj.loadSheetData(unitSheet, SpreadJsObj.DataType.Tree, node.unitTree);
             } else {
@@ -151,6 +154,7 @@ $(document).ready(() => {
                             emptyRows: 0,
                             headRows: 1,
                             headRowHeight: [32],
+                            headColWidth: [30],
                             defaultRowHeight: 21,
                             headerFont: '12px 微软雅黑',
                             font: '12px 微软雅黑',

+ 3 - 0
app/public/js/stage_change.js

@@ -80,6 +80,7 @@ $(document).ready(() => {
         emptyRows: 0,
         headRows: 1,
         headRowHeight: [32],
+        headColWidth: [30],
         defaultRowHeight: 21,
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
@@ -113,6 +114,7 @@ $(document).ready(() => {
         emptyRows: 0,
         headRows: 1,
         headRowHeight: [32],
+        headColWidth: [30],
         defaultRowHeight: 21,
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
@@ -135,6 +137,7 @@ $(document).ready(() => {
         emptyRows: 0,
         headRows: 2,
         headRowHeight: [25, 25],
+        headColWidth: [30],
         defaultRowHeight: 21,
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',

+ 4 - 0
app/public/js/stage_compare.js

@@ -86,9 +86,12 @@ $(document).ready(function () {
     const ledgerSpread = SpreadJsObj.createNewSpread($('#ledger-spread')[0]);
     const ledgerSheet = ledgerSpread.getActiveSheet();
     sjsSettingObj.setFxTreeStyle(ledgerSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(ledgerSpreadSetting);
     SpreadJsObj.initSheet(ledgerSpread.getActiveSheet(), ledgerSpreadSetting);
     // 初始化部位
     const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
+    sjsSettingObj.setGridSelectStyle(posSpreadSetting);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(posSpreadSetting);
     SpreadJsObj.initSheet(posSpread.getActiveSheet(), posSpreadSetting);
 
     $.subMenu({
@@ -166,6 +169,7 @@ $(document).ready(function () {
     // 获取部位明细数据
     function loadPosData(iRow) {
         const node = ledgerSpread.getActiveSheet().zh_tree.nodes[iRow];
+        SpreadJsObj.resetTopAndSelect(posSpread.getActiveSheet());
         if (node) {
             SpreadJsObj.loadSheetData(posSpread.getActiveSheet(), SpreadJsObj.DataType.Data, scPos.getLedgerPos(node.id) || []);
         } else {

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

@@ -59,15 +59,18 @@ $(document).ready(function () {
             ? (data.overRange ? '#f8d7da' : data.differ ? '#FFE699' : defaultColor)
             : defaultColor;
     };
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(gclSpreadSetting);
     SpreadJsObj.initSheet(gclSpread.getActiveSheet(), gclSpreadSetting);
     // 初始化所属项目节
     const leafXmjSpread = SpreadJsObj.createNewSpread($('#leaf-xmj-spread')[0]);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(leafXmjSpreadSetting);
     SpreadJsObj.initSheet(leafXmjSpread.getActiveSheet(), leafXmjSpreadSetting);
 
     let gclGatherData;
     // 获取项目节数据
     function loadLeafXmjData(iGclRow) {
         const gcl = gclGatherData[iGclRow];
+        SpreadJsObj.resetTopAndSelect(leafXmjSpread.getActiveSheet());
         if (gcl) {
             SpreadJsObj.loadSheetData(leafXmjSpread.getActiveSheet(), SpreadJsObj.DataType.Data, gcl.leafXmjs);
         } else {

+ 111 - 11
app/public/js/stage_im.js

@@ -9,7 +9,7 @@
  */
 
 const stageIm = (function () {
-    const imFields = ['uuid', 'doc_code', 'peg', 'bw', 'xm', 'drawing_code', 'calc_memo', 'calc_img', 'calc_img_org', 'position', 'jldy'];
+    const imFields = ['uuid', 'doc_code', 'peg', 'bw', 'xm', 'drawing_code', 'calc_memo', 'calc_img', 'calc_img_org','calc_img_remark', 'position', 'jldy'];
     const splitChar = '-';
     const mergeChar = ';';
     let stage, imType, decimal, details, changes, ImData, pre;
@@ -134,7 +134,6 @@ const stageIm = (function () {
                 return l3Node.id === l4Node.id ? l3Node.name : l3Node.name + ';' + l4Node.name;
             }
         }
-        return node.name;
     }
 
     function CheckPeg(text) {
@@ -344,7 +343,7 @@ const stageIm = (function () {
         const posRange = gsPos.getLedgerPos(node.id);
         if (!posRange) { return }
         for (const p of posRange) {
-            if (!p.gather_qty || checkZero(p.gather_qty)) { continue; }
+            if (checkZero(p.contract_qty) && checkZero(p.qc_qty)) { continue; }
             let lp = _.find(gclBills.pos, {name: p.name});
             if (!lp) {
                 lp = {name: p.name};
@@ -363,7 +362,8 @@ const stageIm = (function () {
                 if ((!p.b_code || p.b_code === '') || (p.children && p.children.length > 0)) {
                     continue;
                 }
-                if ((!p.contract_tp || p.contract_tp === 0) && (!p.qc_tp || p.qc_tp === 0)) {
+                if (checkZero(p.contract_qty) && checkZero(p.contract_tp) &&
+                    checkZero(p.qc_qty) && checkZero(p.qc_tp)) {
                     continue;
                 }
                 let b = _.find(im.gclBills, {bid: p.id});
@@ -417,12 +417,62 @@ const stageIm = (function () {
             }
         }
     }
+
+    function checkUsed(node) {
+        if (node.children && node.children.length > 0) {
+            const posterity = gsTree.getPosterity(node);
+            for (const p of posterity) {
+                if (p.children && p.children.length > 0) continue;
+                if (!checkZero(p.contract_qty) || !checkZero(p.contract_tp) ||
+                    !checkZero(p.qc_qty) || !checkZero(p.qc_tp))
+                    return true;
+                const pPos = gsPos.getLedgerPos(p.id);
+                if (!pPos || pPos.length === 0) continue;
+                for (const pp of pPos) {
+                    if (!checkZero(pp.contract_qty) || !checkZero(pp.qc_qty)) return true;
+                }
+            }
+            return false;
+        } else {
+            if (!checkZero(node.contract_qty) || !checkZero(node.contract_tp) ||
+                !checkZero(node.qc_qty) || !checkZero(node.qc_tp))
+                return true;
+            const pPos = gsPos.getLedgerPos(node.id);
+            if (!pPos || pPos.length === 0) return false;
+            for (const pp of pPos) {
+                if (!checkZero(pp.contract_qty) || !checkZero(pp.qc_qty)) return true;
+            }
+        }
+    }
+
+    function getFirstUsedPos(node) {
+        const pPos = gsPos.getLedgerPos(node.id);
+        if (!pPos || pPos.length === 0) return null;
+        for (const pp of pPos) {
+            if (!checkZero(pp.contract_qty) || !checkZero(pp.qc_qty)) return pp;
+        }
+    }
+
+    function getFirstUsed(node) {
+        if (node.children && node.children.length > 0) {
+            const posterity = gsTree.getPosterity(node);
+            for (const p of posterity) {
+                if (p.children && p.children.length > 0) continue;
+                if (checkUsed(p)) return [p, getFirstUsedPos(p)]
+            }
+        } else if (checkUsed(node)) {
+            return [node, getFirstUsedPos(node)];
+        } else {
+            return [null, null]
+        }
+    }
+
     /**
      * 生成 0号台账 中间计量数据
      * @param {Object} node - 生成中间计量表的节点
      */
     function generateTzImData (node) {
-        if (node.gather_tp) {
+        if (checkUsed(node)) {
             const nodeIndex = gsTree.getNodeIndex(node);
             const peg = getPegNode(node);
             const im = {
@@ -477,7 +527,7 @@ const stageIm = (function () {
         im.jl = ZhCalc.add(im.contract_jl, im.qc_jl);
     }
     function generateBwBillsImData (node) {
-        if (node.gather_tp) {
+        if (checkUsed(node)) {
             const nodeIndex = gsTree.getNodeIndex(node);
             const peg = getPegNode(node);
             const nodeImData = [], posterity = gsTree.getPosterity(node);
@@ -497,11 +547,11 @@ const stageIm = (function () {
             for (const p of posterity) {
                 if (p.children && p.children.length > 0) continue;
                 if (!p.b_code || p.b_code === '') continue;
-                if (!p.gather_tp || p.gather_tp === 0) continue;
+                if (!checkUsed(p)) continue;
                 const pPos = gsPos.getLedgerPos(p.id);
                 if (pPos && pPos.length > 0) {
                     for (const pp of pPos) {
-                        if (!pp.gather_qty && !pp.contract_qty && !pp.qc_qty) continue;
+                        if (checkZero(pp.contract_qty) && checkZero(pp.qc_qty)) continue;
                         let im = nodeImData.find(function (d) {
                             return d.lid === node.id && d.pos_name === pp.name;
                         });
@@ -546,6 +596,7 @@ const stageIm = (function () {
 
                     imDefault.contract_jl = ZhCalc.add(imDefault.contract_jl, p.contract_tp);
                     imDefault.qc_jl = ZhCalc.add(imDefault.qc_jl, p.qc_tp);
+                    imDefault.used = true;
 
                     addBwBillsGclBills(imDefault, {
                         b_code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price,
@@ -553,7 +604,7 @@ const stageIm = (function () {
                     });
                 }
             }
-            if (imDefault.contract_jl || imDefault.qc_jl) {
+            if (imDefault.used) {
                 imDefault.jl = ZhCalc.add(imDefault.contract_jl, imDefault.qc_jl);
                 ImData.push(imDefault);
             }
@@ -643,7 +694,7 @@ const stageIm = (function () {
         for (const p of posterity) {
             if (p.children && p.children.length > 0 ) { continue; }
             if (!p.b_code || p.b_code === '') { continue }
-            if (!p.gather_tp || p.gather_tp === 0) { continue; }
+            if (!checkUsed(p)) { continue; }
             let im = nodeImData.find(function (d) {
                 return d.lid === node.id &&
                     d.code === p.b_code && p.name === d.name && p.unit === d.unit && checkZero(ZhCalc.sub(p.unit_price, d.unit_price));
@@ -694,7 +745,7 @@ const stageIm = (function () {
             if (pPos && pPos.length > 0) {
                 const nodeIndex = gsTree.getNodeIndex(node);
                 for (const pp of pPos) {
-                    if ((!pp.contract_qty || pp.contract_qty === 0) && (!pp.qc_qty || pp.qc_qty === 0)) { continue }
+                    if (checkZero(pp.contract_qty) && checkZero(pp.qc_qty)) { continue }
                     const im = {
                         lid: node.id, code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price, pid: pp.id,
                         jl: pp.gather_qty, contract_jl: pp.contract_qty, qc_jl: pp.qc_qty,
@@ -832,7 +883,10 @@ const stageIm = (function () {
             if (imData) {
                 _.assignInWith(imData, d, function (oV, sV, key) {
                     return imFields.indexOf(key) > -1 && !_.isUndefined(sV) && !_.isNull(sV) ? sV : oV;
+
                 });
+                imData.calc_img = d.calc_img;
+                imData.calc_img_org = d.calc_img_org;
             }
         }
     }
@@ -875,6 +929,49 @@ const stageIm = (function () {
         return buildImData();
     }
 
+    function getParentCheckNode(node) {
+        let parent = node;
+        while (parent) {
+            if (parent.check) {
+                return parent;
+            } else {
+                parent = this.getParent(parent);
+            }
+        }
+        return null;
+    }
+    function getRelaXmj(node) {
+        if (checkUsed(node)) {
+            if (stage.im_gather) {
+                const checkedParent = getParentCheckNode(node);
+                return checkedParent ? checkedParent : gsTree.getLeafXmjParent(node);
+            } else {
+                return gsTree.getLeafXmjParent(node);
+            }
+        } else {
+            return null;
+        }
+    }
+    function getRelaImData(relaXmj, bills, pos) {
+        if (stage.im_type === imType.tz.value) {
+            return _.find(ImData, {lid: relaXmj.id});
+        } else if (stage.im_type === imType.zl.value) {
+            return _.find(ImData, {lid: relaXmj.id, code: bills.b_code, name: bills.name, unit: bills.unit, unit_price: bills.unit_price});
+        } else if (stage.im_type === imType.bw.value) {
+            if (pos) {
+                return _.find(ImData, {lid: relaXmj.id, code: bills.b_code, name: bills.name, unit: bills.unit, unit_price: bills.unit_price, pid: pos.id});
+            } else {
+                return _.find(ImData, {lid: relaXmj.id, code: bills.b_code, name: bills.name, unit: bills.unit, unit_price: bills.unit_price, pid: ''});
+            }
+        } else if (stage.im_type === imType.bb.value) {
+            if (pos) {
+                return _.find(ImData, {lid: relaXmj.id, pos_name: pos.name});
+            } else {
+                return _.find(ImData, {lid: relaXmj.id, pos_name: ''});
+            }
+        }
+    }
+
     return {
         init,
         initCheck,
@@ -890,5 +987,8 @@ const stageIm = (function () {
         getImData: function () {
             return ImData;
         },
+        getFirstUsed: getFirstUsed,
+        getRelaXmj: getRelaXmj,
+        getRelaImData: getRelaImData,
     }
 })();

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

@@ -144,6 +144,7 @@ $(document).ready(() => {
         emptyRows: 0,
         headRows: 1,
         headRowHeight: [32],
+        headColWidth: [30],
         defaultRowHeight: 21,
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
@@ -279,6 +280,7 @@ $(document).ready(() => {
         },
     };
     SpreadJsObj.initSpreadSettingEvents(paySpreadSetting, payCol);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(paySpreadSetting, ['sprice', 'rprice']);
     SpreadJsObj.initSheet(paySpread.getActiveSheet(), paySpreadSetting);
     SpreadJsObj.loadSheetData(paySpread.getActiveSheet(), SpreadJsObj.DataType.Data, dealPay);
 

+ 11 - 3
app/public/js/tender_list_progress.js

@@ -308,7 +308,7 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
     const html = [];
     html.push('<tr pid="' + pid + '">');
     // 名称
-    html.push('<td width="40%" class="in-' + node.level + '">');
+    html.push('<td width="30%" class="in-' + node.level + '">');
     if (node.cid) {
         html.push('<span onselectstart="return false" style="{-moz-user-select:none}" class="fold-switch mr-1" title="收起" cid="'+ node.sort_id +'"><i class="fa fa-minus-square-o"></i></span> <i class="fa fa-folder-o"></i> ', node.name);
     } else {
@@ -325,6 +325,13 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
         html.push(node.lastStage ? '第' + node.lastStage.order + '期' : '台账');
     }
     html.push('</td>');
+
+    // 审批状态
+    html.push('<td style="width: 10%">');
+    html.push(node.lastStage ? auditConst.stage.statusString[node.lastStage.status] : auditConst.ledger.statusString[node.ledger_status]);
+    html.push(node.status_users ? '(' + node.status_users + ')' : '');
+    html.push('</td>');
+
     // 累计合同计量
     html.push('<td width="15%" class="text-right">');
     const sum = node.lastStage ? ZhCalc.add(node.total_price, node.lastStage.end_qc_tp) : node.total_price;
@@ -352,10 +359,11 @@ function getTenderTreeHtml () {
         const html = [];
         html.push('<table class="table table-hover table-bordered">');
         html.push('<thead style="position: fixed;left:56px;top: 34px;">', '<tr>');
-        html.push('<th style="width: 35%" class="text-center">', '标段名称', '</th>');
+        html.push('<th style="width: 30%" class="text-center">', '标段名称', '</th>');
         html.push('<th style="width: 10%" class="text-center">', '计量期数', '</th>');
+        html.push('<th style="width: 10%" class="text-center">','审批状态','</th>');
         html.push('<th style="width: 10%" class="text-center">', '总价 <i class="fa fa-question-circle text-primary"  data-placement="bottom" data-toggle="tooltip" data-original-title="0号台账+截止本期数量变更"></i>', '</th>');
-        html.push('<th style="width: 45%" class="text-center">', '截止上期完成/本期完成/未完成', '</th>');
+        html.push('<th style="width: 40%" class="text-center">', '截止上期完成/本期完成/未完成', '</th>');
         html.push('</tr>', '</thead>');
         parentId = 0;
         for (const t of tenderTree) {

+ 17 - 0
app/router.js

@@ -59,6 +59,10 @@ module.exports = app => {
     app.post('/setting/user/exist', sessionAuth, 'settingController.accountExist');
     app.post('/setting/user/unbind', sessionAuth, 'settingController.userUnbind');
 
+    // 显示设置
+    app.get('/setting/show', sessionAuth, 'settingController.show');
+    app.post('/setting/show/update', sessionAuth, 'settingController.showListUpdate');
+
     // 标段自定义类别
     app.get('/setting/category', sessionAuth, 'settingController.category');
     app.post('/setting/category/add', sessionAuth, 'settingController.addCategory');
@@ -180,6 +184,7 @@ module.exports = app => {
     app.get('/tender/:id/measure/stage/:order/download/file/:fid', sessionAuth, 'stageController.downloadFile');
     app.post('/tender/:id/measure/stage/:order/delete/file', sessionAuth, tenderCheck, stageCheck, 'stageController.deleteFile');
     app.post('/tender/:id/measure/stage/:order/save/file', sessionAuth, tenderCheck, stageCheck, 'stageController.saveFile');
+    app.post('/tender/:id/measure/stage/:order/check/file', sessionAuth, tenderCheck, stageCheck, 'stageController.checkFile');
     // 中间计量
     app.get('/tender/:id/measure/stage/:order/detail', sessionAuth, tenderCheck, stageCheck, 'stageController.detail');
     app.post('/tender/:id/measure/stage/:order/detail/build', sessionAuth, tenderCheck, stageCheck, 'stageController.buildDetailData');
@@ -228,6 +233,7 @@ module.exports = app => {
     app.post('/tender/:id/measure/stage/:order/extra/delete/file', sessionAuth, tenderCheck, stageCheck, 'stageExtraController.deleteFile');
     // 期审批管理
     app.get('/tender/:id/measure/stage/:order/manager', sessionAuth, tenderCheck, stageCheck, 'stageController.manager');
+    app.post('/tender/:id/measure/stage/:order/manager/audit/delete', sessionAuth, tenderCheck, stageCheck, 'stageController.managerAuditDelete');
     // 报表
     app.get('/tender/:id/report', sessionAuth, tenderCheck, 'reportController.index');
     app.get('/tender/:id/measure/stage/:order/report', sessionAuth, tenderCheck, stageCheck, 'reportController.index');
@@ -285,6 +291,12 @@ module.exports = app => {
     app.get('/tender/:id/measure/material/:order/list', sessionAuth, tenderCheck, materialCheck, 'materialController.list');
     app.post('/tender/:id/measure/material/:order/list/save', sessionAuth, tenderCheck, materialCheck, 'materialController.saveListsData');
 
+    // 附件
+    app.get('/tender/:id/measure/material/:order/file', sessionAuth, tenderCheck, materialCheck, 'materialController.file');
+    app.post('/tender/:id/measure/material/:order/file/upload', sessionAuth, tenderCheck, materialCheck, 'materialController.upload');
+    app.post('/tender/:id/measure/material/:order/file/find', sessionAuth, tenderCheck, materialCheck, 'materialController.getCurMatericalFiles');
+    app.post('/tender/measure/material/file/delete', sessionAuth, 'materialController.deleteFile');
+
     // 个人账号相关
     app.get('/profile/info', sessionAuth, 'profileController.info');
     app.get('/profile/sms', sessionAuth, 'profileController.sms');
@@ -331,4 +343,9 @@ module.exports = app => {
     app.get('/wap/tender/:id/stage/:order', sessionAuth, tenderCheck, 'wapController.stage');
     app.get('/wap/tender/:id/change/:cid/info', sessionAuth, tenderCheck, 'wapController.change');
     app.post('/wap/tender/:id/change/approval', sessionAuth, tenderCheck, 'wapController.changeApproval');
+
+    // 微信
+    app.get('/wx', 'wechatController.index');
+    app.get('/wx/oauth', 'wechatController.oauth');
+    app.get('/wx/hello', 'wechatController.hello');
 };

+ 2 - 1
app/service/deal_bills.js

@@ -158,7 +158,8 @@ module.exports = app => {
                     }
                 } else {
                     const code = this.ctx.helper.replaceReturn(this.ctx.helper._.trim(row[iCode]));
-                    if (this.ctx.helper.validBillsCode(code)) {
+                    //if (this.ctx.helper.validBillsCode(code)) {
+                    if (code) {
                         const data = {
                             deal_id: bills.length + 1,
                             tender_id: tenderId,

+ 12 - 8
app/service/material.js

@@ -27,7 +27,7 @@ module.exports = app => {
          * 获取 最新一期 材料调差期计量
          * @param tenderId
          * @param includeUnCheck
-         * @returns {Promise<*>}
+         * @return {Promise<*>}
          */
         async getLastestMaterial(tenderId, includeUnCheck = false) {
             this.initSqlBuilder();
@@ -50,7 +50,7 @@ module.exports = app => {
         /**
          * 获取 最新一期 审批完成的 材料调差期计量
          * @param tenderId
-         * @returns {Promise<*>}
+         * @return {Promise<*>}
          */
         async getLastestCompleteMaterial(tenderId) {
             this.initSqlBuilder();
@@ -71,7 +71,7 @@ module.exports = app => {
         /**
          * 获取标段下的全部计量期,按倒序
          * @param tenderId
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async getValidMaterials(tenderId) {
             const materials = await this.db.select(this.tableName, {
@@ -101,7 +101,7 @@ module.exports = app => {
          * 添加材料调差期
          * @param tenderId - 标段id
          * @param data - post的数据
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async addMaterial(tenderId, data) {
             const materials = await this.getAllDataByCondition({
@@ -169,7 +169,7 @@ module.exports = app => {
          * 删除材料调差期
          *
          * @param {Number} id - 期Id
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async deleteMaterial(id) {
             const transaction = await this.db.beginTransaction();
@@ -179,6 +179,7 @@ module.exports = app => {
                 await transaction.delete(this.ctx.service.materialList.tableName, { mid: id });
                 await transaction.delete(this.ctx.service.materialListNotjoin.tableName, { mid: id });
                 await transaction.delete(this.ctx.service.materialBillsHistory.tableName, { mid: id });
+                await transaction.delete(this.ctx.service.materialFile.tableName, { mid: id });
                 // 如果存在上一期,把上一期的quantity,pre_tp添加到bill中
                 const materialInfo = await this.getDataById(id);
                 if (materialInfo.order > 1) {
@@ -191,6 +192,9 @@ module.exports = app => {
                 }
                 await transaction.delete(this.tableName, { id });
                 await transaction.commit();
+                // 删除期第一个参数不用传,第二个为期数
+                const attList = await this.ctx.service.materialFile.getAllMaterialFiles('', id);
+                await this.ctx.helper.delFiles(attList);
                 return true;
             } catch (err) {
                 await transaction.rollback();
@@ -202,7 +206,7 @@ module.exports = app => {
          * 获取包含当前期之前的调差期id
          *
          * @param {Number} id - 期Id
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async getPreMidList(tid, order) {
             const midList = await this.getAllDataByCondition({
@@ -221,7 +225,7 @@ module.exports = app => {
         /**
          * 修改增税税率
          * @param {int} rate 税率
-         * @returns {Promise<*>}
+         * @return {Promise<*>}
          */
         async changeRate(rate) {
             const updateData = {
@@ -235,7 +239,7 @@ module.exports = app => {
          * 修改增税税率
          * @param {int} tid 标段id
          * @param {int} order 调差期数
-         * @returns {Promise<*>}
+         * @return {Promise<*>}
          */
         async getPreTpHs(tid, order) {
             const sql = 'SELECT SUM(ROUND(`m_tp`*(1+ `rate`/100),2)) AS `pre_tp_hs` FROM ?? WHERE `tid` = ? AND `order` < ?';

+ 6 - 0
app/service/material_audit.js

@@ -198,8 +198,14 @@ module.exports = app => {
 
                 // 本期一些必要数据(如应耗数量和上期调差金额)插入到material_bills_history表里
                 const materialBillsData = await this.ctx.service.materialBills.getAllDataByCondition({ where: { tid: this.ctx.tender.id } });
+                if (materialBillsData.length === 0) {
+                    throw '调差工料不能为空';
+                }
                 const mbhList = [];
                 for (const mb of materialBillsData) {
+                    if (mb.code === '') {
+                        throw '调差工料编号不能为空';
+                    }
                     const newMbh = {
                         tid: this.ctx.tender.id,
                         mid: this.ctx.material.id,

+ 84 - 0
app/service/material_file.js

@@ -0,0 +1,84 @@
+'use strict';
+const auditConst = require('../const/audit');
+/**
+ * 附件表 数据模型
+ * @author LanJianRong
+ * @date 2020/6/30
+ * @version
+ */
+
+module.exports = app => {
+    class MaterialFile extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'material_file';
+        }
+
+        /**
+         * 获取当前标段(期)所有上传的附件
+         * @param {Number} tid 标段id
+         * @param {Number?} mid 期id
+         * @return {Promise<void>} 数据库查询实例
+         */
+        async getAllMaterialFiles(tid, mid) {
+            const where = { tid };
+            if (mid) where.mid = mid;
+            const materialList = await this.db.select(this.ctx.service.material.tableName, {
+                where: { tid },
+            });
+            // 当用户点击所有期时,不能通过当前的material.status !== auditConst.status.checked来判断是否有删除权限
+            // 需要获取当前标段所有期与fileList一一对比,可删除的加上pre_delete标记true
+            const fileList = await this.db.select(this.tableName, {
+                where,
+            });
+            return fileList.map(file => {
+                const material = materialList.find(i => i.id === file.mid && i.tid === file.tid);
+                if (material) {
+                    const { status } = material;
+                    if ( status !== auditConst.stage.status.checked) {
+                        return { ...file, pre_delete: true }
+                    }
+                }
+                return file
+            })
+
+        }
+
+
+
+        /**
+         * 存储上传的文件信息至数据库
+         * @param {Array} payload 载荷
+         * @return {Promise<void>} 数据库插入执行实例
+         */
+        async saveFileMsgToDb(payload) {
+            return await this.db.insert(this.tableName, payload);
+        }
+
+        /**
+         * 获取单个文件信息
+         * @param {Number} id 文件id
+         * @return {Promise<void>} 数据库查询实例
+         */
+        async getMaterialFileById(id) {
+            return await this.getDataByCondition({ id });
+        }
+
+        /**
+         * 删除附件
+         * @param {Number} id - 附件id
+         * @return {void}
+         */
+        async delete(id) {
+            return await this.deleteById(id);
+        }
+    }
+    return MaterialFile;
+};
+

+ 1 - 1
app/service/material_list.js

@@ -253,7 +253,7 @@ module.exports = app => {
                 };
                 copyMLArray.push(newMaterialList);
             }
-            return await transaction.insert(this.tableName, copyMLArray);
+            return copyMLArray.length !== 0 ? await transaction.insert(this.tableName, copyMLArray) : true;
         }
     }
     return MaterialList;

+ 3 - 3
app/service/pos.js

@@ -26,7 +26,7 @@ module.exports = app => {
             return await this.db.select(this.tableName, {
                 where: condition,
                 columns: ['id', 'tid', 'lid', 'name', 'quantity', 'position', 'drawing_code', 'sgfh_qty', 'sjcl_qty',
-                    'qtcl_qty', 'in_time', 'porder', 'add_stage', 'sgfh_expr', 'sjcl_expr', 'qtcl_expr'],
+                    'qtcl_qty', 'in_time', 'porder', 'add_stage', 'sgfh_expr', 'sjcl_expr', 'qtcl_expr', 'real_qty'],
                 order: [['porder', 'ASC']],
             });
         }
@@ -34,7 +34,7 @@ module.exports = app => {
         async getPosDataWithAddStageOrder(condition) {
             const sql = 'SELECT p.id, p.tid, p.lid, p.name, p.quantity, p.position, p.drawing_code,' +
                 '    p.sgfh_qty, p.sjcl_qty, p.qtcl_qty, p.porder, p.add_stage, p.add_times, p.add_user, s.order As add_stage_order,' +
-                '    p.sgfh_expr, p.sjcl_expr, p.qtcl_expr' +
+                '    p.sgfh_expr, p.sjcl_expr, p.qtcl_expr, p.real_qty' +
                 '  FROM ' + this.tableName + ' p ' +
                 '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s' +
                 '  ON p.add_stage = s.id'
@@ -44,7 +44,7 @@ module.exports = app => {
 
         async getPosDataByIds(ids) {
             if (ids instanceof Array && ids.length > 0) {
-                const sql = 'SELECT id, tid, lid, name, quantity, position, drawing_code, sgfh_qty, sjcl_qty, qtcl_qty, add_stage, add_times, add_user, sgfh_expr, sjcl_expr, qtcl_expr' +
+                const sql = 'SELECT id, tid, lid, name, quantity, position, drawing_code, sgfh_qty, sjcl_qty, qtcl_qty, add_stage, add_times, add_user, sgfh_expr, sjcl_expr, qtcl_expr, real_qty' +
                     '  FROM ' + this.tableName +
                     '  WHERE id in (' + this.ctx.helper.getInArrStrSqlFilter(ids) + ')';
                 return await this.db.query(sql, []);

+ 5 - 4
app/service/report_memory.js

@@ -402,6 +402,7 @@ module.exports = app => {
                 'chapter',
                 'leaf_xmj_id',
                 'sgfh_expr', 'sjcl_expr', 'qtcl_expr', 'contract_expr',
+                'deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2',
             ]);
         }
 
@@ -895,10 +896,10 @@ module.exports = app => {
                 if (!checkStageField(stage.order)) return;
 
                 await this.ctx.service.stage.doCheckStage(stage);
-                calcFields.push('s' + stageOrder + '_contract_tp');
-                calcFields.push('s' + stageOrder + '_qc_tp');
-                calcFields.push('s' + stageOrder + '_gather_tp');
-                calcFields.push('s' + stageOrder + '_');
+                calcFields.push('s' + stage.order + '_contract_tp');
+                calcFields.push('s' + stage.order + '_qc_tp');
+                calcFields.push('s' + stage.order + '_gather_tp');
+                calcFields.push('s' + stage.order + '_');
 
                 const curStage = stage.readOnly
                     ? await this.ctx.service.stageBills.getAuditorStageData(this.ctx.tender.id, stage.id, stage.curTimes, stage.curOrder)

+ 1 - 1
app/service/rpt_gather_memory.js

@@ -258,7 +258,7 @@ module.exports = app => {
                 if (hasPre) {
                     const preStage = stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(tender, stage.order - 1) : [];
                     this.ctx.helper.assignRelaData(billsData, [
-                        {data: preStage, fields: ['contract_qty', 'qc_qty'], prefix: 'pre_', relaId: 'lid'}
+                        {data: preStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: 'pre_', relaId: 'lid'}
                     ]);
                 }
             }

+ 72 - 0
app/service/setting_show.js

@@ -0,0 +1,72 @@
+'use strict';
+
+/**
+ * Created by LanJianRong on 2020/7/6.
+
+ * 项目设置->显示设置表数据模型
+ * @author LanJianRong
+ * @date 2020/07/06
+ * @version
+ */
+
+const { listPath } = require('../const/setting');
+const BaseService = require('../base/base_service');
+module.exports = app => {
+
+    class settingShow extends BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'project';
+        }
+
+        /**
+         * 获取表的全部记录
+         * @param {Number | Null} i listPath对应下标
+         * @return {Array} 查询结果集
+         */
+        async getList(i = 0) {
+            return listPath.map((item, idx) => {
+                if (i === idx) {
+                    return { ...item, is_default: true }
+                }
+                return item;
+            });
+        }
+
+        /**
+         * 设置默认显示字段
+         * @param {Number} id 标签索引
+         * @param {Number} pid 项目id
+         * @return {Promise<void>} 查询结果集
+         */
+        async setDefaultLabel(id, pid) {
+            await this.update({ page_path: id }, { id: pid });
+            return listPath.map((item, idx) => {
+                if (id === idx) {
+                    return { ...item, is_default: true}
+                }
+                return item;
+            });
+        }
+
+        /**
+         * 返回项目默认打开的url
+         * @param {Number} pid 项目id
+         * @return {String} path 路由名
+         */
+        async getDefaultPath(pid) {
+            const record = await this.getDataByCondition({ id: pid });
+            const { page_path = 0 } = record;
+            const list = listPath.find((item, idx) => idx === page_path);
+            return list.path;
+        }
+    }
+    return settingShow;
+};

+ 2 - 2
app/service/stage.js

@@ -450,7 +450,7 @@ module.exports = app => {
                         cb.value = sum.qc_tp;
                         break;
                     case 'ybbqwc':
-                        const sumGcl = await this.ctx.service.stageBills.getSumTotalPriceGcl(stage, '^1[0-9]{2}-');
+                        const sumGcl = await this.ctx.service.stageBills.getSumTotalPriceGcl(stage, '^[^0-9]*1[0-9]{2}-');
                         cb.value = this.ctx.helper.add(sumGcl.contract_tp, sumGcl.qc_tp);
                         break;
                     default:
@@ -572,7 +572,7 @@ module.exports = app => {
                         cb.value = sum.qc_tp;
                         break;
                     case 'ybbqwc':
-                        const sumGcl = await this.ctx.service.stageBills.getSumTotalPriceGclByMaterial(stage_list, '^1[0-9]{2}-');
+                        const sumGcl = await this.ctx.service.stageBills.getSumTotalPriceGclByMaterial(stage_list, '^[^0-9]*1[0-9]{2}-');
                         cb.value = this.ctx.helper.add(sumGcl.contract_tp, sumGcl.qc_tp);
                         break;
                     default:

+ 4 - 4
app/service/stage_att.js

@@ -62,8 +62,8 @@ module.exports = app => {
          * @return {void}
          */
         async getDataByTenderIdAndStageId(tid, sid) {
-            const sql = 'SELECT att.id, att.lid, att.uid, att.filename, att.fileext, att.filesize, att.remark, att.in_time,' +
-                ' pa.name as `username`, leg.name as `lname`, leg.code as `code`, leg.b_code as `b_code`' +
+            const sql = 'SELECT att.id, att.lid, att.uid, att.filename, att.fileext, att.filesize, att.re_upload, att.remark, att.in_time,' +
+                ' pa.name as `username`, leg.name as `lname`, leg.code as `code`, leg.ledger_id as `ledger_id`, leg.b_code as `b_code`' +
                 ' FROM ?? AS att,?? AS pa,?? AS leg' +
                 ' WHERE leg.id = att.lid AND pa.id = att.uid AND att.tid = ? AND att.sid = ? ORDER BY att.id DESC';
             const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, this.ctx.service.ledger.tableName, tid, sid];
@@ -77,8 +77,8 @@ module.exports = app => {
          * @return {void}
          */
         async getDataByFid(id) {
-            const sql = 'SELECT att.id, att.lid, att.uid, att.filename, att.fileext, att.filesize, att.remark, att.in_time,' +
-                ' pa.name as `username`, leg.name as `lname`, leg.code as `code`, leg.b_code as `b_code`' +
+            const sql = 'SELECT att.id, att.lid, att.uid, att.filename, att.re_upload, att.fileext, att.filesize, att.remark, att.in_time,' +
+                ' pa.name as `username`, leg.name as `lname`, leg.code as `code`, leg.ledger_id as `ledger_id`,leg.b_code as `b_code`' +
                 ' FROM ?? AS att,?? AS pa,?? AS leg' +
                 ' WHERE leg.id = att.lid AND pa.id = att.uid AND att.id = ? ORDER BY att.in_time DESC';
             const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, this.ctx.service.ledger.tableName, id];

+ 57 - 0
app/service/stage_audit.js

@@ -983,6 +983,63 @@ module.exports = app => {
             const sqlParam = [this.tableName, this.ctx.service.stage.tableName, this.ctx.service.tender.tableName, this.ctx.service.tenderInfo.tableName, auditorId, auditConst.status.checking];
             return await this.db.query(sql, sqlParam);
         }
+
+        /**
+         * 删除本次审批流程
+         * @param {Number} stageId - 标段id
+         * @param {Number} times - 第几次审批
+         * @returns {Promise<void>}
+         */
+        async timesDelete() {
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 当前审批人2次添加至流程中
+                // 判断当前期是否是重新上报状态,决定删除times数并获取times个数
+                await transaction.delete(this.tableName, { sid: this.ctx.stage.id, times: this.ctx.stage.times });
+                const isCheckNo = this.ctx.stage.status === auditConst.status.checkNo;
+                const nowTimes = isCheckNo ? this.ctx.stage.times - 1 : this.ctx.stage.times;
+                if (isCheckNo) {
+                    await transaction.delete(this.tableName, { sid: this.ctx.stage.id, times: nowTimes });
+                }
+                // 添加上一次审批人
+                const sql = 'SELECT `tid`, `sid`, `aid`, `order` FROM ?? WHERE `sid` = ? and `times` = ? GROUP BY `aid` ORDER BY `id` ASC';
+                const sqlParam = [this.tableName, this.ctx.stage.id, nowTimes - 1];
+                const auditors = await this.db.query(sql, sqlParam);
+                let order = 1;
+                for (const a of auditors) {
+                    a.times = nowTimes;
+                    a.order = order;
+                    a.status = auditConst.status.uncheck;
+                    order++;
+                }
+
+                // 拷贝新一次审核流程列表
+                await transaction.insert(this.tableName, auditors);
+
+                // const tpData = await this.ctx.service.stageBills.getSumTotalPrice(this.ctx.stage);
+                // // 计算并合同支付最终数据
+                // const [yfPay, sfPay] = await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
+                // 同步 期信息
+                await transaction.update(this.ctx.service.stage.tableName, {
+                    id: this.ctx.stage.id, status: auditConst.status.checkNo,
+                    // contract_tp: tpData.contract_tp,
+                    // qc_tp: tpData.qc_tp,
+                    times: nowTimes,
+                    // yf_tp: yfPay.tp,
+                    // sf_tp: sfPay.tp,
+                    // tp_history: JSON.stringify(this.ctx.stage.tp_history),
+                    cache_time_r: this.ctx.stage.cache_time_l,
+                });
+                // 复制一份最新数据给原报
+                // await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times + 1, 0, transaction);
+                // await this.ctx.service.stageJgcl.updateHistory(this.ctx.stage, transaction);
+                // await this.ctx.service.stageBonus.updateHistory(this.ctx.stage, transaction);
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
     }
 
     return StageAudit;

+ 2 - 2
app/service/stage_bills.js

@@ -375,13 +375,13 @@ module.exports = app => {
                 '  FROM ' + this.tableName + ' As Bills ' +
                 '  INNER JOIN ( ' +
                 '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `flow`, `lid` From ' + this.tableName +
-                '      WHERE (`times` < ? OR (`times` = ? AND `order` <= ?))' +
+                '      WHERE (`times` < ? OR (`times` = ? AND `order` <= ?)) AND `sid` = ?' +
                 '      GROUP BY `lid`' +
                 '  ) As MaxFilter ' +
                 '  ON (Bills.times * ' + timesLen + ' + `order`) = MaxFilter.flow And Bills.lid = MaxFilter.lid ' +
                 '  INNER JOIN ' + this.ctx.service.ledger.tableName + ' As Ledger ON Bills.lid = Ledger.id' +
                 '  WHERE Bills.sid = ? And Ledger.b_code ' + operate + ' ?';
-            const sqlParam = [stage.times, stage.curTimes, stage.curAuditor ? stage.curAuditor.order : 0, stage.id, filter];
+            const sqlParam = [stage.times, stage.curTimes, stage.curOrder, stage.id, stage.id, filter];
             const result = await this.db.queryOne(sql, sqlParam);
             return result;
         }

+ 4 - 2
app/service/stage_bonus.js

@@ -164,11 +164,13 @@ module.exports = app => {
             const datas = await this.getStageData(stage.id);
             if (datas.length === 0) return;
 
-            const filter = {stimes: this.ctx.stage.curTimes, sorder: this.ctx.stage.curOrder};
             const updateDatas = [];
             for (const d of datas) {
                 const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = this.ctx.helper._.find(datas, filter);
+                const his = datas.find(function (x) {
+                    return x.stimes && x.stimes === this.ctx.stage.curTimes
+                        && x.sorder && x.sorder === this.ctx.stage.curOrder;
+                });
                 if (his) {
                     his.tp = d.tp;
                 } else {

+ 1 - 0
app/service/stage_detail.js

@@ -175,6 +175,7 @@ module.exports = app => {
                             result.push(d);
                         } else {
                             const nd = this._.assign(od, d);
+                            delete nd.id;
                             nd.times = this.ctx.stage.curTimes;
                             nd.order = this.ctx.stage.curOrder;
                             await transaction.insert(this.tableName, nd);

+ 9 - 10
app/service/stage_jgcl.js

@@ -185,11 +185,13 @@ module.exports = app => {
             const datas = await this.getStageData(stage);
             if (datas.length === 0) return;
 
-            const filter = {stimes: this.ctx.stage.curTimes, sorder: this.ctx.stage.curOrder};
             const updateDatas = [];
             for (const d of datas) {
                 const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = this.ctx.helper._.find(datas, filter);
+                const his = datas.find(function (x) {
+                    return x.stimes && x.stimes === this.ctx.stage.curTimes
+                        && x.sorder && x.sorder === this.ctx.stage.curOrder;
+                });
                 if (his) {
                     his.arrive_qty = d.arrive_qty;
                     his.arrive_tp = d.arrive_tp;
@@ -211,17 +213,14 @@ module.exports = app => {
             if (!stage || !preStage) {
                 throw '标段数据有误';
             }
-            const preDatas = await this.getStageData(preStage);
+            const preDatas = await this.getAllDataByCondition({
+                columns: ['uuid', 'name', 'unit', 'unit_price', 'source', 'bills_code', 'check_code', 'memo', 'add_uid', 'add_sid', 'order'],
+                where: { sid: preStage.id },
+            });
             if (preDatas.length > 0) {
                 for (const pd of preDatas) {
-                    delete pd.id;
                     pd.pre_used = pd.pre_used || !this.ctx.helper.checkZero(pd.arrive_qty) || !this.ctx.helper.checkZero(pd.deduct_qty);
-                    delete pd.arrive_qty;
-                    delete pd.arrive_tp;
-                    delete pd.deduct_qty;
-                    delete pd.deduct_tp;
                     pd.sid = stage.id;
-                    delete pd.shistory;
                 }
                 const result = await transaction.insert(this.tableName, preDatas);
                 return result.affectedRows === preDatas.length;
@@ -232,4 +231,4 @@ module.exports = app => {
     }
 
     return StageJgcl;
-};
+};

+ 8 - 6
app/service/stage_other.js

@@ -158,11 +158,13 @@ module.exports = app => {
             const datas = await this.getStageData(stage);
             if (datas.length === 0) return;
 
-            const filter = {stimes: this.ctx.stage.curTimes, sorder: this.ctx.stage.curOrder};
             const updateDatas = [];
             for (const d of datas) {
                 const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = this.ctx.helper._.find(datas, filter);
+                const his = datas.find(function (x) {
+                    return x.stimes && x.stimes === this.ctx.stage.curTimes
+                        && x.sorder && x.sorder === this.ctx.stage.curOrder;
+                });
                 if (his) {
                     his.tp = d.tp;
                     if (d.sid === d.add_sid) his.total_price = d.total_price;
@@ -180,15 +182,15 @@ module.exports = app => {
             if (!stage || !preStage) {
                 throw '标段数据有误';
             }
-            const preDatas = await this.getStageData(preStage);
+            const preDatas = await this.getAllDataByCondition({
+                columns: ['uuid', 'tid', 'add_uid', 'add_sid', 'add_time', 'name', 'o_type', 'total_price', 'order', 'memo', 'real_time'],
+                where: { sid: preStage.id }
+            });
             if (preDatas.length > 0) {
                 for (const pd of preDatas) {
-                    delete pd.id;
                     pd.pre_used = pd.pre_used || !this.ctx.helper.checkZero(pd.tp);
-                    delete pd.tp;
                     pd.sid = stage.id;
                     pd.sorder = stage.order;
-                    delete pd.shistory;
                 }
                 const result = await transaction.insert(this.tableName, preDatas);
                 return result.affectedRows === preDatas.length;

+ 13 - 6
app/service/stage_pos.js

@@ -150,7 +150,7 @@ module.exports = app => {
             const  result = {pos: [], ledger: []};
             const datas = data instanceof Array ? data : [data], calcStageBills = [];
             if (datas[0].sgfh_qty !== undefined || datas[0].sjcl_qty !== undefined || datas[0].qtcl_qty !== undefined
-                || datas[0].contract_qty !== undefined || datas[0].qc_qty !== undefined) {
+                || datas[0].contract_qty !== undefined || datas[0].qc_qty !== undefined || datas[0].real_qty !== undefined) {
                 bills = await this.ctx.service.ledger.getDataById(datas[0].lid);
                 precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
                 result.ledger.push(bills.id);
@@ -175,8 +175,10 @@ module.exports = app => {
                     if (d.sjcl_expr !== undefined) p.sjcl_expr = d.sjcl_expr;
                     if (d.qtcl_expr !== undefined) p.qtcl_expr = d.qtcl_expr;
                     p.quantity = this.ctx.helper.sum([p.sgfh_qty, p.sjcl_qty, p.qtcl_qty]);
-                    if (!updateBills) updateBills = {id: bills.id, sgfh_qty: bills.sgfh_qty, sjcl_qty: bills.sjcl_qty, qtcl_qty: bills.qtcl_qty};
+                    if (!updateBills)
+                        updateBills = {id: bills.id, sgfh_qty: bills.sgfh_qty, sjcl_qty: bills.sjcl_qty, qtcl_qty: bills.qtcl_qty};
                 }
+                if (d.real_qty!== undefined) p.real_qty = this.round(d.real_qty, precision.value);
                 insertPos.push(p);
                 result.pos.push(p.id);
 
@@ -258,7 +260,7 @@ module.exports = app => {
                 {pid: this._.map(datas, 'pid')});
 
             if (datas[0].sgfh_qty !== undefined || datas[0].qtcl_qty !== undefined || datas[0].sjcl_qty !== undefined
-                || datas[0].contract_qty !== undefined || datas[0].qc_qty !== undefined) {
+                || datas[0].contract_qty !== undefined || datas[0].qc_qty !== undefined || datas[0].real_qty !== undefined) {
                 bills = await this.ctx.service.ledger.getDataById(datas[0].lid);
                 precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
                 result.ledger.push(bills.id);
@@ -266,10 +268,14 @@ module.exports = app => {
             const updatePos = [], updatePosStage = [], insertPosStage = [];
             for (const d of datas) {
                 if (d.name !== undefined || d.drawing_code !== undefined || d.position !== undefined
-                    || d.sgfh_qty !== undefined || d.qtcl_qty !== undefined || d.sjcl_qty !== undefined) {
-
+                    || d.sgfh_qty !== undefined || d.qtcl_qty !== undefined || d.sjcl_qty !== undefined
+                    || d.real_qty !== undefined
+                ) {
                     const op = this._.find(orgPos, {id: d.pid});
-                    if (op.add_stage !== this.ctx.stage.id) throw '不可修改数据';
+                    if (op.add_stage !== this.ctx.stage.id && (
+                            d.name !== undefined || d.drawing_code !== undefined || d.position !== undefined
+                            || d.sgfh_qty !== undefined || d.qtcl_qty !== undefined || d.sjcl_qty !== undefined
+                        )) throw '不可修改数据';
 
                     const p = {id: op.id};
                     if (d.name !== undefined) p.name = d.name;
@@ -285,6 +291,7 @@ module.exports = app => {
                         if (!updateBills) updateBills = {id: bills.id};
                     }
                     if (d.drawing_code !== undefined) p.drawing_code = d.drawing_code;
+                    if (d.real_qty !== undefined) p.real_qty = this.ctx.helper.round(d.real_qty, precision.value);
                     updatePos.push(p);
                 }
 

+ 8 - 11
app/service/tender.js

@@ -147,7 +147,7 @@ module.exports = app => {
             return list;
         }
 
-        async getTender (id) {
+        async getTender(id) {
             this.initSqlBuilder();
             this.sqlBuilder.setAndWhere('id', {
                 value: id,
@@ -230,7 +230,7 @@ module.exports = app => {
             id = parseInt(id);
 
             const rowData = {
-                id: id,
+                id,
                 name: postData.name,
                 type: postData.type,
                 category: JSON.stringify(postData.category),
@@ -258,7 +258,7 @@ module.exports = app => {
         /**
          * 真删除
          * @param {Number} id - 删除的标段id
-         * @returns {Promise<boolean>} - 结果
+         * @return {Promise<boolean>} - 结果
          */
         async deleteTenderNoBackup(id) {
             const transaction = await this.db.beginTransaction();
@@ -293,18 +293,15 @@ module.exports = app => {
                 await transaction.delete(this.ctx.service.materialBillsHistory.tableName, { tid: id });
                 await transaction.delete(this.ctx.service.materialList.tableName, { tid: id });
                 await transaction.delete(this.ctx.service.materialListNotjoin.tableName, { tid: id });
+                await transaction.delete(this.ctx.service.materialFile.tableName, { tid: id });
 
                 await transaction.delete(this.ctx.service.signatureUsed.tableName, { tender_id: id });
                 await transaction.delete(this.ctx.service.signatureRole.tableName, { tender_id: id });
                 // 先删除附件文件
                 const attList = await this.ctx.service.changeAtt.getAllDataByCondition({ where: { tid: id } });
-                if (attList.length !== 0) {
-                    for (const att of attList) {
-                        if (fs.existsSync(path.join(this.app.baseDir, att.filepath))) {
-                            await fs.unlinkSync(path.join(this.app.baseDir, att.filepath));
-                        }
-                    }
-                }
+                const newAttList = await this.ctx.service.materialFile.getAllMaterialFiles(id);
+                attList.concat(newAttList);
+                await this.ctx.helper.delFiles(attList);
                 await transaction.delete(this.ctx.service.changeAtt.tableName, { tid: id });
                 await transaction.commit();
                 return true;
@@ -371,7 +368,7 @@ module.exports = app => {
             const tenderNodeTemplateData = await this.ctx.service.tenderNodeTemplate.getData(templateId);
             const conn = await this.db.beginTransaction();
             try {
-                await conn.update(this.tableName, {id: tender.id, measure_type: type});
+                await conn.update(this.tableName, { id: tender.id, measure_type: type });
 
                 // 复制模板数据到标段数据表
                 const result = await this.ctx.service.ledger.innerAdd(tenderNodeTemplateData, tender.id, conn);

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

@@ -12,7 +12,7 @@
             <% for (const index in ctx.menuList) { %>
             <% if (ctx.menuList[index].display === undefined || !ctx.menuList[index].display) { continue } %>
             <li <% if(ctx.controllerName === index || (ctx.controllerName === 'list' && index === 'tender')) { %>class="active"<% } %>>
-                <a href="<%- ctx.menuList[index].url %>" data-toggle="tooltip" data-placement="right" title="" data-original-title="<%- ctx.menuList[index].name %>">
+                <a href="<%- (index === 'tender' ? ctx.curListUrl : ctx.menuList[index].url) %>" id="<%- 'nav_' + index%>" data-toggle="tooltip" data-placement="right" title="" data-original-title="<%- ctx.menuList[index].name %>">
                     <i class="fa <%- ctx.menuList[index].icon %>"></i>
                     <% if (ctx.menuList[index].caption) { %>
                     <span><%- ctx.menuList[index].caption %></span>

+ 1 - 0
app/view/ledger/audit.ejs

@@ -95,6 +95,7 @@
 </div>
 <script type="text/javascript">
     const tender = JSON.parse('<%- JSON.stringify(tender) %>');
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
     const measureType = JSON.parse('<%- JSON.stringify(measureType) %>');
     let ledgerSpreadSetting = '<%- ledgerSpreadSetting %>';
     ledgerSpreadSetting = JSON.parse(ledgerSpreadSetting);

+ 1 - 0
app/view/ledger/bwtz.ejs

@@ -123,5 +123,6 @@
             colWidth: true,
         }
     };
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
     const decimal = <%- ctx.tender.info.decimal.tp %>;
 </script>

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

@@ -175,6 +175,7 @@
     const readOnly = <%- ctx.tender.ledgerReadOnly %>;
     const tender = JSON.parse('<%- JSON.stringify(tender) %>');
     const tenderInfo = JSON.parse('<%- JSON.stringify(tenderInfo) %>');
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
     const measureType = JSON.parse('<%- JSON.stringify(measureType) %>');
     let ledgerSpreadSetting = '<%- ledgerSpreadSetting %>';
     ledgerSpreadSetting = JSON.parse(ledgerSpreadSetting);

+ 2 - 1
app/view/ledger/gather.ejs

@@ -66,5 +66,6 @@
     </div>
 </div>
 <script>
-    chapter = JSON.parse('<%- JSON.stringify(ctx.tender.info.chapter) %>');
+    const chapter = JSON.parse('<%- JSON.stringify(ctx.tender.info.chapter) %>');
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
 </script>

+ 51 - 0
app/view/material/file.ejs

@@ -0,0 +1,51 @@
+<% include ./material_sub_menu.ejs %>
+<div class="panel-content">
+  <div class="panel-title">
+    <div class="title-main d-flex justify-content-between">
+      <div class="d-flex justify-content-start align-items-center">
+        <% if(auditors.includes(ctx.session.sessionUser.accountId)) { %>
+          <a href="#addfujian" data-toggle="modal" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="添加清单"><i class="fa fa-cloud-upload" aria-hidden="true"></i> 上传附件</a>
+        <% } %>
+          <span class="d-flex align-items-center" style="margin-left: 5px;">
+            <input type="checkbox" id="file-checkbox">
+            <span class="text-primary" style="margin-left: 5px;">所有期</span>
+          </span>
+        </div>
+    </div>
+  </div>
+  <div class="content-wrap">
+    <div class="c-body">
+      <div class="sjs-height-0">
+        <table class="table table-bordered">
+          <thead>
+            <tr><th width="50">序号</th><th>名称</th><th width="90">大小</th><th width="100">上传时间</th><th width="100">操作</th></tr>
+          </thead>
+          <tbody id="file-list">
+            <% fileList.forEach(function(file, idx){ %>
+              <tr>
+                <td><%=idx + 1%></td>
+                <td>
+                  <a href="/<%- file.filepath %>" target="_blank"><%=file.file_name%></a>
+                </td><td><%=file.file_size%></td>
+                <td><%=file.upload_time%></td>
+                <td>
+                  <% if(material.status !== auditConst.status.checked &&
+                  ctx.session.sessionUser.accountId === file.user_id) { %>
+                    <a href="javascript: void;" class="btn btn-light btn-sm delete-file" title="删除附件" data-attid="<%- file.id %>">
+                      <span class="fa fa-trash text-danger"></span>
+                    </a>
+                  <% } %>
+                </td>
+              </tr>
+            <% }) %>
+          </tbody>
+        </table>
+      </div>
+    </div>
+  </div>
+</div>
+<script>
+  const curMaterialStatus = '<%- material.status %>';
+  const fileList = '<%- JSON.stringify(fileList) %>';
+  const auditors = JSON.parse('<%- JSON.stringify(auditors) %>');
+</script>

+ 54 - 0
app/view/material/file_modal.ejs

@@ -0,0 +1,54 @@
+<!--添加附件-->
+<div class="modal fade" id="addfujian">
+    <div class="modal-dialog" role="document">
+      <div class="modal-content">
+        <div class="modal-header">
+          <h5 class="modal-title" id="myModalLabel">上传附件</h5>
+      <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+        <span aria-hidden="true">&times;</span>
+      </button>
+        </div>
+        <div class="modal-body">
+          <p>大小限制:10MB,支持office等文档格式、图片格式、压缩包格式</p>
+                  <p>
+                      <input id="upload-fujian-file" type="file" multiple="multiple">
+                      <!-- <a href="javascript: void(0);" id="upload-fujian" class="btn btn-primary">选择文件</a> -->
+                    </p>
+        </div>
+    <div class="modal-footer">
+      <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
+        <button id="upload-file-ok" type="button" class="btn btn-primary">添加</button>
+    </div>
+      </div>
+    </div>
+  </div>
+<!--提交审批-->
+  <div class="modal fade" id="sub-ap" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+    <div class="modal-dialog" role="document">
+      <div class="modal-content">
+        <div class="modal-header">
+          <h5 class="modal-title" id="myModalLabel">提交审批</h5>
+      <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+        <span aria-hidden="true">&times;</span>
+      </button>
+        </div>
+        <div class="modal-body">
+            <h5>还没添加任何变更清单,无法提交。</h5>
+            <h5>还没设置审批流程,无法提交。</h5>
+            <h5>确认提交审批?</h5>
+        </div>
+        <div class="modal-footer">
+      <!--无法提交-->
+          <button type="button" class="btn btn-secondary" data-dismiss="modal">好的</button>
+      <!--确认提交-->
+          <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
+      <button type="button" class="btn btn-primary" data-dismiss="modal">确认提交</button>
+        </div>
+      </div>
+    </div>
+  </div>
+  <% include ./audit_modal.ejs %>
+<script>
+  const user_id = '<%- ctx.session.sessionUser.accountId %>';
+  const checked = '<%- material.status !== auditConst.status.checked %>';
+</script>

+ 7 - 0
app/view/material/material_sub_menu.ejs

@@ -24,6 +24,13 @@
                     </li>
                 </ul>
             </div>
+        <div class="nav-box">
+            <ul class="nav-list list-unstyled">
+                <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material/' + ctx.material.order + '/file') { %>active<% } %>">
+                    <a href="/tender/<%- ctx.tender.id %>/measure/material/<%- material.order %>/file"><span class="ml-3">附件</span></a>
+                </li>
+            </ul>
+        </div>
         <% include ./audit_btn.ejs %>
         <div class="side-fold"><a href="javascript: void(0)" data-toggle="tooltip" data-placement="top" data-original-title="折叠侧栏" id="to-mini-menu"><i class="fa fa-sign-out fa-flip-horizontal"></i></a></div>
     </div>

+ 4 - 1
app/view/measure/compare.ejs

@@ -55,4 +55,7 @@
             </div>
         </div>
     </div>
-</div>
+</div>
+<script>
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
+</script>

+ 23 - 13
app/view/measure/stage.ejs

@@ -20,18 +20,18 @@
                 <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>
-                        <th class="text-center">本期实付</th>
-                        <th class="text-center">审批进度</th>
-                        <th class="text-center">操作</th>
+                        <th class="text-center" width="5%">计量期数</th>
+                        <th class="text-center" width="5%">计量月份</th>
+                        <th class="text-center" width="10%">开始-截止日期</th>
+                        <th class="text-center" width="9%">本期合同计量</th>
+                        <th class="text-center" width="9%">本期数量变更计量</th>
+                        <th class="text-center" width="9%">本期完成计量</th>
+                        <th class="text-center" width="9%">截止上期完成计量</th>
+                        <th class="text-center" width="9%">截止本期完成计量</th>
+                        <th class="text-center" width="9%">本期应付</th>
+                        <th class="text-center" width="9%">本期实付</th>
+                        <th class="text-center" width="10%">审批进度</th>
+                        <th class="text-center" width="7%">操作</th>
                     </tr>
                     </thead>
                     <tbody>
@@ -44,7 +44,16 @@
                             <% } %>
                         </td>
                         <td class="text-center"><%- s.s_time %></td>
-                        <td class="text-center" width="12%"><%- s.period %></td>
+                        <td class="text-center"><%- s.period %></td>
+                        <% if (ctx.tender.info.display.thousandth) { %>
+                        <td class="text-right"><%- (s.contract_tp ? s.contract_tp.toFixed(3) : '')%></td>
+                        <td class="text-right"><%- (s.qc_tp ? s.qc_tp.toFixed(3) : '')%></td>
+                        <td class="text-right"><%- (s.tp ? s.tp.toFixed(3) : '')%></td>
+                        <td class="text-right"><%- (s.pre_tp ? s.pre_tp.toFixed(3) : '')%></td>
+                        <td class="text-right"><%- (s.end_tp ? s.end_tp.toFixed(3) : '')%></td>
+                        <td class="text-right"><%- (s.yf_tp ? s.yf_tp.toFixed(3) : '') %></td>
+                        <td class="text-right"><%- (s.sf_tp ? s.sf_tp.toFixed(3) : '') %></td>
+                        <% } else { %>
                         <td class="text-right"><%- (s.contract_tp ? s.contract_tp : '')%></td>
                         <td class="text-right"><%- (s.qc_tp ? s.qc_tp : '')%></td>
                         <td class="text-right"><%- (s.tp ? s.tp : '')%></td>
@@ -52,6 +61,7 @@
                         <td class="text-right"><%- (s.end_tp ? s.end_tp : '')%></td>
                         <td class="text-right"><%- (s.yf_tp ? s.yf_tp : '') %></td>
                         <td class="text-right"><%- (s.sf_tp ? s.sf_tp : '') %></td>
+                        <% } %>
                         <td class="<%- auditConst.auditProgressClass[s.status] %>">
                             <% if (s.curAuditor) { %>
                             <a href="#sp-list" data-toggle="modal" data-target="#sp-list" s-order="<%- s.order %>"><%- s.curAuditor.name %><%if (s.curAuditor.role !== '' && s.curAuditor.role !== null) { %>-<%- s.curAuditor.role %><% } %></a>

+ 1 - 0
app/view/revise/history.ejs

@@ -109,4 +109,5 @@
     const readOnly = <%- readOnly %>;
     const isTz = <%- ctx.tender.data.measure_type === measureType.tz.value %>;
     const billsSpreadSetting = JSON.parse('<%- JSON.stringify(ledgerSpread) %>');
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
 </script>

+ 1 - 0
app/view/revise/info.ejs

@@ -215,4 +215,5 @@
     const isTz = <%- ctx.tender.data.measure_type === measureType.tz.value %>;
     const billsSpreadSetting = JSON.parse('<%- JSON.stringify(ledgerSpread) %>');
     const posSpreadSetting = JSON.parse('<%- JSON.stringify(posSpread) %>');
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
 </script>

+ 49 - 0
app/view/setting/show.ejs

@@ -0,0 +1,49 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main">
+            <h2>显示设置</h2>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-body">
+            <div class="sjs-height-0">
+                <div class="row m-0 mt-3">
+                    <div class="col-5">
+                        <div class="form-group">
+                            <label>项目列表默认显示</label>
+                            <div class="card w-50">
+                                <ul class="list-group list-group-flush">
+                                    <% showList.forEach(function(item, idx) { %>
+                                        <li class="list-group-item">
+                                            <%= item.label_name %>
+                                            <% if(!item.is_default) { %>
+                                                <a href="javascript:void(0)" id="set-default" class="btn btn-primary btn-sm pull-right" data-attid="<%- idx %>">设为默认</a>
+                                            <% } else {%>
+                                                <span class="pull-right">默认</span>
+                                            <% } %>
+                                        </li>
+                                    <% }) %>
+                                    <!-- <li class="list-group-item">
+                                        标段列表<a href="#" class="btn btn-primary btn-sm pull-right">设为默认</a>
+                                    </li>
+                                    <li class="list-group-item">金额概况<span class="pull-right">默认</span></li>
+                                    <li class="list-group-item">
+                                        计量进度<a href="#" class="btn btn-primary btn-sm pull-right">设为默认</a>
+                                    </li> -->
+                                </ul>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script src="/public/js/setting.js"></script>
+<script>
+    $(function () {
+        autoFlashHeight();
+    })
+      const showList = JSON.parse('<%- JSON.stringify(showList) %>');
+</script>

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

@@ -25,7 +25,7 @@
     <% if (ctx.stage.auditors !== undefined && ctx.stage.auditors.length !== 0 && ctx.stage.auditors[ctx.stage.auditors.length-1].aid === ctx.session.sessionUser.accountId && ctx.stage.status === auditConst.status.checked && ctx.stage.order === ctx.stage.highOrder) { %>
         <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-back" class="btn btn-warning btn-sm btn-block">重新审批</a>
     <% } %>
-    <% if (ctx.stage.user_id === ctx.session.sessionUser.accountId && ctx.stage.order === ctx.stage.highOrder) { %>
+    <% if (ctx.stage.user_id === ctx.session.sessionUser.accountId && ctx.stage.order === ctx.stage.highOrder && (ctx.stage.status === auditConst.status.checkNo || ctx.stage.status === auditConst.status.uncheck)) { %>
         <a href="#del-qi" data-toggle="modal" data-target="#del-qi" class="btn btn-outline-danger btn-sm btn-block mt-5">删除本期</a>
     <% } %>
 </div>

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

@@ -1450,7 +1450,7 @@
         </div>
     </div>
 <% } %>
-<% if (ctx.stage.user_id === ctx.session.sessionUser.accountId && ctx.stage.order === ctx.stage.highOrder) { %>
+<% if (ctx.stage.user_id === ctx.session.sessionUser.accountId && ctx.stage.order === ctx.stage.highOrder && (ctx.stage.status === auditConst.status.checkNo || ctx.stage.status === auditConst.status.uncheck)) { %>
     <!--删除期-->
     <% if (ctx.stage.hadMaterial) { %>
         <div class="modal fade" id="del-qi" data-backdrop="static">

+ 3 - 0
app/view/stage/bwtz.ejs

@@ -128,6 +128,8 @@
             {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
             {title: '台账|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 60, type: 'Number'},
+            {title: '现场实际数量', colSpan: '1', rowSpan: '2', field: 'real_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '预计变更数量', colSpan: '1', rowSpan: '2', field: 'estimate_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             {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'},
@@ -159,4 +161,5 @@
         },
     };
     const decimal = <%- ctx.tender.info.decimal.tp %>;
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
 </script>

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

@@ -81,4 +81,5 @@
     const scCacheKey = 'stage-compare-role-' + stage.tid + '-' + stage.order;
     let scRoles = getLocalCache(scCacheKey);
     scRoles = scRoles ? scRoles.split(',') : [0];
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
 </script>

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

@@ -101,5 +101,6 @@
         key: 'stage-gather-leafXmj',
         colWidth: true,
     }
-    chapter = JSON.parse('<%- JSON.stringify(ctx.tender.info.chapter) %>');
+    const chapter = JSON.parse('<%- JSON.stringify(ctx.tender.info.chapter) %>');
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
 </script>

+ 18 - 14
app/view/stage/index.ejs

@@ -10,14 +10,14 @@
                             <i class="fa fa-list-ol"></i> 显示层级
                         </button>
                         <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
-                            <a class="dropdown-item" name="showLevel" tag="1" href="javascirpt: void(0);">第一层</a>
-                            <a class="dropdown-item" name="showLevel" tag="2" href="javascirpt: void(0);">第二层</a>
-                            <a class="dropdown-item" name="showLevel" tag="3" href="javascirpt: void(0);">第三层</a>
-                            <a class="dropdown-item" name="showLevel" tag="4" href="javascirpt: void(0);">第四层</a>
-                            <a class="dropdown-item" name="showLevel" tag="5" href="javascirpt: void(0);">第五层</a>
-                            <a class="dropdown-item" name="showLevel" tag="last" href="javascirpt: void(0);">最底层</a>
-                            <a class="dropdown-item" name="showLevel" tag="leafXmj" href="javascirpt: void(0);">只显示项目节</a>
-                            <a class="dropdown-item" name="showLevel" tag="curMeasure" href="javascirpt: void(0);">只显示本期计量</a>
+                            <a class="dropdown-item" name="showLevel" tag="1" href="javascript: void(0);">第一层</a>
+                            <a class="dropdown-item" name="showLevel" tag="2" href="javascript: void(0);">第二层</a>
+                            <a class="dropdown-item" name="showLevel" tag="3" href="javascript: void(0);">第三层</a>
+                            <a class="dropdown-item" name="showLevel" tag="4" href="javascript: void(0);">第四层</a>
+                            <a class="dropdown-item" name="showLevel" tag="5" href="javascript: void(0);">第五层</a>
+                            <a class="dropdown-item" name="showLevel" tag="last" href="javascript: void(0);">最底层</a>
+                            <a class="dropdown-item" name="showLevel" tag="leafXmj" href="javascript: void(0);">只显示项目节</a>
+                            <a class="dropdown-item" name="showLevel" tag="curMeasure" href="javascript: void(0);">只显示本期计量</a>
                         </div>
                     </div>
                 </div>
@@ -293,20 +293,22 @@
                                 <div class="tab-content" id="showAttachment" style="display: none" file-id="">
                                     <div class="sjs-bottom-2">
                                         <div class="d-flex justify-content-end mb-1" id="btn-att">
+                                            <a href="javascript:void(0);" content="location" class="btn btn-sm btn-outline-primary" style="margin-right: 5px">定位</a>
+                                            <a href="javascript:void(0);" content="view" class="btn btn-sm btn-outline-primary" style="margin-right: 5px">查看</a>
                                             <!--默认 有删除权限-->
-                                            <a href="javascript:void(0);" content="del" class="btn btn-sm text-danger" style="display: none">删除</a>
+                                            <a href="javascript:void(0);" content="del" class="btn btn-sm text-danger" style="display: none; margin-right: 5px">删除</a>
                                             <!--默认 有编辑权限-->
-                                            <a href="javascript:void(0);" content="edit" class="btn btn-sm btn-outline-primary" style="display: none">编辑</a>
+                                            <a href="javascript:void(0);" content="edit" class="btn btn-sm btn-outline-primary" style="display: none; margin-right: 5px">编辑</a>
                                             <!--编辑模式-->
-                                            <a href="javascript:void(0);" content="save" class="btn btn-sm btn-outline-success mr-1" style="display: none">保存</a>
-                                            <a href="javascript:void(0);" content="cancel" class="btn btn-sm btn-outline-secondary" style="display: none">取消</a>
+                                            <a href="javascript:void(0);" content="save" class="btn btn-sm btn-outline-success mr-1" style="display: none; margin-right: 5px">保存</a>
+                                            <a href="javascript:void(0);" content="cancel" class="btn btn-sm btn-outline-secondary" style="display: none; margin-right: 5px">取消</a>
                                         </div>
                                         <!--显示信息-->
                                         <table class="table table-sm table-bordered" id="show-att" style="word-break:break-all; table-layout: fixed">
                                             <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="javascript:void(0);">下载附件</a></td></tr>
+                                            <tr><th>所在节点</th><td colspan="3" id="show-att-node">1 第一部分 建筑安装工程非</td></tr>
+                                            <tr><td colspan="4"><a href="javascript:void(0);" target="_blank"><span>下载附件</span></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>
@@ -576,12 +578,14 @@
     }
     const tender = JSON.parse('<%- JSON.stringify(tender) %>');
     const tenderInfo = JSON.parse('<%- JSON.stringify(ctx.tender.info) %>');
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
     const measureType = JSON.parse('<%- JSON.stringify(measureType) %>');
     const stage = JSON.parse('<%- JSON.stringify(ctx.stage) %>');
     const imType = JSON.parse('<%- JSON.stringify(imType) %>');
     const whiteList = JSON.parse('<%- JSON.stringify(whiteList) %>');
     let attData = JSON.parse('<%- JSON.stringify(attData) %>');
     const ckColSetting = 'stage-col-visible-1.0.3-<%- tender.id %>';
+    const auditConst = JSON.parse('<%- JSON.stringify(auditConst) %>');
 </script>
 <% if (ctx.stage.status === auditConst.status.uncheck && ctx.session.sessionUser.accountId === ctx.stage.user_id) {%>
 <script>

+ 12 - 3
app/view/stage/manager.ejs

@@ -13,7 +13,7 @@
             <div class="c-body">
                 <% if (lastStage) { %>
                 <% for (let i = lastStage.order; i > 0; i--) { %>
-                    <% if (i === lastStage.order && lastStage.status === auditConst.status.checked) { %>
+                    <% if (i === lastStage.order && lastStage.status !== auditConst.status.uncheck) { %>
                     <table class="table table-bordered">
                         <thead>
                         <tr><th>第<%- lastStage.order %>期</th></tr>
@@ -24,7 +24,16 @@
                                 <table class="table table-bordered table-hover">
                                     <thead>
                                     <tr><th colspan="5"><%- lastAuditList[la][0].times %>#
-                                            <% if (parseInt(la) === 0) { %><a href="#del" data-toggle="modal" class="btn btn-sm btn-light pull-right text-danger">删除本次审批</a><a href="#pass" data-toggle="modal" class="btn btn-sm btn-warning pull-right mr-2">设置终审审批</a><% } %>
+                                            <% if (parseInt(la) === 0) { %>
+                                                <% if (lastStage.times === 1 || (lastStage.times === 2 && lastStage.status === auditConst.status.checkNo)) { %>
+                                                <a href="#del-qi" data-toggle="modal" class="btn btn-sm btn-light pull-right text-danger">删除本期</a>
+                                                <% } else { %>
+                                                <a href="#del" data-toggle="modal" class="btn btn-sm btn-light pull-right text-danger">删除本次审批</a>
+                                                <% } %>
+                                                <% if (lastStage.status === auditConst.status.checked) { %>
+                                                <a href="#pass" data-toggle="modal" class="btn btn-sm btn-warning pull-right mr-2">设置终审审批</a>
+                                                <% } %>
+                                            <% } %>
                                         </th></tr>
                                     </thead>
                                     <tbody>
@@ -44,7 +53,7 @@
                                             <% } else { %>
                                             <span class="<%- auditConst.auditStringClass[audit.status] %>"><% if (audit.status !== auditConst.status.uncheck) { %><%- auditConst.auditProgress[audit.status] %><% } %></span>
                                             <% } %>
-                                            <p class="text-muted m-0"><% if (audit.status !== auditConst.status.uncheck) { %><%- audit.end_time.toLocaleDateString() %><% } %></p></td>
+                                            <p class="text-muted m-0"><% if (audit.status !== auditConst.status.uncheck && audit.status !== auditConst.status.checking) { %><%- audit.end_time.toLocaleDateString() %><% } %></p></td>
                                         <td><%- audit.opinion %></td>
                                     </tr>
                                     <% } %>

+ 37 - 9
app/view/stage/manager_modal.ejs

@@ -1,7 +1,8 @@
+<% if (lastStage) { %>
 <!--删除本次审批-->
 <div class="modal fade" id="del" data-backdrop="static">
     <div class="modal-dialog" role="document">
-        <div class="modal-content">
+        <form class="modal-content" action='/tender/<%= ctx.tender.id %>/measure/stage/<%- lastStage.order %>/manager/audit/delete' method="post">
             <div class="modal-header">
                 <h5 class="modal-title">删除本次审批</h5>
             </div>
@@ -9,32 +10,59 @@
                 <p class="mb-2">删除本次审批,本期计量将退回至上一轮审批状态。</p>
                 <p class="mb-2">删除后数据不可恢复,请谨慎操作。</p>
                 <p class="mb-2">请在下方文本框输入文本「<span class="text-danger">确认删除本次审批</span>」,确认删除。</p>
-                <p class="mb-2"><input type="text" class="form-control form-control-sm" placeholder="输入文本,确认删除"></p>
+                <p class="mb-2"><input type="text" name="confirm" class="form-control form-control-sm" placeholder="输入文本,确认删除"></p>
             </div>
             <div class="modal-footer">
+                <input type="hidden" name="_csrf" value="<%= ctx.csrf %>" />
                 <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
-                <button type="button" class="btn btn-sm btn-danger">确认删除</button>
+                <button type="submit" class="btn btn-sm btn-danger">确认删除</button>
             </div>
-        </div>
+        </form>
     </div>
 </div>
+<!--删除本期-->
+<div class="modal fade" id="del-qi" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <form class="modal-content" action='/tender/<%= ctx.tender.id %>/measure/stage/delete' method="post">
+            <div class="modal-header">
+                <h5 class="modal-title">删除期</h5>
+            </div>
+            <div class="modal-body">
+                <h5>确认删除「第<%= lastStage.order %>期」?</h5>
+                <h5>删除后,数据无法恢复,请谨慎操作。</h5>
+                <p class="mb-2">请在下方文本框输入文本「<span class="text-danger">确认删除本期</span>」,确认删除。</p>
+                <p class="mb-2"><input type="text" name="confirm" class="form-control form-control-sm" placeholder="输入文本,确认删除"></p>
+            </div>
+            <div class="modal-footer">
+                <input type="hidden" name="stage_id" value="<%= lastStage.id %>">
+                <input type="hidden" name="stage_order" value="<%- ctx.stage.order %>">
+                <input type="hidden" name="_csrf" value="<%= ctx.csrf %>" />
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">取消</button>
+                <button type="submit" class="btn btn-danger btn-sm">确定删除</button>
+            </div>
+        </form>
+    </div>
+</div>
+<% if (lastStage.status === auditConst.status.checked) { %>
 <!--设置终审-->
 <div class="modal fade" id="pass" data-backdrop="static">
     <div class="modal-dialog" role="document">
-        <div class="modal-content">
+        <form action="/tender/<%- ctx.tender.id %>/measure/stage/<%- lastStage.order %>/audit/check/again" method="get" class="modal-content">
             <div class="modal-header">
                 <h5 class="modal-title">设置终审审批</h5>
             </div>
             <div class="modal-body">
-                <p class="mb-2">设置本期计量终审「孙鸿福」为审批中状态。</p>
+                <p class="mb-2">设置本期计量终审「<%- lastAuditList[0][0].name  %>」为审批中状态。</p>
                 <p class="mb-2">请在下方文本框输入文本「<span class="text-danger">确认设置终审审批</span>」,确认设置。</p>
-                <p class="mb-2"><input type="text" class="form-control form-control-sm" placeholder="输入文本,确认设置">
+                <p class="mb-2"><input type="text" name="confirm" class="form-control form-control-sm" placeholder="输入文本,确认设置">
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
-                <button type="button" class="btn btn-sm btn-warning">确认设置</button>
+                <button type="submit" class="btn btn-sm btn-warning">确认设置</button>
             </div>
-        </div>
+        </form>
     </div>
 </div>
+<% } %>
+<% } %>
 <% include ./audit_modal.ejs %>

+ 9 - 0
app/view/stage/modal.ejs

@@ -328,6 +328,10 @@
                 </p>
                 <div class="img-view">
                 </div>
+                <div class="mt-2">
+                    <textarea id="text-edit" class="form-control form-control-sm" placeholder="草图备注" rows="3">
+                    </textarea>
+                </div>
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
@@ -374,7 +378,12 @@
             </div>
             <div class="modal-body">
                 <img src="" id="view-calc-img">
+                <div class="mt-2">
+                    <textarea id="view-calc-remark" class="form-control form-control-sm" placeholder="草图备注" rows="3">
+                    </textarea>
+                </div>
             </div>
+
             <div class="modal-footer">
                 <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
             </div>

+ 2 - 1
app/view/stage/pay.ejs

@@ -38,7 +38,7 @@
                             <td><%- iBase + 1 %></td>
                             <td><%- calcBase[iBase].name %></td>
                             <td><%- calcBase[iBase].code %></td>
-                            <td><%- calcBase[iBase].value %></td>
+                            <td class="text-right"><%- (ctx.tender.info.display.thousandth ? calcBase[iBase].value.toFixed(3) : calcBase[iBase].value) %></td>
                         </tr>
                         <% } %>
                     </table>
@@ -58,6 +58,7 @@
     const dealPay = JSON.parse('<%- JSON.stringify(dealPay) %>');
     const calcBase = JSON.parse('<%- JSON.stringify(calcBase) %>');
     const decimal = JSON.parse('<%- JSON.stringify(ctx.tender.info.decimal) %>');
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
     const whiteList = JSON.parse('<%- JSON.stringify(whiteList) %>');
     const uploadPermission = <%- uploadPermission %>;
     const preContractTp = <%- (pre.contract_tp || 0) %>;

+ 9 - 0
app/view/stage/stage_sub_menu.ejs

@@ -68,6 +68,15 @@
                 </li>
             </ul>
         </div>
+        <% if (ctx.session.sessionUser.is_admin) { %>
+        <div class="nav-box">
+            <ul class="nav-list list-unstyled">
+                <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/stage/' + ctx.stage.order + '/manager') { %>active<% } %>">
+                    <a href="/tender/<%- ctx.tender.id %>/measure/stage/<%- ctx.stage.order %>/manager"><span class="ml-3">审批管理</span></a>
+                </li>
+            </ul>
+        </div>
+        <% } %>
         <% include ./audit_btn.ejs %>
         <div class="side-fold"><a href="javascript: void(0)" data-toggle="tooltip" data-placement="top" data-original-title="折叠侧栏" id="to-mini-menu"><i class="fa fa-upload fa-rotate-270"></i></a></div>
     </div>

+ 9 - 0
app/view/stage/stage_sub_mini_menu.ejs

@@ -68,6 +68,15 @@
                 </li>
             </ul>
         </div>
+        <% if (ctx.session.sessionUser.is_admin) { %>
+            <div class="nav-box">
+                <ul class="nav-list list-unstyled">
+                    <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/stage/' + ctx.stage.order + '/manager') { %>active<% } %>">
+                        <a href="/tender/<%- ctx.tender.id %>/measure/stage/<%- ctx.stage.order %>/manager"><span class="ml-3">审批管理</span></a>
+                    </li>
+                </ul>
+            </div>
+        <% } %>
         <% include ./audit_btn.ejs %>
         <div class="side-fold"><a href="javascript: void(0);" data-toggle="tooltip" data-placement="top" data-original-title="展开侧栏" id="to-menu"><i class="fa fa-upload fa-rotate-90"></i></a></div>
     </div>

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

@@ -1,7 +1,7 @@
 <div class="panel-sidebar" id="sub-menu">
     <div class="panel-title">
         <div class="title-bar">
-            <h2 class="text-truncate" style="white-space:nowrap; overflow:hidden; text-overflow:ellipsis;" data-toggle="tooltip" data-placement="right" title=""  data-original-title="其他台账">其他台账</h2>
+            <h2 class="text-truncate" style="white-space:nowrap; overflow:hidden; text-overflow:ellipsis;" data-toggle="tooltip" data-placement="right" title=""  data-original-title="第<%- ctx.stage.order%>期 - 其他台账">其他台账</h2>
         </div>
     </div>
     <div class="scrollbar-auto">

+ 46 - 4
app/view/tender/detail.ejs

@@ -181,7 +181,13 @@
                 name : '金额',
                 position:'left',
                 axisLabel : {
+                    <% if (ctx.tender.info.display.thousandth) { %>
+                    formatter: function (value, index) {
+                        return value.toFixed(3) + ' 元'
+                    }
+                    <% } else { %>
                     formatter: '{value} 元'
+                    <% } %>
                 },
                 splitArea : {show : true},
                 splitLine : {show : true},
@@ -201,7 +207,16 @@
             {
                 name:'本期合同计量',
                 type:'bar',
-                tooltip : {trigger: 'item',formatter: "{b}  <br/>{a}:{c}元"},
+                tooltip : {
+                    trigger: 'item',
+                    <% if (ctx.tender.info.display.thousandth) { %>
+                    formatter: function (params, ticket, callback) {
+                        return params.name + '<br/>' + params.seriesName + ': ' + params.value.toFixed(3) + ' 元';
+                    }
+                    <% } else { %>
+                    formatter: '{value} 元'
+                    <% } %>
+                },
                 stack: '合同',
                 data:[
                     <% for (const s of stages) {%>
@@ -212,7 +227,16 @@
             {
                 name:'本期数量变更计量',
                 type:'bar',
-                tooltip : {trigger: 'item',formatter: "{b}  <br/>{a}:{c}元"},
+                tooltip : {
+                    trigger: 'item',
+                    <% if (ctx.tender.info.display.thousandth) { %>
+                    formatter: function (params, ticket, callback) {
+                        return params.name + '<br/>' + params.seriesName + ': ' + params.value.toFixed(3) + ' 元';
+                    }
+                    <% } else { %>
+                    formatter: '{value} 元'
+                    <% } %>
+                },
                 stack: '变更',
                 data:[
                     <% for (const s of stages) {%>
@@ -223,7 +247,16 @@
             {
                 name:'截至上期累计完成',
                 type:'bar',
-                tooltip : {trigger: 'item',formatter: "{b}  <br/>{a}:{c}元"},
+                tooltip : {
+                    trigger: 'item',
+                    <% if (ctx.tender.info.display.thousandth) { %>
+                    formatter: function (params, ticket, callback) {
+                        return params.name + '<br/>' + params.seriesName + ': ' + params.value.toFixed(3) + ' 元';
+                    }
+                    <% } else { %>
+                    formatter: '{value} 元'
+                    <% } %>
+                },
                 stack: '完成',
                 data:[
                     <% for (const s of stages) {%>
@@ -234,7 +267,16 @@
             {
                 name:'本期完成计量',
                 type:'bar',
-                tooltip : {trigger: 'item',formatter: "{b}  <br/>{a}:{c}元"},
+                tooltip : {
+                    trigger: 'item',
+                    <% if (ctx.tender.info.display.thousandth) { %>
+                    formatter: function (params, ticket, callback) {
+                        return params.name + '<br/>' + params.seriesName + ': ' + params.value.toFixed(3) + ' 元';
+                    }
+                    <% } else { %>
+                    formatter: '{value} 元'
+                    <% } %>
+                },
                 stack: '完成',
                 data:[
                     <% for (const s of stages) {%>

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

@@ -501,10 +501,18 @@
                         <input type="checkbox" class="form-check-input" id="ledger-dgn-qty" checked="">
                         <label class="form-check-label" for="ledger-dgn-qty">项目节数量</label>
                     </div>
-                    <div class="custom-control custom-checkbox">
+                    <div class="custom-control custom-checkbox mb-2">
                         <input type="checkbox" class="form-check-input" id="ledger-cl-qty" checked="">
                         <label class="form-check-label" for="ledger-cl-qty">错漏增减</label>
                     </div>
+                    <div class="custom-control custom-checkbox mb-2">
+                        <input type="checkbox" class="form-check-input" id="thousandth" checked="">
+                        <label class="form-check-label" for="thousandth">千分位</label>
+                    </div>
+                    <div class="custom-control custom-checkbox mb-2">
+                        <input type="checkbox" class="form-check-input" id="stage-rc" checked="">
+                        <label class="form-check-label" for="stage-rc">实际完成量</label>
+                    </div>
                 </div>
             </div>
             <div class="modal-footer">
@@ -1071,6 +1079,8 @@
     function loadDisplayProperty () {
         $('#ledger-dgn-qty')[0].checked = property.display.ledger.dgnQty;
         $('#ledger-cl-qty')[0].checked = property.display.ledger.clQty;
+        $('#thousandth')[0].checked = property.display.thousandth;
+        $('#stage-rc')[0].checked = property.display.stage.realComplete;
     }
     $('#bd-set-5').on('show.bs.modal', function () {
         loadDisplayProperty();
@@ -1079,6 +1089,8 @@
         const prop = {
             display: {
                 ledger: { dgnQty: $('#ledger-dgn-qty')[0].checked, clQty: $('#ledger-cl-qty')[0].checked, },
+                thousandth: $('#thousandth')[0].checked,
+                stage: { realComplete: $('#stage-rc')[0].checked, },
             },
         };
         const tenderId = window.location.pathname.split('/')[2];

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

@@ -15,5 +15,6 @@
     const measureType = JSON.parse('<%- JSON.stringify(measureType) %>');
     const uid = '<%- uid %>';
     const pid = '<%- pid %>';
+
     const uphlname = 'user_' + uid + '_pro_' + pid + '_category_hide_list';
 </script>

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

@@ -11,6 +11,7 @@
     const tenders = JSON.parse('<%- JSON.stringify(tenderList) %>');
     const categoryType = JSON.parse('<%- JSON.stringify(settingConst.cType) %>');
     const category = JSON.parse('<%- JSON.stringify(categoryData) %>');
+    const auditConst = JSON.parse('<%- JSON.stringify(auditConst) %>');
     const uid = '<%- uid %>';
     const pid = '<%- pid %>';
     const uphlname = 'user_' + uid + '_pro_' + pid + '_category_hide_list';

+ 9 - 5
builder_report_index_define.js

@@ -636,6 +636,7 @@ const stage_im_zl = {
         { name: '台账金额', field: 'total_price', type: dataType.currency },
         { name: '位置', field: 'position', type: dataType.str },
         { name: '计量单元', field: 'jldy', type: dataType.str },
+        { name: '草图备注', field: 'calc_memo_remark', type: dataType.str },
     ],
 };
 const stage_im_tz = {
@@ -671,6 +672,7 @@ const stage_im_tz = {
         { name: '截止本期 - 变更金额', field: 'end_qc_jl', type: dataType.currency },
         { name: '位置', field: 'position', type: dataType.str },
         { name: '计量单元', field: 'jldy', type: dataType.str },
+        { name: '草图备注', field: 'calc_img_remark', type: dataType.str },
     ],
 };
 const stage_im_tz_bills = {
@@ -785,11 +787,11 @@ const gather_stage_bills = {
 
         { name: '交叉排序', field: 'cross_index', type: dataType.int },
 
-        { name: '树结构-id', fields: 'id', type: dataType.int },
-        { name: '树结构-父项id', fields: 'pid', type: dataType.int },
-        { name: '树结构-层次', fields: 'level', type: dataType.int },
-        { name: '树结构-排序', fields: 'order', type: dataType.int },
-        { name: '树结构-完整路径', fields: 'full_path', type: dataType.str },
+        { name: '树结构-id', field: 'id', type: dataType.int },
+        { name: '树结构-父项id', field: 'pid', type: dataType.int },
+        { name: '树结构-层次', field: 'level', type: dataType.int },
+        { name: '树结构-排序', field: 'order', type: dataType.int },
+        { name: '树结构-完整路径', field: 'full_path', type: dataType.str },
 
         { name: '(特殊1-需替换key1)台账-设计数量1', field: 'ts_key1_dgn_qty1', type: dataType.currency },
         { name: '(特殊1-需替换key1)台账-设计数量2', field: 'ts_key1_dgn_qty2', type: dataType.currency },
@@ -806,6 +808,8 @@ const gather_stage_bills = {
         { name: '(标段)合同-设计数量2', field: 't_deal_dgn_qty2', type: dataType.currency },
         { name: '(标段)变更-设计数量1', field: 't_c_dgn_qty1', type: dataType.currency },
         { name: '(标段)变更-设计数量2', field: 't_c_dgn_qty2', type: dataType.currency },
+
+        { name: '树结构-是否子项', field: 'is_leaf', type: dataType.int },
     ],
 };
 const gather_tender_info = {

+ 19 - 0
config/config.default.js

@@ -163,5 +163,24 @@ module.exports = appInfo => {
         weak: false,
     };
 
+    config.wechatAll = {
+        appid: 'wx1c1cd8bae5836439',
+        appsecret: 'a35104f156faf19ab7a3ae4f990a1dd4',
+        token: 'smartcost3850888',
+        encodingAESKey: 'yjTsgluXZnsx5At4XjtOgeIZzmPuuFqoa3tLe25WxtC',
+        payment: {
+            partnerKey: '',
+            mchId: '',
+            notifyUrl: '',
+            pfx: '',
+        },
+        modules: {
+            message: false, // enable or disable co-wechat
+            api: true, // enable or disable co-wechat-api
+            oauth: true, // enable or disable co-wechat-oauth
+            payment: false, // enable or disable co-wechat-payment
+        },
+    };
+
     return config;
 };

+ 6 - 0
config/menu.js

@@ -261,6 +261,12 @@ const settingMenu = {
         url: '/setting/user',
         caption: '账号设置',
     },
+    show: {
+        name: '显示设置',
+        display: true,
+        url: '/setting/show',
+        caption: '显示设置',
+    },
     category: {
         name: '标段自定义类别',
         display: true,

+ 5 - 1
config/plugin.js

@@ -32,4 +32,8 @@ exports.jsValidator = {
 };
 exports.etag = {
     package: 'egg-etag',
-};
+};
+exports.wechatAll = {
+    enable: true,
+    package: 'egg-wechat-all',
+};

+ 386 - 360
config/web.js

@@ -32,433 +32,449 @@
  * @version
  */
 const JsFiles = {
-    webPath: "/public/js/web/",
+    webPath: '/public/js/web/',
     commonFiles: [
-        "/public/js/jquery/jquery-3.2.1.min.js",
-        "/public/js/jquery/jquery-ui.js",
-        "/public/js/jquery/jquery.validate.js",
-        "/public/js/messages_zh.js",
-        "/public/js/popper/popper.min.js",
-        "/public/js/bootstrap/bootstrap.min.js",
-        "/public/js/vue/vue.js",
-        "/public/js/component/input.js",
-        "/public/js/cookies.js",
-        "/public/js/jquery-contextmenu/jquery.ui.position.min.js",
-        "/public/js/jquery-contextmenu/jquery.contextMenu.min.js",
-        "/public/js/lodash.js",
-        "/public/js/lz-string/lz-string.js",
-        "/public/js/number-precision.js",
-        "/public/js/toastr.min.js",
-        "/public/js/global.js",
+        '/public/js/jquery/jquery-3.2.1.min.js',
+        '/public/js/jquery/jquery-ui.js',
+        '/public/js/jquery/jquery.validate.js',
+        '/public/js/messages_zh.js',
+        '/public/js/popper/popper.min.js',
+        '/public/js/bootstrap/bootstrap.min.js',
+        '/public/js/vue/vue.js',
+        '/public/js/component/input.js',
+        '/public/js/cookies.js',
+        '/public/js/jquery-contextmenu/jquery.ui.position.min.js',
+        '/public/js/jquery-contextmenu/jquery.contextMenu.min.js',
+        '/public/js/lodash.js',
+        '/public/js/lz-string/lz-string.js',
+        '/public/js/number-precision.js',
+        '/public/js/toastr.min.js',
+        '/public/js/global.js',
     ],
     controller: {
         tender: {
             list: {
                 files: [
-                    "/public/js/ztree/jquery.ztree.core.js",
-                    "/public/js/ztree/jquery.ztree.exedit.js",
-                    "/public/js/decimal.min.js",
-                    "/public/js/moment/moment.min.js",
+                    '/public/js/ztree/jquery.ztree.core.js',
+                    '/public/js/ztree/jquery.ztree.exedit.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/moment/moment.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/zh_calc.js",
-                    "/public/js/shares/tender_list_order.js",
-                    "/public/js/tender_showhide.js",
-                    "/public/js/tender_list.js"
+                    '/public/js/zh_calc.js',
+                    '/public/js/shares/tender_list_order.js',
+                    '/public/js/tender_showhide.js',
+                    '/public/js/tender_list.js',
                 ],
                 mergeFile: 'tender_list',
             },
             info: {
                 files: [
-                    "/public/js/ztree/jquery.ztree.core.js",
-                    "/public/js/ztree/jquery.ztree.exedit.js",
-                    "/public/js/decimal.min.js",
+                    '/public/js/ztree/jquery.ztree.core.js',
+                    '/public/js/ztree/jquery.ztree.exedit.js',
+                    '/public/js/decimal.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/zh_calc.js",
-                    "/public/js/shares/tender_list_order.js",
-                    "/public/js/tender_showhide.js",
-                    "/public/js/tender_list_info.js"
+                    '/public/js/zh_calc.js',
+                    '/public/js/shares/tender_list_order.js',
+                    '/public/js/tender_showhide.js',
+                    '/public/js/tender_list_info.js',
                 ],
                 mergeFile: 'tender_list_info',
             },
             progress: {
                 files: [
-                    "/public/js/ztree/jquery.ztree.core.js",
-                    "/public/js/ztree/jquery.ztree.exedit.js",
-                    "/public/js/decimal.min.js",
+                    '/public/js/ztree/jquery.ztree.core.js',
+                    '/public/js/ztree/jquery.ztree.exedit.js',
+                    '/public/js/decimal.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/zh_calc.js",
-                    "/public/js/shares/tender_list_order.js",
-                    "/public/js/tender_showhide.js",
-                    "/public/js/tender_list_progress.js"
+                    '/public/js/zh_calc.js',
+                    '/public/js/shares/tender_list_order.js',
+                    '/public/js/tender_showhide.js',
+                    '/public/js/tender_list_progress.js',
                 ],
                 mergeFile: 'tender_list_progress',
             },
             manage: {
                 files: [
-                    "/public/js/ztree/jquery.ztree.core.js",
-                    "/public/js/ztree/jquery.ztree.exedit.js",
-                    "/public/js/moment/moment.min.js",
-                    "/public/js/decimal.min.js",
+                    '/public/js/ztree/jquery.ztree.core.js',
+                    '/public/js/ztree/jquery.ztree.exedit.js',
+                    '/public/js/moment/moment.min.js',
+                    '/public/js/decimal.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/zh_calc.js",
-                    "/public/js/shares/tender_list_order.js",
-                    "/public/js/tender_showhide.js",
-                    "/public/js/tender_list_manage.js",
+                    '/public/js/zh_calc.js',
+                    '/public/js/shares/tender_list_order.js',
+                    '/public/js/tender_showhide.js',
+                    '/public/js/tender_list_manage.js',
                 ],
                 mergeFile: 'tender_list_manage',
             },
             tenderInfo: {
                 files: [
-                    "/public/js/echarts/echarts.min.js",
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
+                    '/public/js/echarts/echarts.min.js',
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/zh_calc.js",
-                    //"/public/js/tender.js",
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/zh_calc.js',
+                    // "/public/js/tender.js",
                 ],
                 mergeFile: 'tender',
-            }
+            },
         },
         ledger: {
             explode: {
                 files: [
-                    "/public/js/js-xlsx/xlsx.full.min.js",
-                    "/public/js/js-xlsx/xlsx.utils.js",
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
-                    "/public/js/math.min.js",
-                    "/public/js/file-saver/FileSaver.js",
-                    "/public/js/shares/export_excel.js",
-                ],
-                mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/sjs_setting.js",
-                    "/public/js/shares/cs_tools.js",
-                    "/public/js/shares/merge_peg.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/ledger_tree_col.js",
-                    "/public/js/std_lib.js",
-                    "/public/js/ledger.js",
+                    '/public/js/js-xlsx/xlsx.full.min.js',
+                    '/public/js/js-xlsx/xlsx.utils.js',
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/math.min.js',
+                    '/public/js/file-saver/FileSaver.js',
+                    '/public/js/shares/export_excel.js',
+                ],
+                mergeFiles: [
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/shares/merge_peg.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/ledger_tree_col.js',
+                    '/public/js/std_lib.js',
+                    '/public/js/ledger.js',
                 ],
                 mergeFile: 'explode',
             },
             audit: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/sjs_setting.js",
-                    "/public/js/shares/cs_tools.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/ledger_audit.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/ledger_audit.js',
                 ],
                 mergeFile: 'ledger_audit',
             },
             bwtz: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
-                    "/public/js/file-saver/FileSaver.js",
-                    "/public/js/shares/export_excel.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/file-saver/FileSaver.js',
+                    '/public/js/shares/export_excel.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/sjs_setting.js",
-                    "/public/js/shares/cs_tools.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/shares/bills_pos_convert.js",
-                    "/public/js/ledger_bwtz.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/shares/bills_pos_convert.js',
+                    '/public/js/ledger_bwtz.js',
                 ],
                 mergeFile: 'ledger_bwtz',
             },
             gather: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
-                    "/public/js/file-saver/FileSaver.js",
-                    "/public/js/shares/export_excel.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/file-saver/FileSaver.js',
+                    '/public/js/shares/export_excel.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/gcl_gather.js",
-                    "/public/js/ledger_gather.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/gcl_gather.js',
+                    '/public/js/ledger_gather.js',
                 ],
                 mergeFile: 'ledger_gather',
             },
             revise: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/cs_tools.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/std_lib.js",
-                    "/public/js/revise.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/std_lib.js',
+                    '/public/js/revise.js',
                 ],
                 mergeFile: 'revise',
-            }
+            },
         },
         revise: {
             info: {
                 files: [
-                    "/public/js/js-xlsx/xlsx.full.min.js",
-                    "/public/js/js-xlsx/xlsx.utils.js",
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
-                    "/public/js/math.min.js",
+                    '/public/js/js-xlsx/xlsx.full.min.js',
+                    '/public/js/js-xlsx/xlsx.utils.js',
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/math.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/sjs_setting.js",
-                    "/public/js/shares/cs_tools.js",
-                    "/public/js/shares/merge_peg.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/std_lib.js",
-                    "/public/js/revise.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/shares/merge_peg.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/std_lib.js',
+                    '/public/js/revise.js',
                 ],
                 mergeFile: 'revise',
             },
             history: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/sjs_setting.js",
-                    "/public/js/shares/cs_tools.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/revise_history.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/revise_history.js',
                 ],
                 mergeFile: 'revise_history',
-            }
+            },
         },
         stage: {
             // 本期计量台账
             index: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
-                    "/public/js/math.min.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/math.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/msg_box.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/sjs_setting.js",
-                    "/public/js/shares/cs_tools.js",
-                    "/public/js/shares/merge_peg.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/stage_im.js",
-                    "/public/js/stage.js",
-                    "/public/js/stage_audit.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/msg_box.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/shares/merge_peg.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/stage_im.js',
+                    '/public/js/stage.js',
+                    '/public/js/stage_audit.js',
                 ],
                 mergeFile: 'stage',
             },
             detail: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
-                    "/public/js/html2canvas/html2canvas.min.js",
-                    "/public/js/html2canvas/canvas2image.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/html2canvas/html2canvas.min.js',
+                    '/public/js/html2canvas/canvas2image.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/shares/cs_tools.js",
-                    "/public/js/stage_im.js",
-                    "/public/js/stage_detail.js",
-                    "/public/js/stage_audit.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/stage_im.js',
+                    '/public/js/stage_detail.js',
+                    '/public/js/stage_audit.js',
                 ],
                 mergeFile: 'stage_detail',
             },
             pay: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/math.min.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/math.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/cs_tools.js",
-                    "/public/js/stage_pay.js",
-                    "/public/js/stage_audit.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/stage_pay.js',
+                    '/public/js/stage_audit.js',
                 ],
                 mergeFile: 'stage_pay',
             },
             bwtz: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
-                    "/public/js/file-saver/FileSaver.js",
-                    "/public/js/shares/export_excel.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/file-saver/FileSaver.js',
+                    '/public/js/shares/export_excel.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/sjs_setting.js",
-                    "/public/js/shares/cs_tools.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/shares/bills_pos_convert.js",
-                    "/public/js/stage_bwtz.js",
-                    "/public/js/stage_audit.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/shares/bills_pos_convert.js',
+                    '/public/js/stage_bwtz.js',
+                    '/public/js/stage_audit.js',
                 ],
                 mergeFile: 'stage_bwtz',
             },
             change: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/cs_tools.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/stage_change.js",
-                    "/public/js/stage_audit.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/stage_change.js',
+                    '/public/js/stage_audit.js',
                 ],
                 mergeFile: 'stage_change',
             },
             gather: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
-                    "/public/js/file-saver/FileSaver.js",
-                    "/public/js/shares/export_excel.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/file-saver/FileSaver.js',
+                    '/public/js/shares/export_excel.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/cs_tools.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/gcl_gather.js",
-                    "/public/js/stage_gather.js",
-                    "/public/js/stage_audit.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/gcl_gather.js',
+                    '/public/js/stage_gather.js',
+                    '/public/js/stage_audit.js',
                 ],
                 mergeFile: 'stage_gather',
             },
             compare: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
-                    "/public/js/file-saver/FileSaver.js",
-                    "/public/js/shares/export_excel.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/file-saver/FileSaver.js',
+                    '/public/js/shares/export_excel.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/sjs_setting.js",
-                    "/public/js/shares/cs_tools.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/stage_compare.js",
-                    "/public/js/stage_audit.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/stage_compare.js',
+                    '/public/js/stage_audit.js',
                 ],
                 mergeFile: 'stage_compare',
-            }
+            },
+            manager: {
+                files: [
+                    '/public/js/decimal.min.js',
+                ],
+                mergeFiles: [
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/stage_audit.js',
+                ],
+                mergeFile: 'stage_manager',
+            },
         },
         stageExtra: {
             jgcl: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
-                    "/public/js/file-saver/FileSaver.js",
-                    "/public/js/shares/export_excel.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/file-saver/FileSaver.js',
+                    '/public/js/shares/export_excel.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/se_jgcl.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/se_jgcl.js',
                 ],
                 mergeFile: 'se_jgcl',
             },
             bonus: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
-                    "/public/js/datepicker/datepicker.min.js",
-                    "/public/js/datepicker/datepicker.zh.js",
-                    "/public/js/file-saver/FileSaver.js",
-                    "/public/js/shares/export_excel.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/datepicker/datepicker.min.js',
+                    '/public/js/datepicker/datepicker.zh.js',
+                    '/public/js/file-saver/FileSaver.js',
+                    '/public/js/shares/export_excel.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/se_bonus.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/se_bonus.js',
                 ],
                 mergeFile: 'se_bonus',
             },
             other: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
-                    "/public/js/datepicker/datepicker.min.js",
-                    "/public/js/datepicker/datepicker.zh.js",
-                    "/public/js/file-saver/FileSaver.js",
-                    "/public/js/shares/export_excel.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/datepicker/datepicker.min.js',
+                    '/public/js/datepicker/datepicker.zh.js',
+                    '/public/js/file-saver/FileSaver.js',
+                    '/public/js/shares/export_excel.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/se_other.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/se_other.js',
                 ],
                 mergeFile: 'se_other',
             },
@@ -466,91 +482,101 @@ const JsFiles = {
         measure: {
             compare: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
-                    "/public/js/file-saver/FileSaver.js",
-                    "/public/js/shares/export_excel.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/file-saver/FileSaver.js',
+                    '/public/js/shares/export_excel.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/sjs_setting.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/measure_compare.js"
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/measure_compare.js',
                 ],
                 mergeFile: 'measure_compare',
-            }
+            },
         },
         material: {
             info: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
-                    "/public/js/toastr.min.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/toastr.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/material.js",
-                    "/public/js/material_audit.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/material.js',
+                    '/public/js/material_audit.js',
                 ],
                 mergeFile: 'material',
             },
             list: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
-                    "/public/js/toastr.min.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/toastr.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/gcl_gather.js",
-                    "/public/js/material_list.js",
-                    "/public/js/material_audit.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/gcl_gather.js',
+                    '/public/js/material_list.js',
+                    '/public/js/material_audit.js',
                 ],
                 mergeFile: 'material_list',
             },
+            file: {
+                files: [
+                    '/public/js/toastr.min.js',
+                ],
+                mergeFiles: [
+                    '/public/js/sub_menu.js',
+                    '/public/js/material_file.js',
+                ],
+                mergeFile: 'material_file',
+            },
         },
         compare: {
             tz: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/sjs_setting.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/compare_tz.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/compare_tz.js',
                 ],
                 mergeFile: 'compare_tz',
             },
             stage: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/sjs_setting.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/compare_stage.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/compare_stage.js',
                 ],
                 mergeFile: 'compare_stage',
             },
@@ -558,33 +584,33 @@ const JsFiles = {
         gather: {
             tz: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/sjs_setting.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/gather_tz.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/gather_tz.js',
                 ],
                 mergeFile: 'gather_tz',
             },
             stage: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/sjs_setting.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/gather_stage.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/gather_stage.js',
                 ],
                 mergeFile: 'gather_stage',
             },
@@ -592,39 +618,39 @@ const JsFiles = {
         tools: {
             checkTz: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/sub_menu.js",
-                    "/public/js/div_resizer.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/sjs_setting.js",
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/spss_check_tz.js",
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/spss_check_tz.js',
                 ],
                 mergeFile: 'spss_check_tz',
-            }
+            },
         },
         report: {
             main: {
                 files: [
-                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
-                    "/public/js/decimal.min.js",
-                    "/public/js/moment/moment.min.js",
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/moment/moment.min.js',
                 ],
                 mergeFiles: [
-                    "/public/js/zh_calc.js",
-                    "/public/js/path_tree.js",
-                    "/public/js/spreadjs_rela/spreadjs_zh.js",
-                    "/public/js/shares/tenders2tree.js",
-                    "/public/report/js/rpt_custom.js",
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/tenders2tree.js',
+                    '/public/report/js/rpt_custom.js',
                 ],
                 mergeFile: 'report_main',
-            }
-        }
-    }
+            },
+        },
+    },
 
 };
 

+ 1 - 0
package.json

@@ -20,6 +20,7 @@
     "egg-validate": "^1.0.0",
     "egg-view": "^1.1.2",
     "egg-view-ejs": "^1.1.0",
+    "egg-wechat-all": "^0.1.4",
     "gt3-sdk": "^2.0.0",
     "gulp": "^4.0.0",
     "js-xlsx": "^0.8.22",

+ 12 - 0
sql/update.sql

@@ -80,3 +80,15 @@ ADD COLUMN `pre_sf_tp`  decimal(24,8) NULL DEFAULT NULL COMMENT '截止上期实
 -- ----------------------------
 -- 以上于2020-6-15 16:40 因测试需求更新至uat,尚未更新prod,如需增加sql,请一定在其后添加
 -- ----------------------------
+
+ALTER TABLE `zh_stage_detail`
+ADD COLUMN `calc_img_remark` TEXT NULL DEFAULT NULL COMMENT '草图备注' AFTER `custom_define`;
+
+ALTER TABLE `zh_pos`
+ADD COLUMN `real_qty`  decimal(24,8) NULL DEFAULT 0 COMMENT '实际使用数量' AFTER `qtcl_expr`;
+
+ALTER TABLE `calculation`.`zh_project`
+ADD COLUMN `page_path` INT NOT NULL DEFAULT 0 AFTER `page_show`;
+
+ALTER TABLE `calculation`.`zh_stage_attachment`
+ADD COLUMN `re_upload` INT NOT NULL DEFAULT 0 COMMENT '是否为审核通过后再次上传的文件,0为否' AFTER `in_time`;

+ 10 - 1
test/app/extend/helper.test.js

@@ -142,5 +142,14 @@ describe('test/app/extend/helper.test.js', () => {
         const id = ['abc', 'efj'];
         const str = 'And id in (' + ctx.helper.getInArrStrSqlFilter(id) + ')';
         assert(str === "And id in ('abc','efj')");
-    })
+    });
+
+    it('test getChapterCode', function () {
+        const ctx = app.mockContext();
+        const testData = ['A101-1-a', 'A201B102-1', '新增203-1', '新增1001-1'];
+        const targetData = ['100', '10000', '200', '1000'];
+        for (const i in testData) {
+            assert(ctx.helper.getChapterCode(testData[i]) === targetData[i]);
+        }
+    });
 });

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

@@ -155,77 +155,6 @@ describe('test/app/service/report_memory.test.js', () => {
         ]);
         if (mainData instanceof Array) {
             yield ctx.helper.saveBufferFile(JSON.stringify(mainData,"","\t"), path.join(savePath, 'mem_stage_bills.json'));
-            const tableDefine = {};
-            tableDefine.Name = '期 - 清单数据表(mem_stage_bills)';
-            tableDefine.remark = '';
-            tableDefine.ID = 25;
-            tableDefine.key = 'mem_stage_bills';
-            tableDefine.items = [];
-            addFields(tableDefine, '台账ID', 'id', dataType.int);
-            addFields(tableDefine, '标段ID', 'tender_id', dataType.int);
-            addFields(tableDefine, '树结构-ID', 'ledger_id', dataType.int);
-            addFields(tableDefine, '树结构-父项ID', 'ledger_pid', dataType.int);
-            addFields(tableDefine, '树结构-层级', 'level', dataType.int);
-            addFields(tableDefine, '树结构-同层排序', 'order', dataType.int);
-            addFields(tableDefine, '树结构-完整路径', 'full_path', dataType.int);
-            addFields(tableDefine, '树结构-是否子项', 'is_leaf', dataType.int); // 8
-
-            addFields(tableDefine, '项目节编号', 'code', dataType.str);
-            addFields(tableDefine, '清单编号', 'b_code', dataType.str);
-            addFields(tableDefine, '名称', 'name', dataType.str);
-            addFields(tableDefine, '单位', 'unit', dataType.str); // 12
-            addFields(tableDefine, '单价', 'unit_price', dataType.currency, {type: 'up'});
-
-            addFields(tableDefine, '签约-数量', 'deal_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '签约-金额', 'deal_tp', dataType.currency, {type: 'tp'});
-
-            addFields(tableDefine, '施工复核-数量', 'sgfh_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '施工复核-金额', 'sgfh_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, '设计错漏-数量', 'sjcl_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '设计错漏-金额', 'sjcl_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, '其他错漏-数量', 'qtcl_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '其他错漏-金额', 'qtcl_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, '台账-数量', 'quantity', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '台账-金额', 'total_price', dataType.currency, {type: 'tp'});
-
-            addFields(tableDefine, '项目节-数量1', 'dgn_qty1', dataType.currency);
-            addFields(tableDefine, '项目节-金额2', 'dgn_qty2', dataType.currency);
-
-            addFields(tableDefine, '图册号', 'drawing_code', dataType.str);
-            addFields(tableDefine, '备注', 'memo', dataType.str);
-            addFields(tableDefine, '节点类型', 'node_type', dataType.int);
-            addFields(tableDefine, '总额计量', 'is_tp', dataType.int);
-
-            addFields(tableDefine, '本期-合同-数量', 'contract_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '本期-合同-金额', 'contract_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, '本期-数量变更-数量', 'qc_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '本期-数量变更-金额', 'qc_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, '本期-完成-数量', 'gather_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '本期-完成-金额', 'gather_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, '本期批注', 'postil', dataType.str);
-
-            addFields(tableDefine, '截止上期-合同-数量', 'pre_contract_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '截止上期-合同-金额', 'pre_contract_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, '截止上期-数量变更-数量', 'pre_qc_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '截止上期-数量变更-金额', 'pre_qc_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, '截止上期-完成-数量', 'pre_gather_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '截止上期-完成-金额', 'pre_gather_tp', dataType.currency, {type: 'tp'});
-
-            addFields(tableDefine, '截止本期-合同-数量', 'end_contract_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '截止本期-合同-金额', 'end_contract_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, '截止本期-数量变更-数量', 'end_qc_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '截止本期-数量变更-金额', 'end_qc_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, '截止本期-完成-数量', 'end_gather_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '截止本期-完成-金额', 'end_gather_tp', dataType.currency, {type: 'tp'});
-
-            addFields(tableDefine, '(台账 + 截止本期变更)-金额', 'final_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, '截止本期完成率(%)', 'final_ratio', dataType.double);
-
-            addFields(tableDefine, '本期-数量变更-变更令', 'qc_bgl_code', dataType.str);
-
-            addFields(tableDefine, '章节编号', 'chapter', dataType.str);
-
-            yield saveTableDefine(ctx, tableDefine, path.join(savePath, 'mem_stage_bills_define.json'));
         }
     });
     // 期部位明细数据
@@ -258,67 +187,6 @@ describe('test/app/service/report_memory.test.js', () => {
         ]);
         if (mainData instanceof Array) {
             yield ctx.helper.saveBufferFile(JSON.stringify(mainData,"","\t"), path.join(savePath, 'mem_stage_bills_compare.json'));
-
-            const tableDefine = {};
-            tableDefine.Name = '期-清单-全参与人数据表(mem_stage_bills_compare)';
-            tableDefine.remark = '';
-            tableDefine.ID = 26;
-            tableDefine.key = 'mem_stage_bills_compare';
-            tableDefine.items = [];
-            addFields(tableDefine, '台账ID', 'id', dataType.int);
-            addFields(tableDefine, '标段ID', 'tender_id', dataType.int);
-            addFields(tableDefine, '树结构-ID', 'ledger_id', dataType.int);
-            addFields(tableDefine, '树结构-父项ID', 'ledger_pid', dataType.int);
-            addFields(tableDefine, '树结构-层级', 'level', dataType.int);
-            addFields(tableDefine, '树结构-同层排序', 'order', dataType.int);
-            addFields(tableDefine, '树结构-完整路径', 'full_path', dataType.int);
-            addFields(tableDefine, '树结构-是否子项', 'is_leaf', dataType.int); // 8
-
-            addFields(tableDefine, '项目节编号', 'code', dataType.str);
-            addFields(tableDefine, '清单编号', 'b_code', dataType.str);
-            addFields(tableDefine, '名称', 'name', dataType.str);
-            addFields(tableDefine, '单位', 'unit', dataType.str); // 12
-            addFields(tableDefine, '单价', 'unit_price', dataType.currency, {type: 'up'});
-
-            addFields(tableDefine, '签约-数量', 'deal_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '签约-金额', 'deal_tp', dataType.currency, {type: 'tp'});
-
-            addFields(tableDefine, '施工复核-数量', 'sgfh_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '施工复核-金额', 'sgfh_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, '设计错漏-数量', 'sjcl_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '设计错漏-金额', 'sjcl_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, '其他错漏-数量', 'qtcl_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '其他错漏-金额', 'qtcl_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, '台账-数量', 'quantity', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '台账-金额', 'total_price', dataType.currency, {type: 'tp'});
-
-            addFields(tableDefine, '项目节-数量1', 'dgn_qty1', dataType.currency);
-            addFields(tableDefine, '项目节-数量2', 'dgn_qty2', dataType.currency);
-
-            addFields(tableDefine, '图册号', 'drawing_code', dataType.str);
-            addFields(tableDefine, '备注', 'memo', dataType.str);
-            addFields(tableDefine, '节点类型', 'node_type', dataType.int);
-            addFields(tableDefine, '总额计量', 'is_tp', dataType.int);
-
-            addFields(tableDefine, '截止上期-合同-数量', 'pre_contract_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '截止上期-合同-金额', 'pre_contract_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, '截止上期-数量变更-数量', 'pre_qc_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '截止上期-数量变更-金额', 'pre_qc_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, '截止上期-完成-数量', 'pre_gather_qty', dataType.currency, {type: 'qty', unitKey: 12});
-            addFields(tableDefine, '截止上期-完成-金额', 'pre_gather_tp', dataType.currency, {type: 'tp'});
-
-            for (let i = 0 ;i < 10; i++) {
-                addFields(tableDefine, '本期-合同-数量' + '_' + i, 'r' + i +'_' + 'contract_qty', dataType.currency, {type: 'qty', unitKey: 12});
-                addFields(tableDefine, '本期-合同-金额' + '_' + i, 'r' + i +'_' + 'contract_tp', dataType.currency, {type: 'tp'});
-                addFields(tableDefine, '本期-数量变更-数量' + '_' + i, 'r' + i +'_' + 'qc_qty', dataType.currency, {type: 'qty', unitKey: 12});
-                addFields(tableDefine, '本期-数量变更-金额' + '_' + i, 'r' + i +'_' + 'qc_tp', dataType.currency, {type: 'tp'});
-                addFields(tableDefine, '本期-完成-数量' + '_' + i, 'r' + i +'_' + 'gather_qty', dataType.currency, {type: 'qty', unitKey: 12});
-                addFields(tableDefine, '本期-完成-金额' + '_' + i, 'r' + i +'_' + 'gather_tp', dataType.currency, {type: 'tp'});
-            }
-
-            addFields(tableDefine, '章节编号', 'chapter', dataType.str);
-
-            yield saveTableDefine(ctx, tableDefine, path.join(savePath, 'mem_stage_bills_compare_define.json'));
         }
     });
     it('test getStagePay', function* () {
@@ -328,29 +196,6 @@ describe('test/app/service/report_memory.test.js', () => {
         const data = yield ctx.service.reportMemory.getStagePayData(stage.tid, stage.id, ['r0_tp', 'r1_tp']);
         if (data instanceof Array) {
             yield ctx.helper.saveBufferFile(JSON.stringify(data, '', '\t'), path.join(savePath, 'mem_stage_pay.json'));
-
-            const tableDefine = {};
-            tableDefine.Name = '期-合同支付-数据(mem_stage_pay)';
-            tableDefine.remark = '';
-            tableDefine.ID = 27;
-            tableDefine.key = 'mem_stage_pay';
-            tableDefine.items = [];
-            const prefix = '期-合同支付-';
-
-            addFields(tableDefine, prefix + '名称', 'name', dataType.str);
-            addFields(tableDefine, prefix + '计算公式', 'expr', dataType.str);
-            addFields(tableDefine, prefix + '金额', 'tp', dataType.currency, {type: 'tp', subType: 'pay'});
-            addFields(tableDefine, prefix + '截止上期-金额', 'pre_tp', dataType.currency, {type: 'tp', subType: 'pay'});
-            addFields(tableDefine, prefix + '截止本期-金额', 'end_tp', dataType.currency, {type: 'tp', subType: 'pay'});
-            addFields(tableDefine, prefix + '排序', 'order', dataType.int);
-            addFields(tableDefine, prefix + '是否扣款项', 'minus', dataType.int);
-            addFields(tableDefine, prefix + '支付类型', 'ptype', dataType.int);
-            addFields(tableDefine, prefix + '起扣金额', 'sprice', dataType.currency, {type: 'tp', subType: 'pay'});
-            addFields(tableDefine, prefix + '扣款限额', 'rprice', dataType.currency, {type: 'tp', subType: 'pay'});
-            for (let i = 0 ;i < 10; i++) {
-                addFields(tableDefine, prefix + '流程-本期-金额_' + i, 'r' + i + '_tp', dataType.currency, {type: 'tp', subType: 'pay'});
-            }
-            yield saveTableDefine(ctx, tableDefine, path.join(savePath, 'mem_stage_pay_define.json'));
         }
     });
     it('test mem_union_data', function* () {
@@ -412,30 +257,6 @@ describe('test/app/service/report_memory.test.js', () => {
         const data = yield ctx.service.reportMemory.getChangeBillsData(12);
         if (data instanceof Array) {
             yield ctx.helper.saveBufferFile(JSON.stringify(data, '', '\t'), path.join(savePath, 'mem_change_bills.json'));
-
-            const tableDefine = {};
-            tableDefine.Name = '变更清单-(mem_change_bills)';
-            tableDefine.remark = '';
-            tableDefine.ID = 29;
-            tableDefine.key = 'mem_change_bills';
-            tableDefine.items = [];
-            const prefix = '变更清单-';
-
-            addFields(tableDefine, prefix + '变更令id', 'cid', dataType.str);
-            addFields(tableDefine, prefix + '签约清单id或台账id', 'lid', dataType.str);
-            addFields(tableDefine, prefix + '清单编号', 'code', dataType.str);
-            addFields(tableDefine, prefix + '名称', 'name', dataType.str);
-            addFields(tableDefine, prefix + '单位', 'unit', dataType.str);
-            addFields(tableDefine, prefix + '单价', 'unit_price', dataType.currency, {type: 'up'});
-            addFields(tableDefine, prefix + '原数量', 'o_qty', dataType.currency, {type: 'qty', unitKey: 5});
-            addFields(tableDefine, prefix + '变更数量', 'c_qty', dataType.currency, {type: 'qty', unitKey: 5});
-            addFields(tableDefine, prefix + '审批变更后数量', 's_qty', dataType.currency, {type: 'qty', unitKey: 5});
-            addFields(tableDefine, prefix + '原-金额', 'o_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, prefix + '变更-金额', 'c_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, prefix + '审批变更后-金额', 's_tp', dataType.currency, {type: 'tp'});
-            addFields(tableDefine, prefix + '部位明细', 'bwmx', dataType.str);
-            addFields(tableDefine, prefix + '变更详情', 'detail', dataType.str);
-            yield saveTableDefine(ctx, tableDefine, path.join(savePath, 'mem_change_bills_define.json'));
         }
     });
 });

+ 0 - 0
test/app/service/rpt_gather_memory.test.js


部分文件因为文件数量过多而无法显示