浏览代码

Merge branch 'master' of http://192.168.1.41:3000/SmartCost/ConstructionOperation

Conflicts:
	modules/reports/rpt_component/jpc_flow_tab.js
TonyKang 5 年之前
父节点
当前提交
8a6aa1ebb0
共有 100 个文件被更改,包括 14806 次插入311 次删除
  1. 3 1
      .gitignore
  2. 2 2
      Dockerfile
  3. 3 2
      Dockerfile_pp
  4. 4 2
      Dockerfile_qa
  5. 17 2
      config/config.js
  6. 3 1
      config/db/db_manager.js
  7. 3 3
      config/menu.js
  8. 347 0
      lib/codemirror/codemirror.css
  9. 8935 0
      lib/codemirror/codemirror.js
  10. 825 0
      lib/codemirror/css.js
  11. 41 0
      lib/codemirror/dracula.css
  12. 743 0
      lib/codemirror/javascript.js
  13. 54 0
      lib/codemirror/material.css
  14. 394 0
      lib/codemirror/xml.js
  15. 67 0
      lib/font-zy/iconfont.css
  16. 二进制
      lib/font-zy/iconfont.eot
  17. 101 0
      lib/font-zy/iconfont.svg
  18. 二进制
      lib/font-zy/iconfont.ttf
  19. 二进制
      lib/font-zy/iconfont.woff
  20. 486 0
      lib/json/json2.js
  21. 1 0
      lib/lz-string/lz-string.min.js
  22. 2 1
      modules/all_models/bills_template_items.js
  23. 17 0
      modules/all_models/compilation.js
  24. 37 0
      modules/all_models/compleRation_coe.js
  25. 29 0
      modules/all_models/compleRation_installFeeItem.js
  26. 42 0
      modules/all_models/compleRation_installSection.js
  27. 34 0
      modules/all_models/comple_section_template.js
  28. 1 1
      modules/all_models/counter.js
  29. 18 8
      modules/all_models/engineering_lib.js
  30. 1 0
      modules/all_models/manager.js
  31. 53 0
      modules/all_models/material_replace_lib.js
  32. 22 0
      modules/all_models/project_feature_lib.js
  33. 12 1
      modules/all_models/rpt_template.js
  34. 1 1
      modules/all_models/stdBills_bills.js
  35. 0 2
      modules/all_models/stdBills_lib.js
  36. 11 2
      modules/all_models/stdGlj_glj.js
  37. 2 0
      modules/all_models/stdRation_coe.js
  38. 4 1
      modules/all_models/stdRation_ration.js
  39. 1 0
      modules/all_models/std_billsGuidance_items.js
  40. 1 0
      modules/all_models/std_billsGuidance_lib.js
  41. 7 3
      modules/common/std/schemas/std_calc_program.js
  42. 8 3
      modules/common/std/schemas/std_fee_rate_libs.js
  43. 12 1
      modules/all_models/user.js
  44. 6 0
      modules/bills_lib/controllers/bills_lib_controllers.js
  45. 116 0
      modules/bills_lib/controllers/bills_permissionController.js
  46. 4 4
      modules/bills_lib/controllers/views_permissionController.js
  47. 34 14
      modules/bills_lib/models/bills_lib_interfaces.js
  48. 47 40
      modules/bills_lib/routes/bills_lib_routes.js
  49. 5 1
      modules/bills_template_lib/controllers/bills_template_controller.js
  50. 2 0
      modules/bills_template_lib/facade/bills_template_facade.js
  51. 100 0
      modules/calc_program_lib/controllers/calc_program_controller.js
  52. 54 0
      modules/calc_program_lib/facade/calc_program_facade.js
  53. 19 0
      modules/calc_program_lib/routes/calc_program_routes.js
  54. 4 0
      modules/common/base/base_model.js
  55. 6 3
      modules/common/const/bills_fixed.js
  56. 67 0
      modules/common/const/category_const.js
  57. 1 0
      modules/common/helper/mongoose_helper.js
  58. 2 2
      modules/common/std/std_bills_lib_lists_model.js
  59. 2 2
      modules/common/std/std_calc_program_model.js
  60. 2 2
      modules/common/std/std_fee_rate_libs_model.js
  61. 100 0
      modules/fee_rate_lib/controllers/fee_rate_controller.js
  62. 50 0
      modules/fee_rate_lib/facade/fee_rate_facade.js
  63. 21 0
      modules/fee_rate_lib/routes/fee_rate_routes.js
  64. 28 8
      modules/main_col_lib/controllers/main_col_controller.js
  65. 1 0
      modules/main_col_lib/routes/main_col_routes.js
  66. 166 0
      modules/material_replace_lib/controllers/material_replace_controller.js
  67. 200 0
      modules/material_replace_lib/facade/material_replace_facade.js
  68. 24 0
      modules/material_replace_lib/routes/material_replace_router.js
  69. 99 0
      modules/project_feature_lib/controllers/project_feature_controller.js
  70. 38 0
      modules/project_feature_lib/facade/project_feature_facade.js
  71. 21 0
      modules/project_feature_lib/routes/project_feature_router.js
  72. 2 1
      modules/ration_repository/controllers/ration_controller.js
  73. 65 19
      modules/ration_repository/controllers/ration_repository_controller.js
  74. 35 7
      modules/ration_repository/controllers/repository_views_controller.js
  75. 270 24
      modules/ration_repository/models/ration_item.js
  76. 1 0
      modules/ration_repository/routes/ration_rep_routes.js
  77. 30 29
      modules/reports/controllers/rpt_tpl_controller.js
  78. 7 1
      modules/reports/facade/rpt_template_facade.js
  79. 9 1
      modules/reports/facade/rpt_tpl_data_facade.js
  80. 13 7
      modules/reports/facade/rpt_tpl_tree_node_facade.js
  81. 1 1
      modules/reports/routes/report_router_operation.js
  82. 9 3
      modules/reports/routes/rpt_tpl_router.js
  83. 43 18
      modules/reports/rpt_component/helper/jpc_helper_common.js
  84. 32 1
      modules/reports/rpt_component/helper/jpc_helper_field.js
  85. 1 1
      modules/reports/rpt_component/jpc_bill_tab.js
  86. 3 3
      modules/reports/rpt_component/jpc_ex.js
  87. 152 0
      modules/reports/rpt_component/jpc_flow_tab.js
  88. 26 4
      modules/reports/rpt_component/jpc_rte.js
  89. 二进制
      modules/reports/util/pdf_base_files/Smart-italic.ttf
  90. 二进制
      modules/reports/util/pdf_base_files/Smart.ttf
  91. 二进制
      modules/reports/util/pdf_base_files/hwxsb.ttf
  92. 206 21
      modules/reports/util/rpt_construct_data_util.js
  93. 31 5
      modules/reports/util/rpt_excel_util.js
  94. 28 0
      modules/reports/util/rpt_font_util.js
  95. 105 41
      modules/reports/util/rpt_pdf_util.js
  96. 106 9
      modules/reports/util/rpt_svg_util.js
  97. 53 0
      modules/reports/util/rpt_tmp_file_sweep.js
  98. 12 0
      modules/std_billsGuidance_lib/controllers/libController.js
  99. 3 2
      modules/std_billsGuidance_lib/controllers/viewController.js
  100. 0 0
      modules/std_billsGuidance_lib/facade/facades.js

+ 3 - 1
.gitignore

@@ -2,4 +2,6 @@ node_modules/
 .git/
 dist/
 .idea/
-tmp/
+tmp/
+modules/reports/util/pdf_base_files/*.ttf
+modules/reports/util/pdf_base_files/*.ttc

+ 2 - 2
Dockerfile

@@ -1,8 +1,8 @@
-FROM operationbase:latest
+FROM operationbase:2.0
 
 WORKDIR /ConstructionOperation
 
-RUN git pull http://192.168.1.12:3000/SmartCost/ConstructionOperation master
+COPY . /ConstructionOperation
 
 RUN mkdir tmp
 

+ 3 - 2
Dockerfile_pp

@@ -1,9 +1,10 @@
-FROM operationbase:latest
+FROM operationbase:2.0
 
 WORKDIR /ConstructionOperation
 
-RUN git pull http://192.168.1.12:3000/SmartCost/ConstructionOperation master
+COPY . /ConstructionOperation
 
+RUN mkdir tmp
 
 RUN cnpm install
 

+ 4 - 2
Dockerfile_qa

@@ -1,8 +1,10 @@
-FROM operationbase:latest
+FROM operationbase:2.0
 
 WORKDIR /ConstructionOperation
 
-RUN git pull http://192.168.1.12:3000/SmartCost/ConstructionOperation master
+COPY .  /ConstructionOperation
+
+RUN mkdir tmp
 
 RUN cnpm install
 

文件差异内容过多而无法显示
+ 17 - 2
config/config.js


+ 3 - 1
config/db/db_manager.js

@@ -49,7 +49,9 @@ module.exports = {
     connect:function (env="local") {
         var config = require("../config.js");
         var dbURL = 'mongodb://' + config[env].server + ":" + config[env].port + '/scConstruct';
-        if(config[env].options){
+        if(config[env].dbURL){
+            mg.connect(config[env].dbURL,{connectTimeoutMS: 20000,useMongoClient: true});
+        } else if(config[env].options){
             mg.connect(dbURL,config[env].options);
         }else {
             mg.connect(dbURL,{connectTimeoutMS: 20000,useMongoClient: true});//useMongoClient': true*! //报 DeprecationWarning: `open()` is deprecated in mongoose这个错

+ 3 - 3
config/menu.js

@@ -22,12 +22,12 @@ let menuData = {
                 title: '普通用户',
                 url: '/user',
                 name: 'index',
-            },
-            'last-login' : {
+            }/*,
+            'test-user' : {
                 title: '测试用户',
                 url: '/user/test-user',
                 name: 'test-user',
-            }
+            }*/
         }
     },
     'notify': {

+ 347 - 0
lib/codemirror/codemirror.css

@@ -0,0 +1,347 @@
+/* BASICS */
+
+.CodeMirror {
+  /* Set height, width, borders, and global font properties here */
+  font-family: monospace;
+  height:300px;
+  color: black;
+}
+
+/* PADDING */
+
+.CodeMirror-lines {
+  padding: 4px 0; /* Vertical padding around content */
+}
+.CodeMirror pre {
+  padding: 0 4px; /* Horizontal padding of content */
+}
+
+.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+  background-color: white; /* The little square between H and V scrollbars */
+}
+
+/* GUTTER */
+
+.CodeMirror-gutters {
+  border-right: 1px solid #ddd;
+  background-color: #f7f7f7;
+  white-space: nowrap;
+}
+.CodeMirror-linenumbers {}
+.CodeMirror-linenumber {
+  padding: 0 3px 0 5px;
+  min-width: 20px;
+  text-align: right;
+  color: #999;
+  white-space: nowrap;
+}
+
+.CodeMirror-guttermarker { color: black; }
+.CodeMirror-guttermarker-subtle { color: #999; }
+
+/* CURSOR */
+
+.CodeMirror-cursor {
+  border-left: 1px solid black;
+  border-right: none;
+  width: 0;
+}
+/* Shown when moving in bi-directional text */
+.CodeMirror div.CodeMirror-secondarycursor {
+  border-left: 1px solid silver;
+}
+.cm-fat-cursor .CodeMirror-cursor {
+  width: auto;
+  border: 0 !important;
+  background: #7e7;
+}
+.cm-fat-cursor div.CodeMirror-cursors {
+  z-index: 1;
+}
+
+.cm-animate-fat-cursor {
+  width: auto;
+  border: 0;
+  -webkit-animation: blink 1.06s steps(1) infinite;
+  -moz-animation: blink 1.06s steps(1) infinite;
+  animation: blink 1.06s steps(1) infinite;
+  background-color: #7e7;
+}
+@-moz-keyframes blink {
+  0% {}
+  50% { background-color: transparent; }
+  100% {}
+}
+@-webkit-keyframes blink {
+  0% {}
+  50% { background-color: transparent; }
+  100% {}
+}
+@keyframes blink {
+  0% {}
+  50% { background-color: transparent; }
+  100% {}
+}
+
+/* Can style cursor different in overwrite (non-insert) mode */
+.CodeMirror-overwrite .CodeMirror-cursor {}
+
+.cm-tab { display: inline-block; text-decoration: inherit; }
+
+.CodeMirror-rulers {
+  position: absolute;
+  left: 0; right: 0; top: -50px; bottom: -20px;
+  overflow: hidden;
+}
+.CodeMirror-ruler {
+  border-left: 1px solid #ccc;
+  top: 0; bottom: 0;
+  position: absolute;
+}
+
+/* DEFAULT THEME */
+
+.cm-s-default .cm-header {color: blue;}
+.cm-s-default .cm-quote {color: #090;}
+.cm-negative {color: #d44;}
+.cm-positive {color: #292;}
+.cm-header, .cm-strong {font-weight: bold;}
+.cm-em {font-style: italic;}
+.cm-link {text-decoration: underline;}
+.cm-strikethrough {text-decoration: line-through;}
+
+.cm-s-default .cm-keyword {color: #708;}
+.cm-s-default .cm-atom {color: #219;}
+.cm-s-default .cm-number {color: #164;}
+.cm-s-default .cm-def {color: #00f;}
+.cm-s-default .cm-variable,
+.cm-s-default .cm-punctuation,
+.cm-s-default .cm-property,
+.cm-s-default .cm-operator {}
+.cm-s-default .cm-variable-2 {color: #05a;}
+.cm-s-default .cm-variable-3 {color: #085;}
+.cm-s-default .cm-comment {color: #a50;}
+.cm-s-default .cm-string {color: #a11;}
+.cm-s-default .cm-string-2 {color: #f50;}
+.cm-s-default .cm-meta {color: #555;}
+.cm-s-default .cm-qualifier {color: #555;}
+.cm-s-default .cm-builtin {color: #30a;}
+.cm-s-default .cm-bracket {color: #997;}
+.cm-s-default .cm-tag {color: #170;}
+.cm-s-default .cm-attribute {color: #00c;}
+.cm-s-default .cm-hr {color: #999;}
+.cm-s-default .cm-link {color: #00c;}
+
+.cm-s-default .cm-error {color: #f00;}
+.cm-invalidchar {color: #f00;}
+
+.CodeMirror-composing { border-bottom: 2px solid; }
+
+/* Default styles for common addons */
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
+.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
+.CodeMirror-activeline-background {background: #e8f2ff;}
+
+/* STOP */
+
+/* The rest of this file contains styles related to the mechanics of
+   the editor. You probably shouldn't touch them. */
+
+.CodeMirror {
+  position: relative;
+  overflow: hidden;
+  background: white;
+}
+
+.CodeMirror-scroll {
+  overflow: scroll !important; /* Things will break if this is overridden */
+  /* 30px is the magic margin used to hide the element's real scrollbars */
+  /* See overflow: hidden in .CodeMirror */
+  margin-bottom: -30px; margin-right: -30px;
+  padding-bottom: 30px;
+  height: 100%;
+  outline: none; /* Prevent dragging from highlighting the element */
+  position: relative;
+}
+.CodeMirror-sizer {
+  position: relative;
+  border-right: 30px solid transparent;
+}
+
+/* The fake, visible scrollbars. Used to force redraw during scrolling
+   before actual scrolling happens, thus preventing shaking and
+   flickering artifacts. */
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+  position: absolute;
+  z-index: 6;
+  display: none;
+}
+.CodeMirror-vscrollbar {
+  right: 0; top: 0;
+  overflow-x: hidden;
+  overflow-y: scroll;
+}
+.CodeMirror-hscrollbar {
+  bottom: 0; left: 0;
+  overflow-y: hidden;
+  overflow-x: scroll;
+}
+.CodeMirror-scrollbar-filler {
+  right: 0; bottom: 0;
+}
+.CodeMirror-gutter-filler {
+  left: 0; bottom: 0;
+}
+
+.CodeMirror-gutters {
+  position: absolute; left: 0; top: 0;
+  min-height: 100%;
+  z-index: 3;
+}
+.CodeMirror-gutter {
+  white-space: normal;
+  height: 100%;
+  display: inline-block;
+  vertical-align: top;
+  margin-bottom: -30px;
+  /* Hack to make IE7 behave */
+  *zoom:1;
+  *display:inline;
+}
+.CodeMirror-gutter-wrapper {
+  position: absolute;
+  z-index: 4;
+  background: none !important;
+  border: none !important;
+}
+.CodeMirror-gutter-background {
+  position: absolute;
+  top: 0; bottom: 0;
+  z-index: 4;
+}
+.CodeMirror-gutter-elt {
+  position: absolute;
+  cursor: default;
+  z-index: 4;
+}
+.CodeMirror-gutter-wrapper {
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  user-select: none;
+}
+
+.CodeMirror-lines {
+  cursor: text;
+  min-height: 1px; /* prevents collapsing before first draw */
+}
+.CodeMirror pre {
+  /* Reset some styles that the rest of the page might have set */
+  -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
+  border-width: 0;
+  background: transparent;
+  font-family: inherit;
+  font-size: inherit;
+  margin: 0;
+  white-space: pre;
+  word-wrap: normal;
+  line-height: inherit;
+  color: inherit;
+  z-index: 2;
+  position: relative;
+  overflow: visible;
+  -webkit-tap-highlight-color: transparent;
+  -webkit-font-variant-ligatures: none;
+  font-variant-ligatures: none;
+}
+.CodeMirror-wrap pre {
+  word-wrap: break-word;
+  white-space: pre-wrap;
+  word-break: normal;
+}
+
+.CodeMirror-linebackground {
+  position: absolute;
+  left: 0; right: 0; top: 0; bottom: 0;
+  z-index: 0;
+}
+
+.CodeMirror-linewidget {
+  position: relative;
+  z-index: 2;
+  overflow: auto;
+}
+
+.CodeMirror-widget {}
+
+.CodeMirror-code {
+  outline: none;
+}
+
+/* Force content-box sizing for the elements where we expect it */
+.CodeMirror-scroll,
+.CodeMirror-sizer,
+.CodeMirror-gutter,
+.CodeMirror-gutters,
+.CodeMirror-linenumber {
+  -moz-box-sizing: content-box;
+  box-sizing: content-box;
+}
+
+.CodeMirror-measure {
+  position: absolute;
+  width: 100%;
+  height: 0;
+  overflow: hidden;
+  visibility: hidden;
+}
+
+.CodeMirror-cursor {
+  position: absolute;
+  pointer-events: none;
+}
+.CodeMirror-measure pre { position: static; }
+
+div.CodeMirror-cursors {
+  visibility: hidden;
+  position: relative;
+  z-index: 3;
+}
+div.CodeMirror-dragcursors {
+  visibility: visible;
+}
+
+.CodeMirror-focused div.CodeMirror-cursors {
+  visibility: visible;
+}
+
+.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+.CodeMirror-crosshair { cursor: crosshair; }
+.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
+.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
+
+.cm-searching {
+  background: #ffa;
+  background: rgba(255, 255, 0, .4);
+}
+
+/* IE7 hack to prevent it from returning funny offsetTops on the spans */
+.CodeMirror span { *vertical-align: text-bottom; }
+
+/* Used to force a border model for a node */
+.cm-force-border { padding-right: .1px; }
+
+@media print {
+  /* Hide the cursor when printing */
+  .CodeMirror div.CodeMirror-cursors {
+    visibility: hidden;
+  }
+}
+
+/* See issue #2901 */
+.cm-tab-wrap-hack:after { content: ''; }
+
+/* Help users use markselection to safely style text background */
+span.CodeMirror-selectedtext { background: none; }

文件差异内容过多而无法显示
+ 8935 - 0
lib/codemirror/codemirror.js


+ 825 - 0
lib/codemirror/css.js

@@ -0,0 +1,825 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.defineMode("css", function(config, parserConfig) {
+  var inline = parserConfig.inline
+  if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
+
+  var indentUnit = config.indentUnit,
+      tokenHooks = parserConfig.tokenHooks,
+      documentTypes = parserConfig.documentTypes || {},
+      mediaTypes = parserConfig.mediaTypes || {},
+      mediaFeatures = parserConfig.mediaFeatures || {},
+      mediaValueKeywords = parserConfig.mediaValueKeywords || {},
+      propertyKeywords = parserConfig.propertyKeywords || {},
+      nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
+      fontProperties = parserConfig.fontProperties || {},
+      counterDescriptors = parserConfig.counterDescriptors || {},
+      colorKeywords = parserConfig.colorKeywords || {},
+      valueKeywords = parserConfig.valueKeywords || {},
+      allowNested = parserConfig.allowNested,
+      supportsAtComponent = parserConfig.supportsAtComponent === true;
+
+  var type, override;
+  function ret(style, tp) { type = tp; return style; }
+
+  // Tokenizers
+
+  function tokenBase(stream, state) {
+    var ch = stream.next();
+    if (tokenHooks[ch]) {
+      var result = tokenHooks[ch](stream, state);
+      if (result !== false) return result;
+    }
+    if (ch == "@") {
+      stream.eatWhile(/[\w\\\-]/);
+      return ret("def", stream.current());
+    } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
+      return ret(null, "compare");
+    } else if (ch == "\"" || ch == "'") {
+      state.tokenize = tokenString(ch);
+      return state.tokenize(stream, state);
+    } else if (ch == "#") {
+      stream.eatWhile(/[\w\\\-]/);
+      return ret("atom", "hash");
+    } else if (ch == "!") {
+      stream.match(/^\s*\w*/);
+      return ret("keyword", "important");
+    } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
+      stream.eatWhile(/[\w.%]/);
+      return ret("number", "unit");
+    } else if (ch === "-") {
+      if (/[\d.]/.test(stream.peek())) {
+        stream.eatWhile(/[\w.%]/);
+        return ret("number", "unit");
+      } else if (stream.match(/^-[\w\\\-]+/)) {
+        stream.eatWhile(/[\w\\\-]/);
+        if (stream.match(/^\s*:/, false))
+          return ret("variable-2", "variable-definition");
+        return ret("variable-2", "variable");
+      } else if (stream.match(/^\w+-/)) {
+        return ret("meta", "meta");
+      }
+    } else if (/[,+>*\/]/.test(ch)) {
+      return ret(null, "select-op");
+    } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
+      return ret("qualifier", "qualifier");
+    } else if (/[:;{}\[\]\(\)]/.test(ch)) {
+      return ret(null, ch);
+    } else if ((ch == "u" && stream.match(/rl(-prefix)?\(/)) ||
+               (ch == "d" && stream.match("omain(")) ||
+               (ch == "r" && stream.match("egexp("))) {
+      stream.backUp(1);
+      state.tokenize = tokenParenthesized;
+      return ret("property", "word");
+    } else if (/[\w\\\-]/.test(ch)) {
+      stream.eatWhile(/[\w\\\-]/);
+      return ret("property", "word");
+    } else {
+      return ret(null, null);
+    }
+  }
+
+  function tokenString(quote) {
+    return function(stream, state) {
+      var escaped = false, ch;
+      while ((ch = stream.next()) != null) {
+        if (ch == quote && !escaped) {
+          if (quote == ")") stream.backUp(1);
+          break;
+        }
+        escaped = !escaped && ch == "\\";
+      }
+      if (ch == quote || !escaped && quote != ")") state.tokenize = null;
+      return ret("string", "string");
+    };
+  }
+
+  function tokenParenthesized(stream, state) {
+    stream.next(); // Must be '('
+    if (!stream.match(/\s*[\"\')]/, false))
+      state.tokenize = tokenString(")");
+    else
+      state.tokenize = null;
+    return ret(null, "(");
+  }
+
+  // Context management
+
+  function Context(type, indent, prev) {
+    this.type = type;
+    this.indent = indent;
+    this.prev = prev;
+  }
+
+  function pushContext(state, stream, type, indent) {
+    state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
+    return type;
+  }
+
+  function popContext(state) {
+    if (state.context.prev)
+      state.context = state.context.prev;
+    return state.context.type;
+  }
+
+  function pass(type, stream, state) {
+    return states[state.context.type](type, stream, state);
+  }
+  function popAndPass(type, stream, state, n) {
+    for (var i = n || 1; i > 0; i--)
+      state.context = state.context.prev;
+    return pass(type, stream, state);
+  }
+
+  // Parser
+
+  function wordAsValue(stream) {
+    var word = stream.current().toLowerCase();
+    if (valueKeywords.hasOwnProperty(word))
+      override = "atom";
+    else if (colorKeywords.hasOwnProperty(word))
+      override = "keyword";
+    else
+      override = "variable";
+  }
+
+  var states = {};
+
+  states.top = function(type, stream, state) {
+    if (type == "{") {
+      return pushContext(state, stream, "block");
+    } else if (type == "}" && state.context.prev) {
+      return popContext(state);
+    } else if (supportsAtComponent && /@component/.test(type)) {
+      return pushContext(state, stream, "atComponentBlock");
+    } else if (/^@(-moz-)?document$/.test(type)) {
+      return pushContext(state, stream, "documentTypes");
+    } else if (/^@(media|supports|(-moz-)?document|import)$/.test(type)) {
+      return pushContext(state, stream, "atBlock");
+    } else if (/^@(font-face|counter-style)/.test(type)) {
+      state.stateArg = type;
+      return "restricted_atBlock_before";
+    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
+      return "keyframes";
+    } else if (type && type.charAt(0) == "@") {
+      return pushContext(state, stream, "at");
+    } else if (type == "hash") {
+      override = "builtin";
+    } else if (type == "word") {
+      override = "tag";
+    } else if (type == "variable-definition") {
+      return "maybeprop";
+    } else if (type == "interpolation") {
+      return pushContext(state, stream, "interpolation");
+    } else if (type == ":") {
+      return "pseudo";
+    } else if (allowNested && type == "(") {
+      return pushContext(state, stream, "parens");
+    }
+    return state.context.type;
+  };
+
+  states.block = function(type, stream, state) {
+    if (type == "word") {
+      var word = stream.current().toLowerCase();
+      if (propertyKeywords.hasOwnProperty(word)) {
+        override = "property";
+        return "maybeprop";
+      } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
+        override = "string-2";
+        return "maybeprop";
+      } else if (allowNested) {
+        override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
+        return "block";
+      } else {
+        override += " error";
+        return "maybeprop";
+      }
+    } else if (type == "meta") {
+      return "block";
+    } else if (!allowNested && (type == "hash" || type == "qualifier")) {
+      override = "error";
+      return "block";
+    } else {
+      return states.top(type, stream, state);
+    }
+  };
+
+  states.maybeprop = function(type, stream, state) {
+    if (type == ":") return pushContext(state, stream, "prop");
+    return pass(type, stream, state);
+  };
+
+  states.prop = function(type, stream, state) {
+    if (type == ";") return popContext(state);
+    if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
+    if (type == "}" || type == "{") return popAndPass(type, stream, state);
+    if (type == "(") return pushContext(state, stream, "parens");
+
+    if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
+      override += " error";
+    } else if (type == "word") {
+      wordAsValue(stream);
+    } else if (type == "interpolation") {
+      return pushContext(state, stream, "interpolation");
+    }
+    return "prop";
+  };
+
+  states.propBlock = function(type, _stream, state) {
+    if (type == "}") return popContext(state);
+    if (type == "word") { override = "property"; return "maybeprop"; }
+    return state.context.type;
+  };
+
+  states.parens = function(type, stream, state) {
+    if (type == "{" || type == "}") return popAndPass(type, stream, state);
+    if (type == ")") return popContext(state);
+    if (type == "(") return pushContext(state, stream, "parens");
+    if (type == "interpolation") return pushContext(state, stream, "interpolation");
+    if (type == "word") wordAsValue(stream);
+    return "parens";
+  };
+
+  states.pseudo = function(type, stream, state) {
+    if (type == "word") {
+      override = "variable-3";
+      return state.context.type;
+    }
+    return pass(type, stream, state);
+  };
+
+  states.documentTypes = function(type, stream, state) {
+    if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
+      override = "tag";
+      return state.context.type;
+    } else {
+      return states.atBlock(type, stream, state);
+    }
+  };
+
+  states.atBlock = function(type, stream, state) {
+    if (type == "(") return pushContext(state, stream, "atBlock_parens");
+    if (type == "}" || type == ";") return popAndPass(type, stream, state);
+    if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
+
+    if (type == "interpolation") return pushContext(state, stream, "interpolation");
+
+    if (type == "word") {
+      var word = stream.current().toLowerCase();
+      if (word == "only" || word == "not" || word == "and" || word == "or")
+        override = "keyword";
+      else if (mediaTypes.hasOwnProperty(word))
+        override = "attribute";
+      else if (mediaFeatures.hasOwnProperty(word))
+        override = "property";
+      else if (mediaValueKeywords.hasOwnProperty(word))
+        override = "keyword";
+      else if (propertyKeywords.hasOwnProperty(word))
+        override = "property";
+      else if (nonStandardPropertyKeywords.hasOwnProperty(word))
+        override = "string-2";
+      else if (valueKeywords.hasOwnProperty(word))
+        override = "atom";
+      else if (colorKeywords.hasOwnProperty(word))
+        override = "keyword";
+      else
+        override = "error";
+    }
+    return state.context.type;
+  };
+
+  states.atComponentBlock = function(type, stream, state) {
+    if (type == "}")
+      return popAndPass(type, stream, state);
+    if (type == "{")
+      return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
+    if (type == "word")
+      override = "error";
+    return state.context.type;
+  };
+
+  states.atBlock_parens = function(type, stream, state) {
+    if (type == ")") return popContext(state);
+    if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
+    return states.atBlock(type, stream, state);
+  };
+
+  states.restricted_atBlock_before = function(type, stream, state) {
+    if (type == "{")
+      return pushContext(state, stream, "restricted_atBlock");
+    if (type == "word" && state.stateArg == "@counter-style") {
+      override = "variable";
+      return "restricted_atBlock_before";
+    }
+    return pass(type, stream, state);
+  };
+
+  states.restricted_atBlock = function(type, stream, state) {
+    if (type == "}") {
+      state.stateArg = null;
+      return popContext(state);
+    }
+    if (type == "word") {
+      if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
+          (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
+        override = "error";
+      else
+        override = "property";
+      return "maybeprop";
+    }
+    return "restricted_atBlock";
+  };
+
+  states.keyframes = function(type, stream, state) {
+    if (type == "word") { override = "variable"; return "keyframes"; }
+    if (type == "{") return pushContext(state, stream, "top");
+    return pass(type, stream, state);
+  };
+
+  states.at = function(type, stream, state) {
+    if (type == ";") return popContext(state);
+    if (type == "{" || type == "}") return popAndPass(type, stream, state);
+    if (type == "word") override = "tag";
+    else if (type == "hash") override = "builtin";
+    return "at";
+  };
+
+  states.interpolation = function(type, stream, state) {
+    if (type == "}") return popContext(state);
+    if (type == "{" || type == ";") return popAndPass(type, stream, state);
+    if (type == "word") override = "variable";
+    else if (type != "variable" && type != "(" && type != ")") override = "error";
+    return "interpolation";
+  };
+
+  return {
+    startState: function(base) {
+      return {tokenize: null,
+              state: inline ? "block" : "top",
+              stateArg: null,
+              context: new Context(inline ? "block" : "top", base || 0, null)};
+    },
+
+    token: function(stream, state) {
+      if (!state.tokenize && stream.eatSpace()) return null;
+      var style = (state.tokenize || tokenBase)(stream, state);
+      if (style && typeof style == "object") {
+        type = style[1];
+        style = style[0];
+      }
+      override = style;
+      state.state = states[state.state](type, stream, state);
+      return override;
+    },
+
+    indent: function(state, textAfter) {
+      var cx = state.context, ch = textAfter && textAfter.charAt(0);
+      var indent = cx.indent;
+      if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
+      if (cx.prev) {
+        if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
+                          cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
+          // Resume indentation from parent context.
+          cx = cx.prev;
+          indent = cx.indent;
+        } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
+            ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
+          // Dedent relative to current context.
+          indent = Math.max(0, cx.indent - indentUnit);
+          cx = cx.prev;
+        }
+      }
+      return indent;
+    },
+
+    electricChars: "}",
+    blockCommentStart: "/*",
+    blockCommentEnd: "*/",
+    fold: "brace"
+  };
+});
+
+  function keySet(array) {
+    var keys = {};
+    for (var i = 0; i < array.length; ++i) {
+      keys[array[i]] = true;
+    }
+    return keys;
+  }
+
+  var documentTypes_ = [
+    "domain", "regexp", "url", "url-prefix"
+  ], documentTypes = keySet(documentTypes_);
+
+  var mediaTypes_ = [
+    "all", "aural", "braille", "handheld", "print", "projection", "screen",
+    "tty", "tv", "embossed"
+  ], mediaTypes = keySet(mediaTypes_);
+
+  var mediaFeatures_ = [
+    "width", "min-width", "max-width", "height", "min-height", "max-height",
+    "device-width", "min-device-width", "max-device-width", "device-height",
+    "min-device-height", "max-device-height", "aspect-ratio",
+    "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
+    "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
+    "max-color", "color-index", "min-color-index", "max-color-index",
+    "monochrome", "min-monochrome", "max-monochrome", "resolution",
+    "min-resolution", "max-resolution", "scan", "grid", "orientation",
+    "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
+    "pointer", "any-pointer", "hover", "any-hover"
+  ], mediaFeatures = keySet(mediaFeatures_);
+
+  var mediaValueKeywords_ = [
+    "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
+    "interlace", "progressive"
+  ], mediaValueKeywords = keySet(mediaValueKeywords_);
+
+  var propertyKeywords_ = [
+    "align-content", "align-items", "align-self", "alignment-adjust",
+    "alignment-baseline", "anchor-point", "animation", "animation-delay",
+    "animation-direction", "animation-duration", "animation-fill-mode",
+    "animation-iteration-count", "animation-name", "animation-play-state",
+    "animation-timing-function", "appearance", "azimuth", "backface-visibility",
+    "background", "background-attachment", "background-blend-mode", "background-clip",
+    "background-color", "background-image", "background-origin", "background-position",
+    "background-repeat", "background-size", "baseline-shift", "binding",
+    "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
+    "bookmark-target", "border", "border-bottom", "border-bottom-color",
+    "border-bottom-left-radius", "border-bottom-right-radius",
+    "border-bottom-style", "border-bottom-width", "border-collapse",
+    "border-color", "border-image", "border-image-outset",
+    "border-image-repeat", "border-image-slice", "border-image-source",
+    "border-image-width", "border-left", "border-left-color",
+    "border-left-style", "border-left-width", "border-radius", "border-right",
+    "border-right-color", "border-right-style", "border-right-width",
+    "border-spacing", "border-style", "border-top", "border-top-color",
+    "border-top-left-radius", "border-top-right-radius", "border-top-style",
+    "border-top-width", "border-width", "bottom", "box-decoration-break",
+    "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
+    "caption-side", "clear", "clip", "color", "color-profile", "column-count",
+    "column-fill", "column-gap", "column-rule", "column-rule-color",
+    "column-rule-style", "column-rule-width", "column-span", "column-width",
+    "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
+    "cue-after", "cue-before", "cursor", "direction", "display",
+    "dominant-baseline", "drop-initial-after-adjust",
+    "drop-initial-after-align", "drop-initial-before-adjust",
+    "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
+    "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
+    "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
+    "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
+    "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
+    "font-stretch", "font-style", "font-synthesis", "font-variant",
+    "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
+    "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
+    "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
+    "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap",
+    "grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap",
+    "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns",
+    "grid-template-rows", "hanging-punctuation", "height", "hyphens",
+    "icon", "image-orientation", "image-rendering", "image-resolution",
+    "inline-box-align", "justify-content", "left", "letter-spacing",
+    "line-break", "line-height", "line-stacking", "line-stacking-ruby",
+    "line-stacking-shift", "line-stacking-strategy", "list-style",
+    "list-style-image", "list-style-position", "list-style-type", "margin",
+    "margin-bottom", "margin-left", "margin-right", "margin-top",
+    "marker-offset", "marks", "marquee-direction", "marquee-loop",
+    "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
+    "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
+    "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
+    "opacity", "order", "orphans", "outline",
+    "outline-color", "outline-offset", "outline-style", "outline-width",
+    "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
+    "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
+    "page", "page-break-after", "page-break-before", "page-break-inside",
+    "page-policy", "pause", "pause-after", "pause-before", "perspective",
+    "perspective-origin", "pitch", "pitch-range", "play-during", "position",
+    "presentation-level", "punctuation-trim", "quotes", "region-break-after",
+    "region-break-before", "region-break-inside", "region-fragment",
+    "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
+    "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
+    "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
+    "shape-outside", "size", "speak", "speak-as", "speak-header",
+    "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
+    "tab-size", "table-layout", "target", "target-name", "target-new",
+    "target-position", "text-align", "text-align-last", "text-decoration",
+    "text-decoration-color", "text-decoration-line", "text-decoration-skip",
+    "text-decoration-style", "text-emphasis", "text-emphasis-color",
+    "text-emphasis-position", "text-emphasis-style", "text-height",
+    "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
+    "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
+    "text-wrap", "top", "transform", "transform-origin", "transform-style",
+    "transition", "transition-delay", "transition-duration",
+    "transition-property", "transition-timing-function", "unicode-bidi",
+    "vertical-align", "visibility", "voice-balance", "voice-duration",
+    "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
+    "voice-volume", "volume", "white-space", "widows", "width", "word-break",
+    "word-spacing", "word-wrap", "z-index",
+    // SVG-specific
+    "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
+    "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
+    "color-interpolation", "color-interpolation-filters",
+    "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
+    "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
+    "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
+    "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
+    "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
+    "glyph-orientation-vertical", "text-anchor", "writing-mode"
+  ], propertyKeywords = keySet(propertyKeywords_);
+
+  var nonStandardPropertyKeywords_ = [
+    "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
+    "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
+    "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
+    "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
+    "searchfield-results-decoration", "zoom"
+  ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
+
+  var fontProperties_ = [
+    "font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
+    "font-stretch", "font-weight", "font-style"
+  ], fontProperties = keySet(fontProperties_);
+
+  var counterDescriptors_ = [
+    "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
+    "speak-as", "suffix", "symbols", "system"
+  ], counterDescriptors = keySet(counterDescriptors_);
+
+  var colorKeywords_ = [
+    "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
+    "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
+    "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
+    "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
+    "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
+    "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
+    "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
+    "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
+    "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
+    "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
+    "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
+    "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
+    "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
+    "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
+    "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
+    "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
+    "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
+    "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
+    "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
+    "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
+    "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
+    "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
+    "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
+    "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
+    "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
+    "whitesmoke", "yellow", "yellowgreen"
+  ], colorKeywords = keySet(colorKeywords_);
+
+  var valueKeywords_ = [
+    "above", "absolute", "activeborder", "additive", "activecaption", "afar",
+    "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
+    "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
+    "arabic-indic", "armenian", "asterisks", "attr", "auto", "avoid", "avoid-column", "avoid-page",
+    "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
+    "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
+    "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
+    "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
+    "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
+    "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
+    "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
+    "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
+    "compact", "condensed", "contain", "content",
+    "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
+    "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
+    "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
+    "destination-in", "destination-out", "destination-over", "devanagari", "difference",
+    "disc", "discard", "disclosure-closed", "disclosure-open", "document",
+    "dot-dash", "dot-dot-dash",
+    "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
+    "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
+    "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
+    "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
+    "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
+    "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
+    "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
+    "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
+    "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
+    "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
+    "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
+    "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
+    "help", "hidden", "hide", "higher", "highlight", "highlighttext",
+    "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
+    "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
+    "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
+    "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
+    "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
+    "katakana", "katakana-iroha", "keep-all", "khmer",
+    "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
+    "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
+    "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
+    "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
+    "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
+    "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
+    "media-controls-background", "media-current-time-display",
+    "media-fullscreen-button", "media-mute-button", "media-play-button",
+    "media-return-to-realtime-button", "media-rewind-button",
+    "media-seek-back-button", "media-seek-forward-button", "media-slider",
+    "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
+    "media-volume-slider-container", "media-volume-sliderthumb", "medium",
+    "menu", "menulist", "menulist-button", "menulist-text",
+    "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
+    "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
+    "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
+    "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
+    "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
+    "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
+    "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
+    "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
+    "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
+    "progress", "push-button", "radial-gradient", "radio", "read-only",
+    "read-write", "read-write-plaintext-only", "rectangle", "region",
+    "relative", "repeat", "repeating-linear-gradient",
+    "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
+    "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
+    "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
+    "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
+    "scroll", "scrollbar", "se-resize", "searchfield",
+    "searchfield-cancel-button", "searchfield-decoration",
+    "searchfield-results-button", "searchfield-results-decoration",
+    "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
+    "simp-chinese-formal", "simp-chinese-informal", "single",
+    "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
+    "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
+    "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
+    "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "spell-out", "square",
+    "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
+    "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "table",
+    "table-caption", "table-cell", "table-column", "table-column-group",
+    "table-footer-group", "table-header-group", "table-row", "table-row-group",
+    "tamil",
+    "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
+    "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
+    "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
+    "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
+    "trad-chinese-formal", "trad-chinese-informal",
+    "translate", "translate3d", "translateX", "translateY", "translateZ",
+    "transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
+    "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
+    "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
+    "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
+    "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
+    "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
+    "xx-large", "xx-small"
+  ], valueKeywords = keySet(valueKeywords_);
+
+  var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
+    .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
+    .concat(valueKeywords_);
+  CodeMirror.registerHelper("hintWords", "css", allWords);
+
+  function tokenCComment(stream, state) {
+    var maybeEnd = false, ch;
+    while ((ch = stream.next()) != null) {
+      if (maybeEnd && ch == "/") {
+        state.tokenize = null;
+        break;
+      }
+      maybeEnd = (ch == "*");
+    }
+    return ["comment", "comment"];
+  }
+
+  CodeMirror.defineMIME("text/css", {
+    documentTypes: documentTypes,
+    mediaTypes: mediaTypes,
+    mediaFeatures: mediaFeatures,
+    mediaValueKeywords: mediaValueKeywords,
+    propertyKeywords: propertyKeywords,
+    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
+    fontProperties: fontProperties,
+    counterDescriptors: counterDescriptors,
+    colorKeywords: colorKeywords,
+    valueKeywords: valueKeywords,
+    tokenHooks: {
+      "/": function(stream, state) {
+        if (!stream.eat("*")) return false;
+        state.tokenize = tokenCComment;
+        return tokenCComment(stream, state);
+      }
+    },
+    name: "css"
+  });
+
+  CodeMirror.defineMIME("text/x-scss", {
+    mediaTypes: mediaTypes,
+    mediaFeatures: mediaFeatures,
+    mediaValueKeywords: mediaValueKeywords,
+    propertyKeywords: propertyKeywords,
+    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
+    colorKeywords: colorKeywords,
+    valueKeywords: valueKeywords,
+    fontProperties: fontProperties,
+    allowNested: true,
+    tokenHooks: {
+      "/": function(stream, state) {
+        if (stream.eat("/")) {
+          stream.skipToEnd();
+          return ["comment", "comment"];
+        } else if (stream.eat("*")) {
+          state.tokenize = tokenCComment;
+          return tokenCComment(stream, state);
+        } else {
+          return ["operator", "operator"];
+        }
+      },
+      ":": function(stream) {
+        if (stream.match(/\s*\{/))
+          return [null, "{"];
+        return false;
+      },
+      "$": function(stream) {
+        stream.match(/^[\w-]+/);
+        if (stream.match(/^\s*:/, false))
+          return ["variable-2", "variable-definition"];
+        return ["variable-2", "variable"];
+      },
+      "#": function(stream) {
+        if (!stream.eat("{")) return false;
+        return [null, "interpolation"];
+      }
+    },
+    name: "css",
+    helperType: "scss"
+  });
+
+  CodeMirror.defineMIME("text/x-less", {
+    mediaTypes: mediaTypes,
+    mediaFeatures: mediaFeatures,
+    mediaValueKeywords: mediaValueKeywords,
+    propertyKeywords: propertyKeywords,
+    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
+    colorKeywords: colorKeywords,
+    valueKeywords: valueKeywords,
+    fontProperties: fontProperties,
+    allowNested: true,
+    tokenHooks: {
+      "/": function(stream, state) {
+        if (stream.eat("/")) {
+          stream.skipToEnd();
+          return ["comment", "comment"];
+        } else if (stream.eat("*")) {
+          state.tokenize = tokenCComment;
+          return tokenCComment(stream, state);
+        } else {
+          return ["operator", "operator"];
+        }
+      },
+      "@": function(stream) {
+        if (stream.eat("{")) return [null, "interpolation"];
+        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
+        stream.eatWhile(/[\w\\\-]/);
+        if (stream.match(/^\s*:/, false))
+          return ["variable-2", "variable-definition"];
+        return ["variable-2", "variable"];
+      },
+      "&": function() {
+        return ["atom", "atom"];
+      }
+    },
+    name: "css",
+    helperType: "less"
+  });
+
+  CodeMirror.defineMIME("text/x-gss", {
+    documentTypes: documentTypes,
+    mediaTypes: mediaTypes,
+    mediaFeatures: mediaFeatures,
+    propertyKeywords: propertyKeywords,
+    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
+    fontProperties: fontProperties,
+    counterDescriptors: counterDescriptors,
+    colorKeywords: colorKeywords,
+    valueKeywords: valueKeywords,
+    supportsAtComponent: true,
+    tokenHooks: {
+      "/": function(stream, state) {
+        if (!stream.eat("*")) return false;
+        state.tokenize = tokenCComment;
+        return tokenCComment(stream, state);
+      }
+    },
+    name: "css",
+    helperType: "gss"
+  });
+
+});

+ 41 - 0
lib/codemirror/dracula.css

@@ -0,0 +1,41 @@
+/*
+
+    Name:       dracula
+    Author:     Michael Kaminsky (http://github.com/mkaminsky11)
+
+    Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)
+
+*/
+
+
+.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters {
+  background-color: #282a36 !important;
+  color: #f8f8f2 !important;
+  border: none;
+}
+.cm-s-dracula .CodeMirror-gutters { color: #282a36; }
+.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; }
+.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; }
+.cm-s-dracula.CodeMirror-focused div.CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }
+.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }
+.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }
+.cm-s-dracula span.cm-comment { color: #6272a4; }
+.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; }
+.cm-s-dracula span.cm-number { color: #bd93f9; }
+.cm-s-dracula span.cm-variable { color: #50fa7b; }
+.cm-s-dracula span.cm-variable-2 { color: white; }
+.cm-s-dracula span.cm-def { color: #ffb86c; }
+.cm-s-dracula span.cm-keyword { color: #ff79c6; }
+.cm-s-dracula span.cm-operator { color: #ff79c6; }
+.cm-s-dracula span.cm-keyword { color: #ff79c6; }
+.cm-s-dracula span.cm-atom { color: #bd93f9; }
+.cm-s-dracula span.cm-meta { color: #f8f8f2; }
+.cm-s-dracula span.cm-tag { color: #ff79c6; }
+.cm-s-dracula span.cm-attribute { color: #50fa7b; }
+.cm-s-dracula span.cm-qualifier { color: #50fa7b; }
+.cm-s-dracula span.cm-property { color: #66d9ef; }
+.cm-s-dracula span.cm-builtin { color: #50fa7b; }
+.cm-s-dracula span.cm-variable-3 { color: #50fa7b; }
+
+.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); }
+.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }

+ 743 - 0
lib/codemirror/javascript.js

@@ -0,0 +1,743 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+// TODO actually recognize syntax of TypeScript constructs
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+function expressionAllowed(stream, state, backUp) {
+  return /^(?:operator|sof|keyword c|case|new|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
+    (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
+}
+
+CodeMirror.defineMode("javascript", function(config, parserConfig) {
+  var indentUnit = config.indentUnit;
+  var statementIndent = parserConfig.statementIndent;
+  var jsonldMode = parserConfig.jsonld;
+  var jsonMode = parserConfig.json || jsonldMode;
+  var isTS = parserConfig.typescript;
+  var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
+
+  // Tokenizer
+
+  var keywords = function(){
+    function kw(type) {return {type: type, style: "keyword"};}
+    var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
+    var operator = kw("operator"), atom = {type: "atom", style: "atom"};
+
+    var jsKeywords = {
+      "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
+      "return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C,
+      "var": kw("var"), "const": kw("var"), "let": kw("var"),
+      "function": kw("function"), "catch": kw("catch"),
+      "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
+      "in": operator, "typeof": operator, "instanceof": operator,
+      "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
+      "this": kw("this"), "class": kw("class"), "super": kw("atom"),
+      "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
+      "await": C, "async": kw("async")
+    };
+
+    // Extend the 'normal' keywords with the TypeScript language extensions
+    if (isTS) {
+      var type = {type: "variable", style: "variable-3"};
+      var tsKeywords = {
+        // object-like things
+        "interface": kw("class"),
+        "implements": C,
+        "namespace": C,
+        "module": kw("module"),
+        "enum": kw("module"),
+
+        // scope modifiers
+        "public": kw("modifier"),
+        "private": kw("modifier"),
+        "protected": kw("modifier"),
+        "abstract": kw("modifier"),
+
+        // operators
+        "as": operator,
+
+        // types
+        "string": type, "number": type, "boolean": type, "any": type
+      };
+
+      for (var attr in tsKeywords) {
+        jsKeywords[attr] = tsKeywords[attr];
+      }
+    }
+
+    return jsKeywords;
+  }();
+
+  var isOperatorChar = /[+\-*&%=<>!?|~^]/;
+  var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
+
+  function readRegexp(stream) {
+    var escaped = false, next, inSet = false;
+    while ((next = stream.next()) != null) {
+      if (!escaped) {
+        if (next == "/" && !inSet) return;
+        if (next == "[") inSet = true;
+        else if (inSet && next == "]") inSet = false;
+      }
+      escaped = !escaped && next == "\\";
+    }
+  }
+
+  // Used as scratch variables to communicate multiple values without
+  // consing up tons of objects.
+  var type, content;
+  function ret(tp, style, cont) {
+    type = tp; content = cont;
+    return style;
+  }
+  function tokenBase(stream, state) {
+    var ch = stream.next();
+    if (ch == '"' || ch == "'") {
+      state.tokenize = tokenString(ch);
+      return state.tokenize(stream, state);
+    } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
+      return ret("number", "number");
+    } else if (ch == "." && stream.match("..")) {
+      return ret("spread", "meta");
+    } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
+      return ret(ch);
+    } else if (ch == "=" && stream.eat(">")) {
+      return ret("=>", "operator");
+    } else if (ch == "0" && stream.eat(/x/i)) {
+      stream.eatWhile(/[\da-f]/i);
+      return ret("number", "number");
+    } else if (ch == "0" && stream.eat(/o/i)) {
+      stream.eatWhile(/[0-7]/i);
+      return ret("number", "number");
+    } else if (ch == "0" && stream.eat(/b/i)) {
+      stream.eatWhile(/[01]/i);
+      return ret("number", "number");
+    } else if (/\d/.test(ch)) {
+      stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
+      return ret("number", "number");
+    } else if (ch == "/") {
+      if (stream.eat("*")) {
+        state.tokenize = tokenComment;
+        return tokenComment(stream, state);
+      } else if (stream.eat("/")) {
+        stream.skipToEnd();
+        return ret("comment", "comment");
+      } else if (expressionAllowed(stream, state, 1)) {
+        readRegexp(stream);
+        stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
+        return ret("regexp", "string-2");
+      } else {
+        stream.eatWhile(isOperatorChar);
+        return ret("operator", "operator", stream.current());
+      }
+    } else if (ch == "`") {
+      state.tokenize = tokenQuasi;
+      return tokenQuasi(stream, state);
+    } else if (ch == "#") {
+      stream.skipToEnd();
+      return ret("error", "error");
+    } else if (isOperatorChar.test(ch)) {
+      stream.eatWhile(isOperatorChar);
+      return ret("operator", "operator", stream.current());
+    } else if (wordRE.test(ch)) {
+      stream.eatWhile(wordRE);
+      var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
+      return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
+                     ret("variable", "variable", word);
+    }
+  }
+
+  function tokenString(quote) {
+    return function(stream, state) {
+      var escaped = false, next;
+      if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
+        state.tokenize = tokenBase;
+        return ret("jsonld-keyword", "meta");
+      }
+      while ((next = stream.next()) != null) {
+        if (next == quote && !escaped) break;
+        escaped = !escaped && next == "\\";
+      }
+      if (!escaped) state.tokenize = tokenBase;
+      return ret("string", "string");
+    };
+  }
+
+  function tokenComment(stream, state) {
+    var maybeEnd = false, ch;
+    while (ch = stream.next()) {
+      if (ch == "/" && maybeEnd) {
+        state.tokenize = tokenBase;
+        break;
+      }
+      maybeEnd = (ch == "*");
+    }
+    return ret("comment", "comment");
+  }
+
+  function tokenQuasi(stream, state) {
+    var escaped = false, next;
+    while ((next = stream.next()) != null) {
+      if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
+        state.tokenize = tokenBase;
+        break;
+      }
+      escaped = !escaped && next == "\\";
+    }
+    return ret("quasi", "string-2", stream.current());
+  }
+
+  var brackets = "([{}])";
+  // This is a crude lookahead trick to try and notice that we're
+  // parsing the argument patterns for a fat-arrow function before we
+  // actually hit the arrow token. It only works if the arrow is on
+  // the same line as the arguments and there's no strange noise
+  // (comments) in between. Fallback is to only notice when we hit the
+  // arrow, and not declare the arguments as locals for the arrow
+  // body.
+  function findFatArrow(stream, state) {
+    if (state.fatArrowAt) state.fatArrowAt = null;
+    var arrow = stream.string.indexOf("=>", stream.start);
+    if (arrow < 0) return;
+
+    var depth = 0, sawSomething = false;
+    for (var pos = arrow - 1; pos >= 0; --pos) {
+      var ch = stream.string.charAt(pos);
+      var bracket = brackets.indexOf(ch);
+      if (bracket >= 0 && bracket < 3) {
+        if (!depth) { ++pos; break; }
+        if (--depth == 0) break;
+      } else if (bracket >= 3 && bracket < 6) {
+        ++depth;
+      } else if (wordRE.test(ch)) {
+        sawSomething = true;
+      } else if (/["'\/]/.test(ch)) {
+        return;
+      } else if (sawSomething && !depth) {
+        ++pos;
+        break;
+      }
+    }
+    if (sawSomething && !depth) state.fatArrowAt = pos;
+  }
+
+  // Parser
+
+  var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
+
+  function JSLexical(indented, column, type, align, prev, info) {
+    this.indented = indented;
+    this.column = column;
+    this.type = type;
+    this.prev = prev;
+    this.info = info;
+    if (align != null) this.align = align;
+  }
+
+  function inScope(state, varname) {
+    for (var v = state.localVars; v; v = v.next)
+      if (v.name == varname) return true;
+    for (var cx = state.context; cx; cx = cx.prev) {
+      for (var v = cx.vars; v; v = v.next)
+        if (v.name == varname) return true;
+    }
+  }
+
+  function parseJS(state, style, type, content, stream) {
+    var cc = state.cc;
+    // Communicate our context to the combinators.
+    // (Less wasteful than consing up a hundred closures on every call.)
+    cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
+
+    if (!state.lexical.hasOwnProperty("align"))
+      state.lexical.align = true;
+
+    while(true) {
+      var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
+      if (combinator(type, content)) {
+        while(cc.length && cc[cc.length - 1].lex)
+          cc.pop()();
+        if (cx.marked) return cx.marked;
+        if (type == "variable" && inScope(state, content)) return "variable-2";
+        return style;
+      }
+    }
+  }
+
+  // Combinator utils
+
+  var cx = {state: null, column: null, marked: null, cc: null};
+  function pass() {
+    for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
+  }
+  function cont() {
+    pass.apply(null, arguments);
+    return true;
+  }
+  function register(varname) {
+    function inList(list) {
+      for (var v = list; v; v = v.next)
+        if (v.name == varname) return true;
+      return false;
+    }
+    var state = cx.state;
+    cx.marked = "def";
+    if (state.context) {
+      if (inList(state.localVars)) return;
+      state.localVars = {name: varname, next: state.localVars};
+    } else {
+      if (inList(state.globalVars)) return;
+      if (parserConfig.globalVars)
+        state.globalVars = {name: varname, next: state.globalVars};
+    }
+  }
+
+  // Combinators
+
+  var defaultVars = {name: "this", next: {name: "arguments"}};
+  function pushcontext() {
+    cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
+    cx.state.localVars = defaultVars;
+  }
+  function popcontext() {
+    cx.state.localVars = cx.state.context.vars;
+    cx.state.context = cx.state.context.prev;
+  }
+  function pushlex(type, info) {
+    var result = function() {
+      var state = cx.state, indent = state.indented;
+      if (state.lexical.type == "stat") indent = state.lexical.indented;
+      else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
+        indent = outer.indented;
+      state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
+    };
+    result.lex = true;
+    return result;
+  }
+  function poplex() {
+    var state = cx.state;
+    if (state.lexical.prev) {
+      if (state.lexical.type == ")")
+        state.indented = state.lexical.indented;
+      state.lexical = state.lexical.prev;
+    }
+  }
+  poplex.lex = true;
+
+  function expect(wanted) {
+    function exp(type) {
+      if (type == wanted) return cont();
+      else if (wanted == ";") return pass();
+      else return cont(exp);
+    };
+    return exp;
+  }
+
+  function statement(type, value) {
+    if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
+    if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
+    if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
+    if (type == "{") return cont(pushlex("}"), block, poplex);
+    if (type == ";") return cont();
+    if (type == "if") {
+      if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
+        cx.state.cc.pop()();
+      return cont(pushlex("form"), expression, statement, poplex, maybeelse);
+    }
+    if (type == "function") return cont(functiondef);
+    if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
+    if (type == "variable") return cont(pushlex("stat"), maybelabel);
+    if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
+                                      block, poplex, poplex);
+    if (type == "case") return cont(expression, expect(":"));
+    if (type == "default") return cont(expect(":"));
+    if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
+                                     statement, poplex, popcontext);
+    if (type == "class") return cont(pushlex("form"), className, poplex);
+    if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
+    if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
+    if (type == "module") return cont(pushlex("form"), pattern, pushlex("}"), expect("{"), block, poplex, poplex)
+    if (type == "async") return cont(statement)
+    return pass(pushlex("stat"), expression, expect(";"), poplex);
+  }
+  function expression(type) {
+    return expressionInner(type, false);
+  }
+  function expressionNoComma(type) {
+    return expressionInner(type, true);
+  }
+  function expressionInner(type, noComma) {
+    if (cx.state.fatArrowAt == cx.stream.start) {
+      var body = noComma ? arrowBodyNoComma : arrowBody;
+      if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
+      else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
+    }
+
+    var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
+    if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
+    if (type == "function") return cont(functiondef, maybeop);
+    if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
+    if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
+    if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
+    if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
+    if (type == "{") return contCommasep(objprop, "}", null, maybeop);
+    if (type == "quasi") return pass(quasi, maybeop);
+    if (type == "new") return cont(maybeTarget(noComma));
+    return cont();
+  }
+  function maybeexpression(type) {
+    if (type.match(/[;\}\)\],]/)) return pass();
+    return pass(expression);
+  }
+  function maybeexpressionNoComma(type) {
+    if (type.match(/[;\}\)\],]/)) return pass();
+    return pass(expressionNoComma);
+  }
+
+  function maybeoperatorComma(type, value) {
+    if (type == ",") return cont(expression);
+    return maybeoperatorNoComma(type, value, false);
+  }
+  function maybeoperatorNoComma(type, value, noComma) {
+    var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
+    var expr = noComma == false ? expression : expressionNoComma;
+    if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
+    if (type == "operator") {
+      if (/\+\+|--/.test(value)) return cont(me);
+      if (value == "?") return cont(expression, expect(":"), expr);
+      return cont(expr);
+    }
+    if (type == "quasi") { return pass(quasi, me); }
+    if (type == ";") return;
+    if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
+    if (type == ".") return cont(property, me);
+    if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
+  }
+  function quasi(type, value) {
+    if (type != "quasi") return pass();
+    if (value.slice(value.length - 2) != "${") return cont(quasi);
+    return cont(expression, continueQuasi);
+  }
+  function continueQuasi(type) {
+    if (type == "}") {
+      cx.marked = "string-2";
+      cx.state.tokenize = tokenQuasi;
+      return cont(quasi);
+    }
+  }
+  function arrowBody(type) {
+    findFatArrow(cx.stream, cx.state);
+    return pass(type == "{" ? statement : expression);
+  }
+  function arrowBodyNoComma(type) {
+    findFatArrow(cx.stream, cx.state);
+    return pass(type == "{" ? statement : expressionNoComma);
+  }
+  function maybeTarget(noComma) {
+    return function(type) {
+      if (type == ".") return cont(noComma ? targetNoComma : target);
+      else return pass(noComma ? expressionNoComma : expression);
+    };
+  }
+  function target(_, value) {
+    if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
+  }
+  function targetNoComma(_, value) {
+    if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
+  }
+  function maybelabel(type) {
+    if (type == ":") return cont(poplex, statement);
+    return pass(maybeoperatorComma, expect(";"), poplex);
+  }
+  function property(type) {
+    if (type == "variable") {cx.marked = "property"; return cont();}
+  }
+  function objprop(type, value) {
+    if (type == "async") return cont(objprop);
+    if (type == "variable" || cx.style == "keyword") {
+      cx.marked = "property";
+      if (value == "get" || value == "set") return cont(getterSetter);
+      return cont(afterprop);
+    } else if (type == "number" || type == "string") {
+      cx.marked = jsonldMode ? "property" : (cx.style + " property");
+      return cont(afterprop);
+    } else if (type == "jsonld-keyword") {
+      return cont(afterprop);
+    } else if (type == "modifier") {
+      return cont(objprop)
+    } else if (type == "[") {
+      return cont(expression, expect("]"), afterprop);
+    } else if (type == "spread") {
+      return cont(expression);
+    }
+  }
+  function getterSetter(type) {
+    if (type != "variable") return pass(afterprop);
+    cx.marked = "property";
+    return cont(functiondef);
+  }
+  function afterprop(type) {
+    if (type == ":") return cont(expressionNoComma);
+    if (type == "(") return pass(functiondef);
+  }
+  function commasep(what, end) {
+    function proceed(type, value) {
+      if (type == ",") {
+        var lex = cx.state.lexical;
+        if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
+        return cont(function(type, value) {
+          if (type == end || value == end) return pass()
+          return pass(what)
+        }, proceed);
+      }
+      if (type == end || value == end) return cont();
+      return cont(expect(end));
+    }
+    return function(type, value) {
+      if (type == end || value == end) return cont();
+      return pass(what, proceed);
+    };
+  }
+  function contCommasep(what, end, info) {
+    for (var i = 3; i < arguments.length; i++)
+      cx.cc.push(arguments[i]);
+    return cont(pushlex(end, info), commasep(what, end), poplex);
+  }
+  function block(type) {
+    if (type == "}") return cont();
+    return pass(statement, block);
+  }
+  function maybetype(type) {
+    if (isTS && type == ":") return cont(typeexpr);
+  }
+  function maybedefault(_, value) {
+    if (value == "=") return cont(expressionNoComma);
+  }
+  function typeexpr(type) {
+    if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);}
+  }
+  function afterType(type, value) {
+    if (value == "<") return cont(commasep(typeexpr, ">"), afterType)
+    if (type == "[") return cont(expect("]"), afterType)
+  }
+  function vardef() {
+    return pass(pattern, maybetype, maybeAssign, vardefCont);
+  }
+  function pattern(type, value) {
+    if (type == "modifier") return cont(pattern)
+    if (type == "variable") { register(value); return cont(); }
+    if (type == "spread") return cont(pattern);
+    if (type == "[") return contCommasep(pattern, "]");
+    if (type == "{") return contCommasep(proppattern, "}");
+  }
+  function proppattern(type, value) {
+    if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
+      register(value);
+      return cont(maybeAssign);
+    }
+    if (type == "variable") cx.marked = "property";
+    if (type == "spread") return cont(pattern);
+    if (type == "}") return pass();
+    return cont(expect(":"), pattern, maybeAssign);
+  }
+  function maybeAssign(_type, value) {
+    if (value == "=") return cont(expressionNoComma);
+  }
+  function vardefCont(type) {
+    if (type == ",") return cont(vardef);
+  }
+  function maybeelse(type, value) {
+    if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
+  }
+  function forspec(type) {
+    if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
+  }
+  function forspec1(type) {
+    if (type == "var") return cont(vardef, expect(";"), forspec2);
+    if (type == ";") return cont(forspec2);
+    if (type == "variable") return cont(formaybeinof);
+    return pass(expression, expect(";"), forspec2);
+  }
+  function formaybeinof(_type, value) {
+    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
+    return cont(maybeoperatorComma, forspec2);
+  }
+  function forspec2(type, value) {
+    if (type == ";") return cont(forspec3);
+    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
+    return pass(expression, expect(";"), forspec3);
+  }
+  function forspec3(type) {
+    if (type != ")") cont(expression);
+  }
+  function functiondef(type, value) {
+    if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
+    if (type == "variable") {register(value); return cont(functiondef);}
+    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
+  }
+  function funarg(type) {
+    if (type == "spread") return cont(funarg);
+    return pass(pattern, maybetype, maybedefault);
+  }
+  function className(type, value) {
+    if (type == "variable") {register(value); return cont(classNameAfter);}
+  }
+  function classNameAfter(type, value) {
+    if (value == "extends") return cont(expression, classNameAfter);
+    if (type == "{") return cont(pushlex("}"), classBody, poplex);
+  }
+  function classBody(type, value) {
+    if (type == "variable" || cx.style == "keyword") {
+      if (value == "static") {
+        cx.marked = "keyword";
+        return cont(classBody);
+      }
+      cx.marked = "property";
+      if (value == "get" || value == "set") return cont(classGetterSetter, functiondef, classBody);
+      return cont(functiondef, classBody);
+    }
+    if (value == "*") {
+      cx.marked = "keyword";
+      return cont(classBody);
+    }
+    if (type == ";") return cont(classBody);
+    if (type == "}") return cont();
+  }
+  function classGetterSetter(type) {
+    if (type != "variable") return pass();
+    cx.marked = "property";
+    return cont();
+  }
+  function afterExport(_type, value) {
+    if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
+    if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
+    return pass(statement);
+  }
+  function afterImport(type) {
+    if (type == "string") return cont();
+    return pass(importSpec, maybeFrom);
+  }
+  function importSpec(type, value) {
+    if (type == "{") return contCommasep(importSpec, "}");
+    if (type == "variable") register(value);
+    if (value == "*") cx.marked = "keyword";
+    return cont(maybeAs);
+  }
+  function maybeAs(_type, value) {
+    if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
+  }
+  function maybeFrom(_type, value) {
+    if (value == "from") { cx.marked = "keyword"; return cont(expression); }
+  }
+  function arrayLiteral(type) {
+    if (type == "]") return cont();
+    return pass(expressionNoComma, commasep(expressionNoComma, "]"));
+  }
+
+  function isContinuedStatement(state, textAfter) {
+    return state.lastType == "operator" || state.lastType == "," ||
+      isOperatorChar.test(textAfter.charAt(0)) ||
+      /[,.]/.test(textAfter.charAt(0));
+  }
+
+  // Interface
+
+  return {
+    startState: function(basecolumn) {
+      var state = {
+        tokenize: tokenBase,
+        lastType: "sof",
+        cc: [],
+        lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
+        localVars: parserConfig.localVars,
+        context: parserConfig.localVars && {vars: parserConfig.localVars},
+        indented: basecolumn || 0
+      };
+      if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
+        state.globalVars = parserConfig.globalVars;
+      return state;
+    },
+
+    token: function(stream, state) {
+      if (stream.sol()) {
+        if (!state.lexical.hasOwnProperty("align"))
+          state.lexical.align = false;
+        state.indented = stream.indentation();
+        findFatArrow(stream, state);
+      }
+      if (state.tokenize != tokenComment && stream.eatSpace()) return null;
+      var style = state.tokenize(stream, state);
+      if (type == "comment") return style;
+      state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
+      return parseJS(state, style, type, content, stream);
+    },
+
+    indent: function(state, textAfter) {
+      if (state.tokenize == tokenComment) return CodeMirror.Pass;
+      if (state.tokenize != tokenBase) return 0;
+      var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
+      // Kludge to prevent 'maybelse' from blocking lexical scope pops
+      if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
+        var c = state.cc[i];
+        if (c == poplex) lexical = lexical.prev;
+        else if (c != maybeelse) break;
+      }
+      if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
+      if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
+        lexical = lexical.prev;
+      var type = lexical.type, closing = firstChar == type;
+
+      if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
+      else if (type == "form" && firstChar == "{") return lexical.indented;
+      else if (type == "form") return lexical.indented + indentUnit;
+      else if (type == "stat")
+        return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
+      else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
+        return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
+      else if (lexical.align) return lexical.column + (closing ? 0 : 1);
+      else return lexical.indented + (closing ? 0 : indentUnit);
+    },
+
+    electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
+    blockCommentStart: jsonMode ? null : "/*",
+    blockCommentEnd: jsonMode ? null : "*/",
+    lineComment: jsonMode ? null : "//",
+    fold: "brace",
+    closeBrackets: "()[]{}''\"\"``",
+
+    helperType: jsonMode ? "json" : "javascript",
+    jsonldMode: jsonldMode,
+    jsonMode: jsonMode,
+
+    expressionAllowed: expressionAllowed,
+    skipExpression: function(state) {
+      var top = state.cc[state.cc.length - 1]
+      if (top == expression || top == expressionNoComma) state.cc.pop()
+    }
+  };
+});
+
+CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
+
+CodeMirror.defineMIME("text/javascript", "javascript");
+CodeMirror.defineMIME("text/ecmascript", "javascript");
+CodeMirror.defineMIME("application/javascript", "javascript");
+CodeMirror.defineMIME("application/x-javascript", "javascript");
+CodeMirror.defineMIME("application/ecmascript", "javascript");
+CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
+CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
+CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
+CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
+CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
+
+});

+ 54 - 0
lib/codemirror/material.css

@@ -0,0 +1,54 @@
+/*
+
+    Name:       material
+    Author:     Michael Kaminsky (http://github.com/mkaminsky11)
+
+    Original material color scheme by Mattia Astorino (https://github.com/equinusocio/material-theme)
+
+*/
+
+.cm-s-material {
+  background-color: #263238;
+  color: rgba(233, 237, 237, 1);
+}
+.cm-s-material .CodeMirror-gutters {
+  background: #263238;
+  color: rgb(83,127,126);
+  border: none;
+  border-right:1px solid #334047
+}
+.cm-s-material .CodeMirror-guttermarker, .cm-s-material .CodeMirror-guttermarker-subtle, .cm-s-material .CodeMirror-linenumber { color: rgb(83,127,126); }
+.cm-s-material .CodeMirror-cursor { border-left: 1px solid #f8f8f0; }
+.cm-s-material div.CodeMirror-selected { background: rgba(255, 255, 255, 0.15); }
+.cm-s-material.CodeMirror-focused div.CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }
+.cm-s-material .CodeMirror-line::selection, .cm-s-material .CodeMirror-line > span::selection, .cm-s-material .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }
+.cm-s-material .CodeMirror-line::-moz-selection, .cm-s-material .CodeMirror-line > span::-moz-selection, .cm-s-material .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }
+
+.cm-s-material .CodeMirror-activeline-background { background: rgba(0, 0, 0, 0); }
+.cm-s-material .cm-keyword { color: rgba(199, 146, 234, 1); }
+.cm-s-material .cm-operator { color: rgba(233, 237, 237, 1); }
+.cm-s-material .cm-variable-2 { color: #80CBC4; }
+.cm-s-material .cm-variable-3 { color: #82B1FF; }
+.cm-s-material .cm-builtin { color: #DECB6B; }
+.cm-s-material .cm-atom { color: #F77669; }
+.cm-s-material .cm-number { color: #F77669; }
+.cm-s-material .cm-def { color: rgba(233, 237, 237, 1); }
+.cm-s-material .cm-string { color: #C3E88D; }
+.cm-s-material .cm-string-2 { color: #80CBC4; }
+.cm-s-material .cm-comment { color: #546E7A; }
+.cm-s-material .cm-variable { color: #82B1FF; }
+.cm-s-material .cm-tag { color: #80CBC4; }
+.cm-s-material .cm-meta { color: #80CBC4; }
+.cm-s-material .cm-attribute { color: #FFCB6B; }
+.cm-s-material .cm-property { color: #80CBAE; }
+.cm-s-material .cm-qualifier { color: #DECB6B; }
+.cm-s-material .cm-variable-3 { color: #DECB6B; }
+.cm-s-material .cm-tag { color: rgba(255, 83, 112, 1); }
+.cm-s-material .cm-error {
+  color: rgba(255, 255, 255, 1.0);
+  background-color: #EC5F67;
+}
+.cm-s-material .CodeMirror-matchingbracket {
+  text-decoration: underline;
+  color: white !important;
+}

+ 394 - 0
lib/codemirror/xml.js

@@ -0,0 +1,394 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+var htmlConfig = {
+  autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
+                    'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
+                    'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
+                    'track': true, 'wbr': true, 'menuitem': true},
+  implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
+                     'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
+                     'th': true, 'tr': true},
+  contextGrabbers: {
+    'dd': {'dd': true, 'dt': true},
+    'dt': {'dd': true, 'dt': true},
+    'li': {'li': true},
+    'option': {'option': true, 'optgroup': true},
+    'optgroup': {'optgroup': true},
+    'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
+          'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
+          'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
+          'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
+          'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
+    'rp': {'rp': true, 'rt': true},
+    'rt': {'rp': true, 'rt': true},
+    'tbody': {'tbody': true, 'tfoot': true},
+    'td': {'td': true, 'th': true},
+    'tfoot': {'tbody': true},
+    'th': {'td': true, 'th': true},
+    'thead': {'tbody': true, 'tfoot': true},
+    'tr': {'tr': true}
+  },
+  doNotIndent: {"pre": true},
+  allowUnquoted: true,
+  allowMissing: true,
+  caseFold: true
+}
+
+var xmlConfig = {
+  autoSelfClosers: {},
+  implicitlyClosed: {},
+  contextGrabbers: {},
+  doNotIndent: {},
+  allowUnquoted: false,
+  allowMissing: false,
+  caseFold: false
+}
+
+CodeMirror.defineMode("xml", function(editorConf, config_) {
+  var indentUnit = editorConf.indentUnit
+  var config = {}
+  var defaults = config_.htmlMode ? htmlConfig : xmlConfig
+  for (var prop in defaults) config[prop] = defaults[prop]
+  for (var prop in config_) config[prop] = config_[prop]
+
+  // Return variables for tokenizers
+  var type, setStyle;
+
+  function inText(stream, state) {
+    function chain(parser) {
+      state.tokenize = parser;
+      return parser(stream, state);
+    }
+
+    var ch = stream.next();
+    if (ch == "<") {
+      if (stream.eat("!")) {
+        if (stream.eat("[")) {
+          if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
+          else return null;
+        } else if (stream.match("--")) {
+          return chain(inBlock("comment", "-->"));
+        } else if (stream.match("DOCTYPE", true, true)) {
+          stream.eatWhile(/[\w\._\-]/);
+          return chain(doctype(1));
+        } else {
+          return null;
+        }
+      } else if (stream.eat("?")) {
+        stream.eatWhile(/[\w\._\-]/);
+        state.tokenize = inBlock("meta", "?>");
+        return "meta";
+      } else {
+        type = stream.eat("/") ? "closeTag" : "openTag";
+        state.tokenize = inTag;
+        return "tag bracket";
+      }
+    } else if (ch == "&") {
+      var ok;
+      if (stream.eat("#")) {
+        if (stream.eat("x")) {
+          ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
+        } else {
+          ok = stream.eatWhile(/[\d]/) && stream.eat(";");
+        }
+      } else {
+        ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
+      }
+      return ok ? "atom" : "error";
+    } else {
+      stream.eatWhile(/[^&<]/);
+      return null;
+    }
+  }
+  inText.isInText = true;
+
+  function inTag(stream, state) {
+    var ch = stream.next();
+    if (ch == ">" || (ch == "/" && stream.eat(">"))) {
+      state.tokenize = inText;
+      type = ch == ">" ? "endTag" : "selfcloseTag";
+      return "tag bracket";
+    } else if (ch == "=") {
+      type = "equals";
+      return null;
+    } else if (ch == "<") {
+      state.tokenize = inText;
+      state.state = baseState;
+      state.tagName = state.tagStart = null;
+      var next = state.tokenize(stream, state);
+      return next ? next + " tag error" : "tag error";
+    } else if (/[\'\"]/.test(ch)) {
+      state.tokenize = inAttribute(ch);
+      state.stringStartCol = stream.column();
+      return state.tokenize(stream, state);
+    } else {
+      stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
+      return "word";
+    }
+  }
+
+  function inAttribute(quote) {
+    var closure = function(stream, state) {
+      while (!stream.eol()) {
+        if (stream.next() == quote) {
+          state.tokenize = inTag;
+          break;
+        }
+      }
+      return "string";
+    };
+    closure.isInAttribute = true;
+    return closure;
+  }
+
+  function inBlock(style, terminator) {
+    return function(stream, state) {
+      while (!stream.eol()) {
+        if (stream.match(terminator)) {
+          state.tokenize = inText;
+          break;
+        }
+        stream.next();
+      }
+      return style;
+    };
+  }
+  function doctype(depth) {
+    return function(stream, state) {
+      var ch;
+      while ((ch = stream.next()) != null) {
+        if (ch == "<") {
+          state.tokenize = doctype(depth + 1);
+          return state.tokenize(stream, state);
+        } else if (ch == ">") {
+          if (depth == 1) {
+            state.tokenize = inText;
+            break;
+          } else {
+            state.tokenize = doctype(depth - 1);
+            return state.tokenize(stream, state);
+          }
+        }
+      }
+      return "meta";
+    };
+  }
+
+  function Context(state, tagName, startOfLine) {
+    this.prev = state.context;
+    this.tagName = tagName;
+    this.indent = state.indented;
+    this.startOfLine = startOfLine;
+    if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
+      this.noIndent = true;
+  }
+  function popContext(state) {
+    if (state.context) state.context = state.context.prev;
+  }
+  function maybePopContext(state, nextTagName) {
+    var parentTagName;
+    while (true) {
+      if (!state.context) {
+        return;
+      }
+      parentTagName = state.context.tagName;
+      if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
+          !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
+        return;
+      }
+      popContext(state);
+    }
+  }
+
+  function baseState(type, stream, state) {
+    if (type == "openTag") {
+      state.tagStart = stream.column();
+      return tagNameState;
+    } else if (type == "closeTag") {
+      return closeTagNameState;
+    } else {
+      return baseState;
+    }
+  }
+  function tagNameState(type, stream, state) {
+    if (type == "word") {
+      state.tagName = stream.current();
+      setStyle = "tag";
+      return attrState;
+    } else {
+      setStyle = "error";
+      return tagNameState;
+    }
+  }
+  function closeTagNameState(type, stream, state) {
+    if (type == "word") {
+      var tagName = stream.current();
+      if (state.context && state.context.tagName != tagName &&
+          config.implicitlyClosed.hasOwnProperty(state.context.tagName))
+        popContext(state);
+      if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
+        setStyle = "tag";
+        return closeState;
+      } else {
+        setStyle = "tag error";
+        return closeStateErr;
+      }
+    } else {
+      setStyle = "error";
+      return closeStateErr;
+    }
+  }
+
+  function closeState(type, _stream, state) {
+    if (type != "endTag") {
+      setStyle = "error";
+      return closeState;
+    }
+    popContext(state);
+    return baseState;
+  }
+  function closeStateErr(type, stream, state) {
+    setStyle = "error";
+    return closeState(type, stream, state);
+  }
+
+  function attrState(type, _stream, state) {
+    if (type == "word") {
+      setStyle = "attribute";
+      return attrEqState;
+    } else if (type == "endTag" || type == "selfcloseTag") {
+      var tagName = state.tagName, tagStart = state.tagStart;
+      state.tagName = state.tagStart = null;
+      if (type == "selfcloseTag" ||
+          config.autoSelfClosers.hasOwnProperty(tagName)) {
+        maybePopContext(state, tagName);
+      } else {
+        maybePopContext(state, tagName);
+        state.context = new Context(state, tagName, tagStart == state.indented);
+      }
+      return baseState;
+    }
+    setStyle = "error";
+    return attrState;
+  }
+  function attrEqState(type, stream, state) {
+    if (type == "equals") return attrValueState;
+    if (!config.allowMissing) setStyle = "error";
+    return attrState(type, stream, state);
+  }
+  function attrValueState(type, stream, state) {
+    if (type == "string") return attrContinuedState;
+    if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
+    setStyle = "error";
+    return attrState(type, stream, state);
+  }
+  function attrContinuedState(type, stream, state) {
+    if (type == "string") return attrContinuedState;
+    return attrState(type, stream, state);
+  }
+
+  return {
+    startState: function(baseIndent) {
+      var state = {tokenize: inText,
+                   state: baseState,
+                   indented: baseIndent || 0,
+                   tagName: null, tagStart: null,
+                   context: null}
+      if (baseIndent != null) state.baseIndent = baseIndent
+      return state
+    },
+
+    token: function(stream, state) {
+      if (!state.tagName && stream.sol())
+        state.indented = stream.indentation();
+
+      if (stream.eatSpace()) return null;
+      type = null;
+      var style = state.tokenize(stream, state);
+      if ((style || type) && style != "comment") {
+        setStyle = null;
+        state.state = state.state(type || style, stream, state);
+        if (setStyle)
+          style = setStyle == "error" ? style + " error" : setStyle;
+      }
+      return style;
+    },
+
+    indent: function(state, textAfter, fullLine) {
+      var context = state.context;
+      // Indent multi-line strings (e.g. css).
+      if (state.tokenize.isInAttribute) {
+        if (state.tagStart == state.indented)
+          return state.stringStartCol + 1;
+        else
+          return state.indented + indentUnit;
+      }
+      if (context && context.noIndent) return CodeMirror.Pass;
+      if (state.tokenize != inTag && state.tokenize != inText)
+        return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
+      // Indent the starts of attribute names.
+      if (state.tagName) {
+        if (config.multilineTagIndentPastTag !== false)
+          return state.tagStart + state.tagName.length + 2;
+        else
+          return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
+      }
+      if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
+      var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
+      if (tagAfter && tagAfter[1]) { // Closing tag spotted
+        while (context) {
+          if (context.tagName == tagAfter[2]) {
+            context = context.prev;
+            break;
+          } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
+            context = context.prev;
+          } else {
+            break;
+          }
+        }
+      } else if (tagAfter) { // Opening tag spotted
+        while (context) {
+          var grabbers = config.contextGrabbers[context.tagName];
+          if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
+            context = context.prev;
+          else
+            break;
+        }
+      }
+      while (context && context.prev && !context.startOfLine)
+        context = context.prev;
+      if (context) return context.indent + indentUnit;
+      else return state.baseIndent || 0;
+    },
+
+    electricInput: /<\/[\s\w:]+>$/,
+    blockCommentStart: "<!--",
+    blockCommentEnd: "-->",
+
+    configuration: config.htmlMode ? "html" : "xml",
+    helperType: config.htmlMode ? "html" : "xml",
+
+    skipAttribute: function(state) {
+      if (state.state == attrValueState)
+        state.state = attrState
+    }
+  };
+});
+
+CodeMirror.defineMIME("text/xml", "xml");
+CodeMirror.defineMIME("application/xml", "xml");
+if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
+  CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
+
+});

文件差异内容过多而无法显示
+ 67 - 0
lib/font-zy/iconfont.css


二进制
lib/font-zy/iconfont.eot


文件差异内容过多而无法显示
+ 101 - 0
lib/font-zy/iconfont.svg


二进制
lib/font-zy/iconfont.ttf


二进制
lib/font-zy/iconfont.woff


+ 486 - 0
lib/json/json2.js

@@ -0,0 +1,486 @@
+/**
+ * Created by zhang on 2018/9/4.
+ */
+/*
+ http://www.JSON.org/json2.js
+ 2010-03-20
+
+ Public Domain.
+
+ NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+ See http://www.JSON.org/js.html
+
+
+ This code should be minified before deployment.
+ See http://javascript.crockford.com/jsmin.html
+
+ USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+ NOT CONTROL.
+
+
+ This file creates a global JSON object containing two methods: stringify
+ and parse.
+
+ JSON.stringify(value, replacer, space)
+ value       any JavaScript value, usually an object or array.
+
+ replacer    an optional parameter that determines how object
+ values are stringified for objects. It can be a
+ function or an array of strings.
+
+ space       an optional parameter that specifies the indentation
+ of nested structures. If it is omitted, the text will
+ be packed without extra whitespace. If it is a number,
+ it will specify the number of spaces to indent at each
+ level. If it is a string (such as '\t' or '&nbsp;'),
+ it contains the characters used to indent at each level.
+
+ This method produces a JSON text from a JavaScript value.
+
+ When an object value is found, if the object contains a toJSON
+ method, its toJSON method will be called and the result will be
+ stringified. A toJSON method does not serialize: it returns the
+ value represented by the name/value pair that should be serialized,
+ or undefined if nothing should be serialized. The toJSON method
+ will be passed the key associated with the value, and this will be
+ bound to the value
+
+ For example, this would serialize Dates as ISO strings.
+
+ Date.prototype.toJSON = function (key) {
+ function f(n) {
+ // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+ }
+
+ return this.getUTCFullYear()   + '-' +
+ f(this.getUTCMonth() + 1) + '-' +
+ f(this.getUTCDate())      + 'T' +
+ f(this.getUTCHours())     + ':' +
+ f(this.getUTCMinutes())   + ':' +
+ f(this.getUTCSeconds())   + 'Z';
+ };
+
+ You can provide an optional replacer method. It will be passed the
+ key and value of each member, with this bound to the containing
+ object. The value that is returned from your method will be
+ serialized. If your method returns undefined, then the member will
+ be excluded from the serialization.
+
+ If the replacer parameter is an array of strings, then it will be
+ used to select the members to be serialized. It filters the results
+ such that only members with keys listed in the replacer array are
+ stringified.
+
+ Values that do not have JSON representations, such as undefined or
+ functions, will not be serialized. Such values in objects will be
+ dropped; in arrays they will be replaced with null. You can use
+ a replacer function to replace those with JSON values.
+ JSON.stringify(undefined) returns undefined.
+
+ The optional space parameter produces a stringification of the
+ value that is filled with line breaks and indentation to make it
+ easier to read.
+
+ If the space parameter is a non-empty string, then that string will
+ be used for indentation. If the space parameter is a number, then
+ the indentation will be that many spaces.
+
+ Example:
+
+ text = JSON.stringify(['e', {pluribus: 'unum'}]);
+ // text is '["e",{"pluribus":"unum"}]'
+
+
+ text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
+ // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+ text = JSON.stringify([new Date()], function (key, value) {
+ return this[key] instanceof Date ?
+ 'Date(' + this[key] + ')' : value;
+ });
+ // text is '["Date(---current time---)"]'
+
+
+ JSON.parse(text, reviver)
+ This method parses a JSON text to produce an object or array.
+ It can throw a SyntaxError exception.
+
+ The optional reviver parameter is a function that can filter and
+ transform the results. It receives each of the keys and values,
+ and its return value is used instead of the original value.
+ If it returns what it received, then the structure is not modified.
+ If it returns undefined then the member is deleted.
+
+ Example:
+
+ // Parse the text. Values that look like ISO date strings will
+ // be converted to Date objects.
+
+ myData = JSON.parse(text, function (key, value) {
+ var a;
+ if (typeof value === 'string') {
+ a =
+ /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+ if (a) {
+ return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+ +a[5], +a[6]));
+ }
+ }
+ return value;
+ });
+
+ myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+ var d;
+ if (typeof value === 'string' &&
+ value.slice(0, 5) === 'Date(' &&
+ value.slice(-1) === ')') {
+ d = new Date(value.slice(5, -1));
+ if (d) {
+ return d;
+ }
+ }
+ return value;
+ });
+
+
+ This is a reference implementation. You are free to copy, modify, or
+ redistribute.
+ */
+
+/*jslint evil: true, strict: false */
+
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
+ call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+ getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+ lastIndex, length, parse, prototype, push, replace, slice, stringify,
+ test, toJSON, toString, valueOf
+ */
+
+
+// Create a JSON object only if one does not already exist. We create the
+// methods in a closure to avoid creating global variables.
+
+if (!this.JSON) {
+    this.JSON = {};
+}
+
+(function () {
+
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10 ? '0' + n : n;
+    }
+
+    if (typeof Date.prototype.toJSON !== 'function') {
+
+        Date.prototype.toJSON = function (key) {
+
+            return isFinite(this.valueOf()) ?
+                this.getUTCFullYear()   + '-' +
+                f(this.getUTCMonth() + 1) + '-' +
+                f(this.getUTCDate())      + 'T' +
+                f(this.getUTCHours())     + ':' +
+                f(this.getUTCMinutes())   + ':' +
+                f(this.getUTCSeconds())   + 'Z' : null;
+        };
+
+        String.prototype.toJSON =
+            Number.prototype.toJSON =
+                Boolean.prototype.toJSON = function (key) {
+                    return this.valueOf();
+                };
+    }
+
+    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        gap,
+        indent,
+        meta = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        rep;
+
+
+    function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+        escapable.lastIndex = 0;
+        return escapable.test(string) ?
+            '"' + string.replace(escapable, function (a) {
+                var c = meta[a];
+                return typeof c === 'string' ? c :
+                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+            }) + '"' :
+            '"' + string + '"';
+    }
+
+
+    function str(key, holder) {
+
+// Produce a string from holder[key].
+
+        var i,          // The loop counter.
+            k,          // The member key.
+            v,          // The member value.
+            length,
+            mind = gap,
+            partial,
+            value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+        if (value && typeof value === 'object' &&
+            typeof value.toJSON === 'function') {
+            value = value.toJSON(key);
+        }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+        if (typeof rep === 'function') {
+            value = rep.call(holder, key, value);
+        }
+
+// What happens next depends on the value's type.
+
+        switch (typeof value) {
+            case 'string':
+                return quote(value);
+
+            case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+                return isFinite(value) ? String(value) : 'null';
+
+            case 'boolean':
+            case 'null':
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce 'null'. The case is included here in
+// the remote chance that this gets fixed someday.
+
+                return String(value);
+
+// If the type is 'object', we might be dealing with an object or an array or
+// null.
+
+            case 'object':
+
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
+// so watch out for that case.
+
+                if (!value) {
+                    return 'null';
+                }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+                gap += indent;
+                partial = [];
+
+// Is the value an array?
+
+                if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+// The value is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+                    length = value.length;
+                    for (i = 0; i < length; i += 1) {
+                        partial[i] = str(i, value) || 'null';
+                    }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+                    v = partial.length === 0 ? '[]' :
+                        gap ? '[\n' + gap +
+                            partial.join(',\n' + gap) + '\n' +
+                            mind + ']' :
+                            '[' + partial.join(',') + ']';
+                    gap = mind;
+                    return v;
+                }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+                if (rep && typeof rep === 'object') {
+                    length = rep.length;
+                    for (i = 0; i < length; i += 1) {
+                        k = rep[i];
+                        if (typeof k === 'string') {
+                            v = str(k, value);
+                            if (v) {
+                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                            }
+                        }
+                    }
+                } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+                    for (k in value) {
+                        if (Object.hasOwnProperty.call(value, k)) {
+                            v = str(k, value);
+                            if (v) {
+                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                            }
+                        }
+                    }
+                }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+                v = partial.length === 0 ? '{}' :
+                    gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+                        mind + '}' : '{' + partial.join(',') + '}';
+                gap = mind;
+                return v;
+        }
+    }
+
+// If the JSON object does not yet have a stringify method, give it one.
+
+    if (typeof JSON.stringify !== 'function') {
+        JSON.stringify = function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+            var i;
+            gap = '';
+            indent = '';
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+            if (typeof space === 'number') {
+                for (i = 0; i < space; i += 1) {
+                    indent += ' ';
+                }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+            } else if (typeof space === 'string') {
+                indent = space;
+            }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+            rep = replacer;
+            if (replacer && typeof replacer !== 'function' &&
+                (typeof replacer !== 'object' ||
+                typeof replacer.length !== 'number')) {
+                throw new Error('JSON.stringify');
+            }
+
+// Make a fake root object containing our value under the key of ''.
+// Return the result of stringifying the value.
+
+            return str('', {'': value});
+        };
+    }
+
+
+// If the JSON object does not yet have a parse method, give it one.
+
+    if (typeof JSON.parse !== 'function') {
+        JSON.parse = function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+            var j;
+
+            function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+                var k, v, value = holder[key];
+                if (value && typeof value === 'object') {
+                    for (k in value) {
+                        if (Object.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
+                            }
+                        }
+                    }
+                }
+                return reviver.call(holder, key, value);
+            }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+            text = String(text);
+            cx.lastIndex = 0;
+            if (cx.test(text)) {
+                text = text.replace(cx, function (a) {
+                    return '\\u' +
+                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+                });
+            }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+            if (/^[\],:{}\s]*$/.
+                test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
+                replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+                replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the third stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+                j = eval('(' + text + ')');
+
+// In the optional fourth stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+                return typeof reviver === 'function' ?
+                    walk({'': j}, '') : j;
+            }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+            throw new SyntaxError('JSON.parse');
+        };
+    }
+}());
+

文件差异内容过多而无法显示
+ 1 - 0
lib/lz-string/lz-string.min.js


+ 2 - 1
modules/all_models/bills_template_items.js

@@ -34,7 +34,8 @@ let BillsTemplateSchema = {
     //计算基数
     calcBase: String,
     //费率ID
-    feeRateID:Number
+    feeRateID:Number,
+    quantity: String,
 };
 
 mongoose.model(collectionName, new Schema(BillsTemplateSchema, {versionKey: false, collection: collectionName}));

+ 17 - 0
modules/all_models/compilation.js

@@ -51,10 +51,27 @@ let modelSchema = {
     creator: String,
     //描述
     description: String,
+    //代码覆盖路径
+    overWriteUrl:String,
     // 发布时间
     release_time: {
         type: Number,
         default: 0
+    },
+    //价格属性
+    priceProperties: {
+        type: Array,
+        default: []
+    },
+    //消耗量属性
+    consumeAmtProperties: {
+        type: Array,
+        default: []
+    },
+    // cld 办事处id
+    categoryID: {
+        type: Number,
+        default: 12 // 总部id
     }
 };
 mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));

+ 37 - 0
modules/all_models/compleRation_coe.js

@@ -0,0 +1,37 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2018/10/23
+ * @version
+ */
+
+/*
+* 补充定额库用户新增子目换算
+* 用户ID与费用定额ID绑定子目换算
+* */
+
+import mongoose from 'mongoose';
+const Schema = mongoose.Schema;
+const coeSchema = new Schema({
+    coeType: String,                // 系数类型
+    gljCode: String,                //要调整的人材机编码
+    gljName: String,
+    operator: String,               // 运算符(*、+、-、=)
+    amount: String,                 // 调整的量
+    _id: false
+});
+
+const coeListSchema = new Schema({
+    userId: String,
+    compilationId: String,
+    ID: String,                         // uuid
+    serialNo: Number,                  //编号
+    name: String,                       // 名称
+    content: String,                    // 说明
+    coes: [coeSchema]
+}, {versionKey: false});
+
+mongoose.model('complementary_ration_coe_list', coeListSchema, 'complementary_ration_coe_list');

+ 29 - 0
modules/all_models/compleRation_installFeeItem.js

@@ -0,0 +1,29 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2018/10/23
+ * @version
+ */
+
+/*
+* 补充定额库用户新增安装增加费费用项
+*
+* */
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+//补充安装增加费-费用项
+const installFeeItemSchema = new Schema({
+    userId: String,
+    compilationId: String,
+    ID: String,
+    feeItem: String, //费用项
+    feeType: String, //费用类型
+    position: String, //记取位置
+    section: [],
+    deleted: false
+}, {versionKey: false});
+
+mongoose.model('complementary_ration_installation', installFeeItemSchema, 'complementary_ration_installation');

+ 42 - 0
modules/all_models/compleRation_installSection.js

@@ -0,0 +1,42 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2018/10/23
+ * @version
+ */
+
+/*
+* 补充定额库用户新增安装增加费分册章节
+*
+*
+* */
+
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+//安装增加费-费用规则
+const feeRuleSchema = new Schema({
+    ID: String,
+    code: String,
+    rule: String,
+    base: String,
+    feeRate: Number,
+    labour: Number,
+    material: Number,
+    machine: Number
+});
+
+//补充安装增加费-分册章节
+const installSectionSchema = new Schema({
+    userId: String,
+    compilationId: String,
+    ID: String,
+    feeItemId: String,
+    name: String,
+    feeRule: [feeRuleSchema],
+    deleted: false
+}, {versionKey: false});
+
+mongoose.model('complementary_ration_installationSection', installSectionSchema, 'complementary_ration_installationSection');

+ 34 - 0
modules/all_models/comple_section_template.js

@@ -0,0 +1,34 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2018/10/23
+ * @version
+ */
+
+/*
+* 我的补充定额库章节树模板,一个费用定额有一个模板
+* 模板的生成目前由手工生成(借助定额库编辑器的章节树编辑)
+* 新用户第一次进入某费用定额的补充定额库时,拷贝模板给该用户
+*
+* */
+
+import mongoose from 'mongoose';
+
+const Schema = mongoose.Schema;
+const compleRationSectionTemp = new Schema({
+    compilationId: String,
+    name: String,
+    ID: Number,
+    ParentID: Number,
+    NextSiblingID: Number,
+    //以下预留结构,以防后续需要默认数据
+    explanation: String,//说明
+    ruleText: String,//计算规则,
+    jobContentSituation: String,//工作内容适用情况,ALL适用本项全部定额,PARTIAL适用本项部分定额
+    annotationSituation: String,//附注适用情况,ALL适用本项全部定额,PARTIAL适用本项部分定额
+});
+
+mongoose.model('complementary_ration_section_templates', compleRationSectionTemp, 'complementary_ration_section_templates');

+ 1 - 1
modules/all_models/counter.js

@@ -7,4 +7,4 @@ var counterSchema = new Schema({
     _id: String,
     sequence_value: Number
 });
-mongoose.model("counters", counterSchema);
+mongoose.model("counters_operation", counterSchema,"counters_operation");

+ 18 - 8
modules/all_models/engineering_lib.js

@@ -13,7 +13,8 @@ let taxGroupSchema = new  Schema({
     taxType: String,//计税方式
     program_lib: { type: Schema.Types.Mixed,default:{}},// 计算程序标准库
     template_lib:{ type: Schema.Types.Mixed,default:{}},//清单模板库
-    col_lib:{ type: Schema.Types.Mixed,default:{}}
+    col_lib:{ type: Schema.Types.Mixed,default:{}},//列设置
+    fee_lib:{ type: Schema.Types.Mixed,default:{}}//费率标准库
 },{_id: false});
 
 
@@ -42,16 +43,21 @@ let modelSchema = {
       type: [taxGroupSchema],
       default: []
     },
-    // 费率标准库
+   /* // 费率标准库 2018-08-28 改成放在tax_group 里了
     fee_lib: {
         type: Schema.Types.Mixed,
         default: []
-    },
+    },*/
     // 人工系数标准库
     artificial_lib: {
         type: Schema.Types.Mixed,
         default: []
     },
+    //工程特征库
+    feature_lib:{
+        type: Schema.Types.Mixed,
+        default: []
+    },
     //设置人材机显示列
     glj_col:{
         showAdjustPrice:Boolean//是否显示调整价列
@@ -60,12 +66,16 @@ let modelSchema = {
     valuationID:{type:String,index: true},
     //工程专业名称
     name:String,
+    //费用标准
+    feeName:String,
     //前端是否显示
-    visible:{
-        type: Boolean,
-        default: false
-    },
-    engineering:Number
+    visible:{type: Boolean, default: false},
+    //取费专业
+    engineering:Number,
+    //单位工程取费专业
+    projectEngineering:Number,
+    //是否计算安装增加费
+    isInstall:{type: Boolean, default: false}
 };
 mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));
 

+ 1 - 0
modules/all_models/manager.js

@@ -24,6 +24,7 @@ let modelSchema = {
         type: Number,
         default: 0
     },
+    real_name:String,
     // 创建时间
     create_time: {
         type: Number,

+ 53 - 0
modules/all_models/material_replace_lib.js

@@ -0,0 +1,53 @@
+/**
+ * Created by zhang on 2018/8/22.
+ */
+//材料替换库
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+const oprSchema = require('../all_schemas/opr_schema');
+const material_lib = new Schema({
+        ID:{type:String,index:true},
+        creator: String,
+        createDate: Number,
+        recentOpr: [oprSchema],
+        name: String,
+        compilationId: String,
+        compilationName: String,
+        billsLibId:Number,
+        billsLibName:String,
+        deleted: Boolean
+    }, {versionKey: false}
+);
+
+mongoose.model("std_material_replace_lib", material_lib,"std_material_replace_lib");
+
+
+const std_replace_bills = new Schema({
+        ID: {type:String,index:true},
+        libID:{type:String,index:true},
+        code: {type:String,index:true},
+        name: String,
+        rule:Number//规则类型
+    }, {versionKey: false}
+);
+
+mongoose.model('std_replace_bills', std_replace_bills, 'std_replace_bills');
+
+
+const std_replace_material = new Schema({
+    ID: {type:String,index:true},
+    libID:{type:String,index:true},
+    billsItemID:{type:String,index:true},
+    code: String,
+    name: String,
+    specs: String,
+    type: Number,
+    unit: String
+},{versionKey: false});
+
+mongoose.model('std_replace_material', std_replace_material, 'std_replace_material');
+
+
+
+
+

+ 22 - 0
modules/all_models/project_feature_lib.js

@@ -0,0 +1,22 @@
+/**
+ * Created by zhang on 2018/9/3.
+ */
+
+//工程特征库
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+const oprSchema = require('../all_schemas/opr_schema');
+const project_feature_lib = new Schema({
+        ID:{type:String,index:true},
+        creator: String,
+        createDate: Number,
+        recentOpr: [oprSchema],
+        name: String,
+        feature:{
+            type: [Schema.Types.Mixed],
+            default: []
+        }
+    }, {versionKey: false}
+);
+
+mongoose.model("std_project_feature_lib", project_feature_lib,"std_project_feature_lib");

+ 12 - 1
modules/all_models/rpt_template.js

@@ -27,6 +27,17 @@ let RptTemplateSchema = new Schema({
     "计算式_集合": Array
 });
 
-let Template = mongoose.model("rpt_templates", RptTemplateSchema, "rpt_templates");
+let RptTplExtCodeSchema = new Schema({
+    "Name": String,
+    "Type": String,
+    "Description": String,
+    "Template": String,
+    "items": Array
+});
+
+mongoose.model("rpt_templates", RptTemplateSchema, "rpt_templates");
+mongoose.model("rpt_ext_code_tpl", RptTplExtCodeSchema, "rpt_ext_code_tpl");
+
+//rpt_ext_code_tpl
 
 // export {Template as default};

+ 1 - 1
modules/all_models/stdBills_bills.js

@@ -17,7 +17,7 @@ const stdBills_bills = new Schema({
             jobs: [],
             items: [],
             recharge:String,
-            billsLibId: Number,
+            billsLibId: {type:Number,index:true},
             sectionInfo: Schema.Types.Mixed,
             deleted: Boolean
     },

+ 0 - 2
modules/all_models/stdBills_lib.js

@@ -12,8 +12,6 @@ const stdBills_lib = new Schema({
 
         billsLibId: Number,
         billsLibName: String,
-        compilationId: String,
-        compilationName: String,
         deleted: Boolean
     },
     {versionKey: false}

+ 11 - 2
modules/all_models/stdGlj_glj.js

@@ -8,7 +8,11 @@ const Schema = mongoose.Schema;
 const std_gljComponent = new Schema(
     {
         ID: Number,
-        consumeAmt: Number
+        consumeAmt: Number,
+        consumeAmtProperty: {
+            type: Schema.Types.Mixed,
+            default: {}
+        }
     },
     {_id: false},
     {versionKey: false}
@@ -22,6 +26,10 @@ const std_glj = new Schema({
     name: String,
     specs: String,
     basePrice: Number,
+    priceProperty: {
+        type: Schema.Types.Mixed,
+        default: {}
+    },
     gljClass: Number,
     gljType: Number,
     shortName: String,
@@ -29,7 +37,8 @@ const std_glj = new Schema({
     adjCoe: Number,
     component: [std_gljComponent],
     materialType: Number, //三材类型:钢材1、钢筋2、木材3、水泥4、标准砖5
-    materialCoe: Number //三材系数
+    materialCoe: Number, //三材系数
+    model: Number, //机型
 },{versionKey: false});
 
 mongoose.model('std_glj_lib_gljList', std_glj, 'std_glj_lib_gljList');

+ 2 - 0
modules/all_models/stdRation_coe.js

@@ -10,6 +10,8 @@ const coeSchema = new Schema({
     gljID: Number,                  // 要调整的工料机ID(当coeType=0时有效)
     operator: String,               // 运算符(*、+、-、=)
     amount: String,                 // 调整的量
+    gljCode: String,
+    gljName: String,
     _id: false
 });
 

+ 4 - 1
modules/all_models/stdRation_ration.js

@@ -34,8 +34,11 @@ const rationItemSchema = new Schema({
     name: String,
     unit: String,
     basePrice: Number,
+    labourPrice: Number,
+    materialPrice: Number,
+    machinePrice: Number,
     sectionId: Number,
-    rationRepId: Number,
+    rationRepId: {type: Number, index: true},
     caption: String,
     feeType: Number,
     jobContent: String,

+ 1 - 0
modules/all_models/std_billsGuidance_items.js

@@ -18,6 +18,7 @@ const stdBillsGuidanceItems = new Schema({
     NextSiblingID: String,
     billsID: String, //关联清单的ID
     name: String,
+    comment: String, //备注
     type: Number, //0:工作内容 1:定额
     rationID: {type: Number, default: null}, //定额类型时
     deleted: {type: Boolean, default: false}

+ 1 - 0
modules/all_models/std_billsGuidance_lib.js

@@ -12,6 +12,7 @@ const mongoose = require('mongoose');
 const Schema = mongoose.Schema;
 
 const stdBillsGuidanceLib = new Schema({
+    type: Number, //1:清单指引 2:清单精灵
     ID: String, //uuid
     compilationId: String,
     compilationName: String,

+ 7 - 3
modules/common/std/schemas/std_calc_program.js

@@ -9,6 +9,7 @@ import mongoose from "mongoose";
 
 let Schema = mongoose.Schema;
 let collectionName = 'std_calc_programs';
+const oprSchema = require('../all_schemas/opr_schema');
 let modelSchema = {
     // 自增id
     ID: Number,
@@ -23,9 +24,12 @@ let modelSchema = {
         type: String,
         index: true
     },
+    compilationName:String,
     // 模板数据
-    templates: Schema.Types.Mixed
+    templates: {type:[Schema.Types.Mixed],default:[]},
+    creator: String,
+    createDate: Number,
+    recentOpr: [oprSchema]
 };
 
-let model = mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));
-export {model as default, collectionName as collectionName};
+mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));

+ 8 - 3
modules/common/std/schemas/std_fee_rate_libs.js

@@ -9,6 +9,7 @@ import mongoose from "mongoose";
 
 let Schema = mongoose.Schema;
 let collectionName = 'std_fee_rate_libs';
+const oprSchema = require('../all_schemas/opr_schema');
 
 let optionSchema = new Schema({
     name:String,
@@ -53,9 +54,13 @@ let modelSchema = {
         type: String,
         index: true
     },
+    compilationName:String,
     // 费率数据
-    rates: [ratesSchema]
+    rates: {type:[ratesSchema],default:[]},
+    creator: String,
+    createDate: Number,
+    recentOpr: [oprSchema]
 };
 
-let model = mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));
-export {model as default, collectionName as collectionName};
+ mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));
+

+ 12 - 1
modules/all_models/user.js

@@ -10,6 +10,14 @@ import mongoose from "mongoose";
 
 let Schema = mongoose.Schema;
 let collectionName = 'users';
+let upgrade = mongoose.Schema({
+    compilationID:String,//编办ID
+    upgrade_time:Number,
+    isUpgrade:Boolean,
+    remark:String//描述:广东办刘飞 2018-06-17 启用/关闭
+}, { _id: false })
+
+
 let modelSchema = {
     // 用户名
     username: String,
@@ -34,15 +42,18 @@ let modelSchema = {
         default: -1
     },
     // 最后登录时间
-    last_login: {
+    latest_login: {
         type: Number,
         default: 0
     },
+    //最近使用编办
+    latest_used:String,
     // 创建时间
     create_time: {
         type: Number,
         default: 0
     },
+    upgrade_list:[upgrade],
     user_type:{
         type:String,
         default:'normal'//  normal : 普通用户,test:测试用户

+ 6 - 0
modules/bills_lib/controllers/bills_lib_controllers.js

@@ -150,6 +150,12 @@ module.exports = {
             callback(req, res, err, message, null);
         });
     },
+    isUsed: function(req, res){
+        let data = JSON.parse(req.body.data);
+        billsLibDao.isUsed(data, function (err, message, datas) {
+            callback(req, res, err, message, datas);
+        });
+    },
     getJobContent: function(req, res){
         let data = JSON.parse(req.body.data);
         billsLibDao.getJobContent(data, function(err, message, jobs){

+ 116 - 0
modules/bills_lib/controllers/bills_permissionController.js

@@ -3,6 +3,14 @@
  */
 let billsController = require("./bills_lib_controllers");
 import baseController from "../../common/base/base_controller";
+import fs from 'fs';
+import path from 'path';
+import multiparty from 'multiparty';
+const uuidV1 = require('uuid/v1');
+
+const shareDir = 'public/share/images';
+const imgTriggers = ['billsRecharge', 'rationExplanation', 'rationRuleText'];
+
 
 class billsPermContr extends baseController{
     getCurrentUniqId(req, res){
@@ -56,6 +64,114 @@ class billsPermContr extends baseController{
     deleteBills(req, res){
         billsController.deleteBills(req, res);
     }
+    isUsed(req, res){
+        billsController.isUsed(req, res);
+    }
+    /*
+    *上传图片
+    * */
+    uploadImg(req, res){
+        let uploadDir = path.join(req.app.locals.rootDir, shareDir);
+        let form = new multiparty.Form({uploadDir: uploadDir});
+        form.parse(req, async function(err, fields, files){
+            try {
+                const file = typeof files.file !== 'undefined' ? files.file[0] : null;
+                if(err || !file) {
+                    throw '上传失败';
+                }
+                //触发上传图片的地方
+                const trigger = typeof fields.trigger !== 'undefined' && fields.trigger.length > 0 ? fields.trigger[0] : null;
+                if (!trigger || !imgTriggers.includes(trigger)) {
+                    throw '该操作没有上传权限'
+                }
+                //根据触发源放置文件到相关文件夹
+                let triggerPath = path.join(uploadDir, trigger);
+                if (!fs.existsSync(triggerPath)) {
+                    fs.mkdirSync(triggerPath);
+                }
+                //文件后缀
+                let px = file.originalFilename.split('.').pop();
+                //时间戳重命名图片并移动文件
+                let newFileName = Date.now() + '.' + px;
+                if (fs.existsSync(newPath)) {
+                    newFileName += uuidV1();
+                }
+                let newPath = path.join(triggerPath, newFileName);
+                fs.renameSync(file.path, newPath);
+                //返回图片域名后的url
+                res.json({error: 0, data: `${shareDir}/${trigger}/${newFileName}`, message: 'success'});
+            } catch (err) {
+                console.log(err);
+                res.json({error: 1, data: null, message: err});
+            }
+        });
+    }
+    /*
+    * 删除图片
+    * */
+    delImg(req, res){
+        try {
+            let data = JSON.parse(req.body.data),
+                trigger = data.trigger,
+                url = data.url;
+            if (!imgTriggers.includes(trigger)) {
+                throw '该操作没有权限';
+            }
+            //拼接完整的删除路径
+            let delUrl = path.join(req.app.locals.rootDir, url);
+            fs.unlinkSync(delUrl);
+            res.json({error: 0, data: null, message: 'success'});
+        } catch(err) {
+            res.json({error: 1, data: null, message: err});
+        }
+    }
+    /*
+     *  根据页码加载图片
+     *  返回总页码和当前页码的图片信息
+     * */
+    loadImgs(req, res){
+        try {
+            let data = JSON.parse(req.body.data),
+                trigger = data.trigger,
+                page = data.page;
+            if (!imgTriggers.includes(trigger)) {
+                throw '该操作没有权限';
+            }
+            //每页显示的图片数
+            const perImgs = 3;
+            //总的图片信息
+            let triggerPath = path.join(req.app.locals.rootDir, shareDir, trigger);
+            if (!fs.existsSync(triggerPath)) {
+                fs.mkdirSync(triggerPath);
+            }
+            let allImgs = fs.readdirSync(triggerPath);
+            //将图片按照时间排序,降序
+            allImgs.sort(function (a, b) {
+                let aV = a.split('.')[0],
+                    bV = b.split('.')[0];
+                    if(aV > bV) {
+                        return -1;
+                    } else if(aV < bV) {
+                        return 1;
+                    }
+                    return 0;
+            });
+            let pageCount = Math.ceil(allImgs.length / perImgs) || 1;
+            //当前页图片信息
+            let startIdx = (page - 1) * perImgs,
+                endIdx = startIdx + 3,
+                currentPageArr = allImgs.slice(startIdx, endIdx),
+                currentImgsUrl = [];
+            for (let img of currentPageArr){
+                currentImgsUrl.push(`${shareDir}/${trigger}/${img}`);
+            }
+            res.json({error: 0, data: {pageCount: pageCount, currentImgsUrl: currentImgsUrl}, message: 'success'});
+
+        } catch (err) {
+            console.log(err);
+            res.json({error: 1, data: {pageCount: 1, currentImgsUrl: []}, message: err});
+        }
+    }
 
 }
 

+ 4 - 4
modules/bills_lib/controllers/views_permissionController.js

@@ -3,7 +3,7 @@
  * Created by Zhong on 2017/8/2.
  */
 import baseController from "../../common/base/base_controller";
-
+let config = require("../../../config/config.js");
 class viewsPermContr extends baseController{
     redirectStdBillsMain(req, res){
         res.render("maintain/bills_lib/html/main.html",
@@ -12,15 +12,15 @@ class viewsPermContr extends baseController{
     }
     redirectStdBills(req, res){
         res.render("maintain/bills_lib/html/qingdan.html",
-            {userAccount: req.session.managerData.username});
+            {userAccount: req.session.managerData.username, LicenseKey:config.getLicenseKey(process.env.NODE_ENV)});
     }
     redirectStdJobs(req, res){
         res.render('maintain/bills_lib/html/neirong.html',
-            {userAccount: req.session.managerData.username});
+            {userAccount: req.session.managerData.username, LicenseKey:config.getLicenseKey(process.env.NODE_ENV)});
     }
     redirectStdItems(req, res){
         res.render('maintain/bills_lib/html/tezheng.html',
-            {userAccount: req.session.managerData.username});
+            {userAccount: req.session.managerData.username, LicenseKey:config.getLicenseKey(process.env.NODE_ENV)});
     }
 }
 

+ 34 - 14
modules/bills_lib/models/bills_lib_interfaces.js

@@ -70,8 +70,6 @@ billsLibDao.prototype.createStdBillsLib = function(clibData, callback){
         let billsLibId = result.sequence_value;
         let userAccount = clibData.userAccount;
         let billsLibName = clibData.name;
-        let compilationId = clibData.compilationId;
-        let compilationName = clibData.compilationName;
         let dateStr = moment().format('YYYY-MM-DD HH:mm:ss');
         let newStdBillsLib = {
             creator: userAccount,
@@ -79,8 +77,6 @@ billsLibDao.prototype.createStdBillsLib = function(clibData, callback){
             recentOpr: [{operator: userAccount, operateDate: dateStr}],
             billsLibId: billsLibId,
             billsLibName: billsLibName,
-            compilationId: compilationId,
-            compilationName: compilationName,
             deleted: false
         };
         StdBillsLib.create(newStdBillsLib, function(err){
@@ -882,23 +878,23 @@ billsLibDao.prototype.removeTotal = function (data, callback) {
         delItems: function () {
             return function (cb) {
                 async.each(delIds, function (delItemId, ecb) {
-                    ItemCharacter.remove({billsLibId: billsLibId, id: delItemId}, function (err) {
+                    ItemCharacter.remove({billsLibId: billsLibId, id: delItemId}, function (err, result) {
                         if(err){
                             ecb(err);
                         }
-                        else{
+                        else {
                             ecb(null);
                         }
-                    }, function (err) {
-                        if(err){
-                            cb(err);
-                        }
-                        else{
-                            cb(null);
-                        }
                     });
+                }, function (err) {
+                    if(err){
+                        cb(err);
+                    }
+                    else{
+                        cb(null);
+                    }
                 });
-            }
+            };
         },
         delItemsArr: function () {
             return function (cb) {
@@ -1828,12 +1824,36 @@ billsLibDao.prototype.deleteBills = function(delData, callback){
     }
 }
 
+billsLibDao.prototype.isUsed = function (data, callback) {
+    if(data.field === 'jobs'){
+        Bills.findOne({$or: [{deleted: null}, {deleted: false}], 'jobs.id': {$in: data.delIds}}, function (err, result) {
+            if(err){
+                callback(1, 'error', null);
+            }
+            else {
+                callback(0, '', {isUsed: result ? true : false});
+            }
+        });
+    }
+    else {
+        Bills.findOne({$or: [{deleted: null}, {deleted: false}], 'items.id': {$in: data.delIds}}, function (err, result) {
+            if(err){
+                callback(1, 'error', null);
+            }
+            else {
+                callback(0, '', {isUsed: result ? true : false});
+            }
+        });
+    }
+};
+
 //--------------JobContent------------------
 
 billsLibDao.prototype.getJobContent = function(gJobData, callback){
     let billsLibId = gJobData.billsLibId;
     JobContent.find({billsLibId: billsLibId, deleted: false}, '-_id').sort({code: 1}).exec(function(err, result){
         if(err){
+            console.log(err);
             callback(1, 'Error', null);
         }
         else {

+ 47 - 40
modules/bills_lib/routes/bills_lib_routes.js

@@ -17,57 +17,64 @@ let itemsContr = new ItemsPermContr();
 
 module.exports =function (app) {
     app.get("/stdBillsmain", viewsContr.auth, viewsContr.init, viewsContr.redirectStdBillsMain);
-     app.get("/stdBills", viewsContr.auth, viewsContr.init, viewsContr.redirectStdBills);
-     app.get('/stdJobs', viewsContr.auth, viewsContr.init, viewsContr.redirectStdJobs);
-     app.get('/stdItems', viewsContr.auth, viewsContr.init, viewsContr.redirectStdItems);
+    app.get("/stdBills", viewsContr.auth, viewsContr.init, viewsContr.redirectStdBills);
+    app.get('/stdJobs', viewsContr.auth, viewsContr.init, viewsContr.redirectStdJobs);
+    app.get('/stdItems', viewsContr.auth, viewsContr.init, viewsContr.redirectStdItems);
 
-     billsRouter.post('/getCompilationList', billsLibContr.auth, billsLibContr.init, billsLibContr.getCompilationList);
-     billsRouter.post('/getMaxNumber', billsLibContr.auth, billsLibContr.init, billsLibContr.getMaxNumber);
-     billsRouter.post('/getABillsLib', billsLibContr.auth, billsLibContr.init, billsLibContr.getABillsLib);
-     billsRouter.post("/getStdBillsLib", billsLibContr.auth, billsLibContr.init, billsLibContr.getStdBillsLib);
-     billsRouter.post("/createStdBillsLib", billsLibContr.auth, billsLibContr.init, billsLibContr.createStdBillsLib);
+    billsRouter.post('/getCompilationList', billsLibContr.auth, billsLibContr.init, billsLibContr.getCompilationList);
+    billsRouter.post('/getMaxNumber', billsLibContr.auth, billsLibContr.init, billsLibContr.getMaxNumber);
+    billsRouter.post('/getABillsLib', billsLibContr.auth, billsLibContr.init, billsLibContr.getABillsLib);
+    billsRouter.post("/getStdBillsLib", billsLibContr.auth, billsLibContr.init, billsLibContr.getStdBillsLib);
+    billsRouter.post("/createStdBillsLib", billsLibContr.auth, billsLibContr.init, billsLibContr.createStdBillsLib);
     billsRouter.post("/deleteStdBillsLib", billsLibContr.auth, billsLibContr.init, billsLibContr.deleteStdBillsLib);
     billsRouter.post("/renameStdBillsLib", billsLibContr.auth, billsLibContr.init, billsLibContr.renameStdBillsLib);
     billsRouter.post("/getStdBillsLibName", billsLibContr.auth, billsLibContr.init, billsLibContr.getStdBillsLibName);
+    //上传图片
+    billsRouter.post('/uploadImg', billsContr.uploadImg);
+    //删除图片
+    billsRouter.post('/delImg', billsContr.delImg);
+    //加载图片
+    billsRouter.post('/loadImgs', billsContr.loadImgs);
 
     billsRouter.post("/upMove", billsContr.auth, billsContr.init, billsContr.upMove);
     billsRouter.post("/downMove", billsContr.auth, billsContr.init, billsContr.downMove);
-     billsRouter.post("/getCurrentUniqId", billsContr.auth, billsContr.init, billsContr.getCurrentUniqId);
-     billsRouter.post("/getBills", billsContr.auth, billsContr.init, billsContr.getBills);
-     billsRouter.post("/createBills", billsContr.auth, billsContr.init, billsContr.createBills);
-     billsRouter.post("/updatePNId", billsContr.auth, billsContr.init, billsContr.updatePNId);
-     billsRouter.post("/upLevel", billsContr.auth, billsContr.init, billsContr.upLevel);
-     billsRouter.post("/downLevel", billsContr.auth, billsContr.init, billsContr.downLevel);
-     billsRouter.post("/updateBills", billsContr.auth, billsContr.init, billsContr.updateBills);
-     billsRouter.post("/updateSectionInfo", billsContr.auth, billsContr.init, billsContr.updateSectionInfo);
-     billsRouter.post("/updateBillsArr", billsContr.auth, billsContr.init, billsContr.updateBillsArr);
-     billsRouter.post("/removeTotal", billsContr.auth, billsContr.init, billsContr.removeTotal);
-     billsRouter.post("/updateSerialNo", billsContr.auth, billsContr.init, billsContr.updateSerialNo);
-     billsRouter.post("/pasteBills", billsContr.auth, billsContr.init, billsContr.pasteBills);
-     billsRouter.post('/updateRecharge', billsContr.auth, billsContr.init, billsContr.updateRecharge);
-     billsRouter.post('/pasteRel', billsContr.auth, billsContr.init, billsContr.pasteRel);
-     billsRouter.post("/deleteBills", billsContr.auth, billsContr.init, billsContr.deleteBills);
+    billsRouter.post("/getCurrentUniqId", billsContr.auth, billsContr.init, billsContr.getCurrentUniqId);
+    billsRouter.post("/getBills", billsContr.auth, billsContr.init, billsContr.getBills);
+    billsRouter.post("/createBills", billsContr.auth, billsContr.init, billsContr.createBills);
+    billsRouter.post("/updatePNId", billsContr.auth, billsContr.init, billsContr.updatePNId);
+    billsRouter.post("/upLevel", billsContr.auth, billsContr.init, billsContr.upLevel);
+    billsRouter.post("/downLevel", billsContr.auth, billsContr.init, billsContr.downLevel);
+    billsRouter.post("/updateBills", billsContr.auth, billsContr.init, billsContr.updateBills);
+    billsRouter.post("/updateSectionInfo", billsContr.auth, billsContr.init, billsContr.updateSectionInfo);
+    billsRouter.post("/updateBillsArr", billsContr.auth, billsContr.init, billsContr.updateBillsArr);
+    billsRouter.post("/removeTotal", billsContr.auth, billsContr.init, billsContr.removeTotal);
+    billsRouter.post("/updateSerialNo", billsContr.auth, billsContr.init, billsContr.updateSerialNo);
+    billsRouter.post("/pasteBills", billsContr.auth, billsContr.init, billsContr.pasteBills);
+    billsRouter.post('/updateRecharge', billsContr.auth, billsContr.init, billsContr.updateRecharge);
+    billsRouter.post('/pasteRel', billsContr.auth, billsContr.init, billsContr.pasteRel);
+    billsRouter.post("/deleteBills", billsContr.auth, billsContr.init, billsContr.deleteBills);
+    billsRouter.post("/isUsed", billsContr.auth, billsContr.init, billsContr.isUsed);
 
-     billsRouter.post("/getJobContent", jobsContr.auth, jobsContr.init, jobsContr.getJobContent);
-     billsRouter.post("/createJobContent", jobsContr.auth, jobsContr.init, jobsContr.createJobContent);
-     billsRouter.post("/updateJobContent", jobsContr.auth, jobsContr.init, jobsContr.updateJobContent);
-     billsRouter.post("/deleteJobContent", jobsContr.auth, jobsContr.init, jobsContr.deleteJobContent);
-     billsRouter.post("/pasteJobs", jobsContr.auth, jobsContr.init, jobsContr.pasteJobs);
-     billsRouter.post("/edCreateJob", jobsContr.auth, jobsContr.init, jobsContr.edCreateJob);
-     billsRouter.post("/edUpdateJob", jobsContr.auth, jobsContr.init, jobsContr.edUpdateJob);
+    billsRouter.post("/getJobContent", jobsContr.auth, jobsContr.init, jobsContr.getJobContent);
+    billsRouter.post("/createJobContent", jobsContr.auth, jobsContr.init, jobsContr.createJobContent);
+    billsRouter.post("/updateJobContent", jobsContr.auth, jobsContr.init, jobsContr.updateJobContent);
+    billsRouter.post("/deleteJobContent", jobsContr.auth, jobsContr.init, jobsContr.deleteJobContent);
+    billsRouter.post("/pasteJobs", jobsContr.auth, jobsContr.init, jobsContr.pasteJobs);
+    billsRouter.post("/edCreateJob", jobsContr.auth, jobsContr.init, jobsContr.edCreateJob);
+    billsRouter.post("/edUpdateJob", jobsContr.auth, jobsContr.init, jobsContr.edUpdateJob);
 
-     billsRouter.post("/getItemCharacter", itemsContr.auth, itemsContr.init, itemsContr.getItemCharacter);
-     billsRouter.post("/createItemCharacter", itemsContr.auth, itemsContr.init, itemsContr.createItemCharacter);
-     billsRouter.post("/updateItemCharacter", itemsContr.auth, itemsContr.init, itemsContr.updateItemCharacter);
-     billsRouter.post("/updateValue", itemsContr.auth, itemsContr.init, itemsContr.updateValue);
-     billsRouter.post("/deleteItemCharacter", itemsContr.auth, itemsContr.init, itemsContr.deleteItemCharacter);
-     billsRouter.post("/pasteItems", itemsContr.auth, itemsContr.init, itemsContr.pasteItems);
-     billsRouter.post("/pasteValues", itemsContr.auth, itemsContr.init, itemsContr.pasteValues);
-     billsRouter.post("/edCreateItem", itemsContr.auth, itemsContr.init, itemsContr.edCreateItem);
-     billsRouter.post("/edUpdateItem", itemsContr.auth, itemsContr.init, itemsContr.edUpdateItem);
+    billsRouter.post("/getItemCharacter", itemsContr.auth, itemsContr.init, itemsContr.getItemCharacter);
+    billsRouter.post("/createItemCharacter", itemsContr.auth, itemsContr.init, itemsContr.createItemCharacter);
+    billsRouter.post("/updateItemCharacter", itemsContr.auth, itemsContr.init, itemsContr.updateItemCharacter);
+    billsRouter.post("/updateValue", itemsContr.auth, itemsContr.init, itemsContr.updateValue);
+    billsRouter.post("/deleteItemCharacter", itemsContr.auth, itemsContr.init, itemsContr.deleteItemCharacter);
+    billsRouter.post("/pasteItems", itemsContr.auth, itemsContr.init, itemsContr.pasteItems);
+    billsRouter.post("/pasteValues", itemsContr.auth, itemsContr.init, itemsContr.pasteValues);
+    billsRouter.post("/edCreateItem", itemsContr.auth, itemsContr.init, itemsContr.edCreateItem);
+    billsRouter.post("/edUpdateItem", itemsContr.auth, itemsContr.init, itemsContr.edUpdateItem);
 
     app.use("/stdBillsEditor", billsRouter);
 
-}
+};
 
 

+ 5 - 1
modules/bills_template_lib/controllers/bills_template_controller.js

@@ -9,6 +9,7 @@ import BaseController from "../../common/base/base_controller";
 import billsTemplateFacade from "../facade/bills_template_facade";
 import {default as BillsFixedFlagConst, List as BillsFixedFlagList} from "../../common/const/bills_fixed.js";
 import {default as BillsTypeFlagConst, List as BillsTypeFlagList} from "../../common/const/bills_type.js";
+let config = require("../../../config/config.js");
 
 class BillsTemplateController extends BaseController {
 
@@ -26,7 +27,8 @@ class BillsTemplateController extends BaseController {
             userAccount: request.session.managerData.username,
             userID: request.session.managerData.userID,
             templateLibs:templateLibs,
-            layout: 'maintain/common/html/layout'
+            layout: 'maintain/common/html/layout',
+            LicenseKey:config.getLicenseKey(process.env.NODE_ENV)
         };
 
         response.render("maintain/bill_template_lib/html/main", randerData);
@@ -45,6 +47,7 @@ class BillsTemplateController extends BaseController {
 
         }catch (err){
             console.log(err);
+            result.message = err.message;
         }
         response.json(result);
     }
@@ -68,6 +71,7 @@ class BillsTemplateController extends BaseController {
                 billsFixedFlagList: JSON.stringify(BillsFixedFlagList),
                 billsTypeFlagList: JSON.stringify(BillsTypeFlagList),
                 libID:libID,
+                LicenseKey:config.getLicenseKey(process.env.NODE_ENV),
                 layout: 'maintain/common/html/edit_layout'
             };
             response.render("maintain/bill_template_lib/html/edit", randerData);

+ 2 - 0
modules/bills_template_lib/facade/bills_template_facade.js

@@ -53,6 +53,8 @@ async function getTemplateDatasByLibID(libID) {
     return await billTemplateItemsModel.find({libID:libID});
 }
 async function deleteLibByID(ID){
+    //删除模板详情:
+    await billTemplateItemsModel.deleteMany({libID:ID});
     return billTemplateLibModel.deleteOne({ID:ID});
 }
 

+ 100 - 0
modules/calc_program_lib/controllers/calc_program_controller.js

@@ -0,0 +1,100 @@
+/**
+ * Created by zhang on 2018/9/11.
+ */
+import BaseController from "../../common/base/base_controller";
+let config = require("../../../config/config.js");
+import calcProgramFacade from "../facade/calc_program_facade";
+
+class CalcProgramController extends BaseController {
+    async main(request, response) {
+        let calcProgramLibs = await calcProgramFacade.findByCondition({}, {templates: 0}, false);
+        let randerData = {
+            title: '计算程序模板库',
+            userAccount: request.session.managerData.username,
+            userID: request.session.managerData.userID,
+            calcProgramLibs: calcProgramLibs,
+            layout: 'maintain/common/html/layout'
+        };
+        response.render("maintain/calc_program_lib/html/main", randerData);
+    }
+    async addLib(request, response){
+        try {
+            await calcProgramFacade.addLib(request.body);
+        }catch (error) {
+            console.log(error);
+        }
+        response.redirect(request.headers.referer);
+    }
+    async findLib(request, response){
+        let result={
+            error:0
+        };
+        try {
+            let data = request.body.data;
+            data = JSON.parse(data);
+            let conditions={ID:data.ID};
+            let resultData = await calcProgramFacade.findByCondition(conditions);
+            result.data=resultData;
+        }catch (err){
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        response.json(result);
+    }
+    async saveLib(request, response){
+        let result={
+            error:0
+        };
+        try {
+            let data = request.body.data;
+            data = JSON.parse(data);
+            let resultData= await calcProgramFacade.saveLib(data);
+            result.data=resultData;
+        }catch (err){
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        response.json(result);
+    }
+    async deleteLibByID(request,response){
+        let result={
+            error:0
+        };
+        try {
+            let data = request.body.data;
+            data = JSON.parse(data);
+            let resultData= await calcProgramFacade.deleteLibByID(data.ID);
+            result.data=resultData;
+        }catch (err){
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        response.json(result);
+    }
+    async edit(request,response){
+        //先取出替换库信息:
+        let libID = request.params.libID;
+        let programLib = await calcProgramFacade.findByCondition({'ID':libID});
+        if(programLib){
+            let randerData = {
+                title:'计算程序模板库',
+                mainURL:'/calcProgram/main',
+                libName:programLib.libName,
+                userAccount: request.session.managerData.username,
+                userID: request.session.managerData.userID,
+                templateList:JSON.stringify(programLib.templates),
+                libID:libID,
+                LicenseKey:config.getLicenseKey(process.env.NODE_ENV),
+                layout: 'maintain/common/html/edit_layout'
+            };
+            response.render("maintain/calc_program_lib/html/edit", randerData);
+        }else {
+            response.redirect(request.headers.referer);
+        }
+    }
+}
+
+export default CalcProgramController;

+ 54 - 0
modules/calc_program_lib/facade/calc_program_facade.js

@@ -0,0 +1,54 @@
+/**
+ * Created by zhang on 2018/9/11.
+ */
+
+import mongoose from "mongoose";
+
+const uuidV1 = require('uuid/v1');
+let moment = require("moment");
+let calcProgramModel = mongoose.model("std_calc_programs");
+let compilationModel = mongoose.model("compilation");
+const _ = require('lodash');
+let counter = require('../../../public/counter/counter');
+
+let calcProgramLib = {
+    findByCondition:async function(conditions,options,single=true){
+        if(single == true){
+            return await calcProgramModel.findOne(conditions,options);
+        }else {
+            return await  calcProgramModel.find(conditions,options);
+        }
+    },
+    addLib : async function (data){
+        let now = new Date().getTime();
+        let dateStr = moment(now).format('YYYY-MM-DD HH:mm:ss');
+        //取编办信息
+        let compilation = await compilationModel.findOne({_id:data.compilationId});
+        if(compilation){
+            let IDcounter = await counter.counterDAO.getIDAfterCount(counter.moduleName.stdCalcProgram,1);
+            let newLib = {
+                creator: data.userAccount,
+                createDate: now,
+                recentOpr: [{operator: data.userAccount, operateDate: dateStr}],
+                libName: data.name,
+                displayName:data.displayName,
+                compilationId: data.compilationId,
+                compilationName: compilation.name,
+                templates:[]
+            };
+            newLib.ID = IDcounter.sequence_value;
+            return await calcProgramModel.create(newLib);
+        }else {
+            throw  new Error("没有找到该编办!");
+        }
+
+    },
+    saveLib:async function(param) {
+        return await calcProgramModel.findOneAndUpdate(param.query,param.data,{new:true});
+    },
+    deleteLibByID:async function(ID){
+        return await calcProgramModel.deleteOne({ID:ID});
+    },
+};
+
+export default calcProgramLib

+ 19 - 0
modules/calc_program_lib/routes/calc_program_routes.js

@@ -0,0 +1,19 @@
+/**
+ * Created by zhang on 2018/9/11.
+ */
+
+let express = require("express");
+let calcProgramRouter =express.Router();
+import CalcProgramController from "../controllers/calc_program_controller";
+let calcProgramController = new CalcProgramController();
+
+module.exports =function (app) {
+
+    calcProgramRouter.get("/main", calcProgramController.auth, calcProgramController.init, calcProgramController.main);
+    calcProgramRouter.post("/addLib", calcProgramController.auth, calcProgramController.init, calcProgramController.addLib);
+    calcProgramRouter.post("/findLib", calcProgramController.auth, calcProgramController.init, calcProgramController.findLib);
+    calcProgramRouter.post("/saveLib", calcProgramController.auth, calcProgramController.init, calcProgramController.saveLib);
+    calcProgramRouter.post("/deleteLibByID", calcProgramController.auth, calcProgramController.init, calcProgramController.deleteLibByID);
+    calcProgramRouter.get("/edit/:libID", calcProgramController.auth, calcProgramController.init, calcProgramController.edit);
+    app.use("/calcProgram", calcProgramRouter);
+};

+ 4 - 0
modules/common/base/base_model.js

@@ -129,6 +129,10 @@ class BaseModel {
         return result.ok !== undefined && result.ok === 1;
     }
 
+    isDef(value){
+        return value !==undefined && value !==null
+    }
+
 }
 
 export default BaseModel;

+ 6 - 3
modules/common/const/bills_fixed.js

@@ -40,14 +40,16 @@ const fixedFlag = {
     // 税金
     TAX: 18,
     //工程造价
-    ENGINEERINGCOST: 19
+    ENGINEERINGCOST: 19,
+    //增值税
+    ADDED_VALUE_TAX: 20
 };
 const fixedFlagList = [
     {name: "分部分项工程", value: fixedFlag.SUB_ENGINERRING},
     {name: "措施项目", value: fixedFlag.MEASURE},
     {name: "施工技术措施项目", value: fixedFlag.CONSTRUCTION_TECH},
     {name: "安全文明施工按实计算费用", value: fixedFlag.SAFETY_CONSTRUCTION_ACTUAL},
-    {name: "施工组织措施专项费用", value: fixedFlag.CONSTRUCTION_ORGANIZATION},
+    {name: "施工组织措施项目", value: fixedFlag.CONSTRUCTION_ORGANIZATION},
     {name: "安全文明施工专项费用", value: fixedFlag.SAFETY_CONSTRUCTION},
     {name: "其他项目", value: fixedFlag.OTHER},
     {name: "暂列金额", value: fixedFlag.PROVISIONAL},
@@ -61,7 +63,8 @@ const fixedFlagList = [
     {name: "社会保险费及住房公积金", value: fixedFlag.SOCIAL_INSURANCE_HOUSING_FUND},
     {name: "工程排污费", value: fixedFlag.POLLUTANTS},
     {name: "税金", value: fixedFlag.TAX},
-    {name: "工程造价", value: fixedFlag.ENGINEERINGCOST}
+    {name: "工程造价", value: fixedFlag.ENGINEERINGCOST},
+    {name: "增值税", value: fixedFlag.ADDED_VALUE_TAX}
 ];
 
 export {fixedFlag as default, fixedFlagList as List};

+ 67 - 0
modules/common/const/category_const.js

@@ -0,0 +1,67 @@
+'use strict';
+
+/**
+ * CLD 办事处常量
+ *
+ * @author EllisRan.
+ * @date 2018/9/25
+ * @version
+ */
+
+const category = {
+    ANHUI: 2,
+    GANSU: 3,
+    GUANGDONG: 4,
+    GUANGXI: 5,
+    JIANGXI: 6,
+    SICHUAN: 7,
+    CHONGQING: 8,
+    NEIMENG: 9,
+    ZHEJIANG: 10,
+    SHANDONG: 11,
+    ZONGBU: 12,
+    YUNNAN: 13,
+    GUIZHOU: 14,
+    BEIJING: 15,
+    FUJIAN: 16,
+    HAINAN: 17,
+    HEBEI: 18,
+    HENAN: 19,
+    HEILONGJIANG: 20,
+    HUBEI: 21,
+    HUNAN: 22,
+    JILIN: 23,
+    JIANGSU: 24,
+    LIAONING: 25,
+    NINGXIA: 26
+};
+
+const categoryList = [
+    {id: category.ANHUI, name: '安徽办'},
+    {id: category.GANSU, name: '甘肃办'},
+    {id: category.GUANGDONG, name: '广东办'},
+    {id: category.GUANGXI, name: '广西办'},
+    {id: category.JIANGXI, name: '江西办'},
+    {id: category.SICHUAN, name: '四川办'},
+    {id: category.CHONGQING, name: '重庆办'},
+    {id: category.NEIMENG, name: '内蒙办'},
+    {id: category.ZHEJIANG, name: '浙江办'},
+    {id: category.SHANDONG, name: '山东办'},
+    {id: category.ZONGBU, name: '总部'},
+    {id: category.YUNNAN, name: '云南办'},
+    {id: category.GUIZHOU, name: '贵州办'},
+    {id: category.BEIJING, name: '北京办'},
+    {id: category.FUJIAN, name: '福建办'},
+    {id: category.HAINAN, name: '海南办'},
+    {id: category.HEBEI, name: '河北办'},
+    {id: category.HENAN, name: '河南办'},
+    {id: category.HEILONGJIANG, name: '黑龙江办'},
+    {id: category.HUBEI, name: '湖北办'},
+    {id: category.HUNAN, name: '湖南办'},
+    {id: category.JILIN, name: '吉林办'},
+    {id: category.JIANGSU, name: '江苏办'},
+    {id: category.LIAONING, name: '辽宁办'},
+    {id: category.NINGXIA, name: '宁夏办'}
+];
+
+export {category as default, categoryList as List};

+ 1 - 0
modules/common/helper/mongoose_helper.js

@@ -72,6 +72,7 @@ class MongooseHelper {
         });
     }
 
+
     /**
      * 关联查找数据
      *

+ 2 - 2
modules/common/std/std_bills_lib_lists_model.js

@@ -38,9 +38,9 @@ class STDBillsLibListsModel extends BaseModel {
         let billList = [];
         for(let tmp of billLib) {
             let tmpRation = {id: tmp.billsLibId, name: tmp.billsLibName};
-            if (compilationId !== tmp.compilationId) {
+            /*if (compilationId !== tmp.compilationId) {
                 continue;
-            }
+            }*/
             if (billList.length <= 0) {
                 billList = [tmpRation];
             } else {

+ 2 - 2
modules/common/std/std_calc_program_model.js

@@ -6,7 +6,7 @@
  * @version
  */
 import BaseModel from "../base/base_model";
-import STDCalcProgramSchema from "./schemas/std_calc_program";
+import mongoose from "mongoose";
 
 class STDCalcProgramModel extends BaseModel {
 
@@ -17,7 +17,7 @@ class STDCalcProgramModel extends BaseModel {
      */
     constructor() {
         let parent = super();
-        parent.model = STDCalcProgramSchema;
+        parent.model = mongoose.model("std_calc_programs");
         parent.init();
     }
 

+ 2 - 2
modules/common/std/std_fee_rate_libs_model.js

@@ -6,7 +6,7 @@
  * @version
  */
 import BaseModel from "../base/base_model";
-import STDFeeRateLibsSchema from "./schemas/std_fee_rate_libs";
+import mongoose from "mongoose";
 
 class STDFeeRateLibsModel extends BaseModel {
 
@@ -17,7 +17,7 @@ class STDFeeRateLibsModel extends BaseModel {
      */
     constructor() {
         let parent = super();
-        parent.model = STDFeeRateLibsSchema;
+        parent.model = mongoose.model("std_fee_rate_libs");
         parent.init();
     }
 

+ 100 - 0
modules/fee_rate_lib/controllers/fee_rate_controller.js

@@ -0,0 +1,100 @@
+/**
+ * Created by zhang on 2018/9/10.
+ */
+import BaseController from "../../common/base/base_controller";
+let config = require("../../../config/config.js");
+import feeRateFacade from "../facade/fee_rate_facade";
+
+class FeeRateController extends BaseController{
+    async main(request, response) {
+        let feeRateLibs = await feeRateFacade.findByCondition({},{rates:0},false);
+        let randerData = {
+            title:'费率标准库',
+            userAccount: request.session.managerData.username,
+            userID: request.session.managerData.userID,
+            feeRateLibs:feeRateLibs,
+            layout: 'maintain/common/html/layout'
+        };
+        response.render("maintain/fee_rate_lib/html/main", randerData);
+    }
+    async addLib(request, response){
+        try {
+            await feeRateFacade.addLib(request.body);
+        }catch (error) {
+            console.log(error);
+        }
+        response.redirect(request.headers.referer);
+    }
+    async findLib(request, response){
+        let result={
+            error:0
+        };
+        try {
+            let data = request.body.data;
+            data = JSON.parse(data);
+            let conditions={ID:data.ID};
+            let resultData = await feeRateFacade.findByCondition(conditions);
+            result.data=resultData;
+        }catch (err){
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        response.json(result);
+    }
+    async saveLib(request, response){
+        let result={
+            error:0
+        };
+        try {
+            let data = request.body.data;
+            data = JSON.parse(data);
+            let resultData= await feeRateFacade.saveLib(data);
+            result.data=resultData;
+        }catch (err){
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        response.json(result);
+    }
+    async deleteLibByID(request,response){
+        let result={
+            error:0
+        };
+        try {
+            let data = request.body.data;
+            data = JSON.parse(data);
+            let resultData= await feeRateFacade.deleteLibByID(data.ID);
+            result.data=resultData;
+        }catch (err){
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        response.json(result);
+    }
+    async edit(request,response){
+        //先取出替换库信息:
+        let libID = request.params.libID;
+        let feeRateLib = await feeRateFacade.findByCondition({'ID':libID});
+        if(feeRateLib){
+            let randerData = {
+                title:'费率标准库',
+                mainURL:'/feeRate/main',
+                libName:feeRateLib.libName,
+                userAccount: request.session.managerData.username,
+                userID: request.session.managerData.userID,
+                rateList:JSON.stringify(feeRateLib.rates),
+                libID:libID,
+                LicenseKey:config.getLicenseKey(process.env.NODE_ENV),
+                layout: 'maintain/common/html/edit_layout'
+            };
+            response.render("maintain/fee_rate_lib/html/edit", randerData);
+        }else {
+            response.redirect(request.headers.referer);
+        }
+    }
+}
+
+export default FeeRateController;

+ 50 - 0
modules/fee_rate_lib/facade/fee_rate_facade.js

@@ -0,0 +1,50 @@
+/**
+ * Created by zhang on 2018/9/10.
+ */
+
+import mongoose from "mongoose";
+
+const uuidV1 = require('uuid/v1');
+let moment = require("moment");
+let feeRateModel = mongoose.model('std_fee_rate_libs');
+let compilationModel = mongoose.model("compilation");
+const _ = require('lodash');
+
+let feeRateLib = {
+    findByCondition:async function(conditions,options,single=true){
+        if(single == true){
+            return await feeRateModel.findOne(conditions,options);
+        }else {
+            return await  feeRateModel.find(conditions,options);
+        }
+    },
+    addLib : async function (data){
+        let now = new Date().getTime();
+        let dateStr = moment(now).format('YYYY-MM-DD HH:mm:ss');
+        //取编办信息
+        let compilation = await compilationModel.findOne({_id:data.compilationId});
+        if(compilation){
+            let newLib = {
+                creator: data.userAccount,
+                createDate: now,
+                recentOpr: [{operator: data.userAccount, operateDate: dateStr}],
+                libName: data.name,
+                compilationId: data.compilationId,
+                compilationName: compilation.name,
+            };
+            newLib.ID = uuidV1();
+            return await feeRateModel.create(newLib);
+        }else {
+            throw  new Error("没有找到该编办!");
+        }
+
+    },
+    saveLib:async function(param) {
+        return await feeRateModel.findOneAndUpdate(param.query,param.data,{new:true});
+    },
+    deleteLibByID:async function(ID){
+        return await feeRateModel.deleteOne({ID:ID});
+    },
+};
+
+export default feeRateLib

+ 21 - 0
modules/fee_rate_lib/routes/fee_rate_routes.js

@@ -0,0 +1,21 @@
+/**
+ * Created by zhang on 2018/9/10.
+ */
+
+let express = require("express");
+let feeRateRouter =express.Router();
+import FeeRateController from "../controllers/fee_rate_controller";
+let feeRateController = new FeeRateController();
+
+module.exports =function (app) {
+
+    feeRateRouter.get("/main", feeRateController.auth, feeRateController.init, feeRateController.main);
+    feeRateRouter.post("/addLib", feeRateController.auth, feeRateController.init, feeRateController.addLib);
+    feeRateRouter.post("/findLib", feeRateController.auth, feeRateController.init, feeRateController.findLib);
+    feeRateRouter.post("/saveLib", feeRateController.auth, feeRateController.init, feeRateController.saveLib);
+    feeRateRouter.post("/deleteLibByID", feeRateController.auth, feeRateController.init, feeRateController.deleteLibByID);
+    feeRateRouter.get("/edit/:libID", feeRateController.auth, feeRateController.init, feeRateController.edit);
+
+
+    app.use("/feeRate", feeRateRouter);
+};

+ 28 - 8
modules/main_col_lib/controllers/main_col_controller.js

@@ -8,17 +8,10 @@
  */
 import BaseController from "../../common/base/base_controller";
 import mainColFacade from "../facade/main_col_facade";
-
+let config = require("../../../config/config.js");
 
 class MainColController extends BaseController {
 
-    /**
-     * 修改计价规则页面
-     *
-     * @param {object} request
-     * @param {object} response
-     * @return {void}
-     */
     async main(request, response) {
         let mainColLibs = await mainColFacade.getAllLibs();
         let randerData = {
@@ -26,6 +19,7 @@ class MainColController extends BaseController {
             userAccount: request.session.managerData.username,
             userID: request.session.managerData.userID,
             mainColLibs:mainColLibs,
+            LicenseKey:config.getLicenseKey(process.env.NODE_ENV),
             layout: 'maintain/common/html/layout'
         };
 
@@ -92,6 +86,32 @@ class MainColController extends BaseController {
         response.json(result);
     }
 
+    async edit(request,response){
+        let result = {
+            error:0
+        }
+        //先取出替换库信息:
+        let libID = request.params.libID;
+        let colLib = await mainColFacade.getLibByID(libID);
+        if(colLib){
+            let randerData = {
+                title:'列设置库',
+                mainURL:'/mainTreeCol/main',
+                libName:colLib.name,
+                userAccount: request.session.managerData.username,
+                userID: request.session.managerData.userID,
+                main_tree_col:JSON.stringify(colLib.main_tree_col),
+                libID:libID,
+                LicenseKey:config.getLicenseKey(process.env.NODE_ENV),
+                layout: 'maintain/common/html/edit_layout'
+            };
+            response.render("maintain/main_col_lib/html/edit", randerData);
+        }else {
+            response.redirect(request.headers.referer);
+        }
+
+    }
+
     /*async updateBillsTemplateItem(request, response) {
         let libID = request.params.libID;
         let result = {error: 1, message: '更新数据错误', data: null};

+ 1 - 0
modules/main_col_lib/routes/main_col_routes.js

@@ -15,6 +15,7 @@ module.exports =function (app) {
     colRouter.post("/deleteLibByID", mainColController.auth, mainColController.init, mainColController.deleteLibByID);
     colRouter.post("/getLibByID", mainColController.auth, mainColController.init, mainColController.getLibByID);
     colRouter.post("/saveLib", mainColController.auth, mainColController.init, mainColController.saveLib);
+    colRouter.get("/edit/:libID", mainColController.auth, mainColController.init, mainColController.edit);
     /*colRouter.get("/editTemplate/:libID", mainColController.auth, mainColController.init, mainColController.editTemplate);
     colRouter.post("/getLibByID", mainColController.auth, mainColController.init, mainColController.getLibByID);
     colRouter.post("/saveLib", mainColController.auth, mainColController.init, mainColController.saveLib);

+ 166 - 0
modules/material_replace_lib/controllers/material_replace_controller.js

@@ -0,0 +1,166 @@
+/**
+ * Created by zhang on 2018/8/22.
+ */
+import BaseController from "../../common/base/base_controller";
+import materialFacade from "../facade/material_replace_facade";
+let config = require("../../../config/config.js");
+
+class ReplaceController extends BaseController{
+    /**
+     * 材料替换库页面
+     *
+     * @param {object} request
+     * @param {object} response
+     * @return {void}
+     */
+    async main(request, response) {
+        let materialLibs = await materialFacade.findByCondition({},null,false);
+        let randerData = {
+            title:'材料替换库',
+            userAccount: request.session.managerData.username,
+            userID: request.session.managerData.userID,
+            materialLibs:materialLibs,
+            layout: 'maintain/common/html/layout'
+        };
+        response.render("maintain/material_replace_lib/html/main", randerData);
+    }
+    async edit(request,response){
+        //先取出替换库信息:
+        let libID = request.params.libID;
+        let materialLib = await materialFacade.findByCondition({'ID':libID});
+        if(materialLib){
+            let billsLibId = materialLib.billsLibId;
+            let compilationId = materialLib.compilationId;
+            let gljLib = await  materialFacade.findGLJLibByComID(compilationId);
+            let billsList = await materialFacade.findBillsByLibID(libID);
+            //let templateDatas = await materialFacade.getTemplateDatasByLibID(libID);
+
+            let randerData = {
+                title:'材料替换库',
+                mainURL:'/materialReplace/main',
+                libName:materialLib.name,
+                userAccount: request.session.managerData.username,
+                userID: request.session.managerData.userID,
+                billsList:JSON.stringify(billsList),
+                billsLibId:billsLibId,
+                gljLibID:gljLib.ID,
+                libID:libID,
+                LicenseKey:config.getLicenseKey(process.env.NODE_ENV),
+                layout: 'maintain/common/html/edit_layout'
+            };
+            response.render("maintain/material_replace_lib/html/edit", randerData);
+        }else {
+            response.redirect(request.headers.referer);
+        }
+    }
+    async findLib(request, response){
+        let result={
+            error:0
+        };
+        try {
+            let data = request.body.data;
+            data = JSON.parse(data);
+            let conditions={};
+            if(data.compilationID) conditions.compilationId = data.compilationID;
+            if(data.billLibID) conditions.billsLibId = data.billLibID;
+            if(data.ID) conditions.ID = data.ID;
+            let resultData = await materialFacade.findByCondition(conditions);
+            result.data=resultData;
+        }catch (err){
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        response.json(result);
+    }
+    async addLib(request, response){
+        try {
+            await materialFacade.addLib(request.body);
+        }catch (error) {
+            console.log(error);
+        }
+        response.redirect(request.headers.referer);
+    }
+    async saveLib(request, response){
+        let result={
+            error:0
+        };
+        try {
+            let data = request.body.data;
+            data = JSON.parse(data);
+            let resultData= await materialFacade.saveLib(data);
+            result.data=resultData;
+        }catch (err){
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        response.json(result);
+    }
+    async deleteLibByID(request,response){
+        let result={
+            error:0
+        };
+        try {
+            let data = request.body.data;
+            data = JSON.parse(data);
+            let resultData= await materialFacade.deleteLibByID(data.ID);
+            result.data=resultData;
+        }catch (err){
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        response.json(result);
+    }
+    async saveBills(request,response){
+        let result={
+            error:0
+        };
+        try {
+            let data = request.body.data;
+            data = JSON.parse(data);
+            let resultData= await materialFacade.saveBills(data);
+            result.data=resultData;
+        }catch (err){
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        response.json(result);
+    }
+    async saveMaterial(request,response){
+        let result={
+            error:0
+        };
+        try {
+            let data = request.body.data;
+            data = JSON.parse(data);
+            let resultData= await materialFacade.saveMaterial(data);
+            result.data=resultData;
+        }catch (err){
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        response.json(result);
+    }
+    async findMaterial (request,response){
+        let result={
+            error:0
+        };
+        try {
+            let data = request.body.data;
+            data = JSON.parse(data);
+            let resultData = await materialFacade.getMaterialByBillsID(data.billsItemID);
+            result.data=resultData;
+        }catch (err){
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        response.json(result);
+    }
+}
+
+export default ReplaceController;

+ 200 - 0
modules/material_replace_lib/facade/material_replace_facade.js

@@ -0,0 +1,200 @@
+/**
+ * Created by zhang on 2018/8/22.
+ */
+
+import mongoose from "mongoose";
+const uuidV1 = require('uuid/v1');
+let moment = require("moment");
+let compilationModel = mongoose.model("compilation");
+let materialLibModel = mongoose.model("std_material_replace_lib");
+let replaceBillModel = mongoose.model("std_replace_bills");
+let replaceMaterialModel = mongoose.model("std_replace_material");
+let StdBillsLib = mongoose.model('std_bills_lib_list');
+let stdBillsModel = mongoose.model('std_bills_lib_bills');
+let stdGLJMode = mongoose.model('std_glj_lib_gljList');
+
+const stdGljLibModel = mongoose.model('std_glj_lib_map');
+
+let materialReplaceLib = {
+    findByCondition:async function(conditions,options,single=true){
+        if(single == true){
+            return await materialLibModel.findOne(conditions,options);
+        }else {
+            return await  materialLibModel.find(conditions,options);
+        }
+    },
+    addLib : async function (data){
+        let now = new Date().getTime();
+        let dateStr = moment(now).format('YYYY-MM-DD HH:mm:ss');
+        //取编办信息
+        let compilation = await compilationModel.findOne({_id:data.compilationId});
+        //取清单规则信息
+        let billLib = await StdBillsLib.findOne({billsLibId:data.billsLibId});
+        if(compilation && billLib){
+            let newLib = {
+                creator: data.userAccount,
+                createDate: now,
+                recentOpr: [{operator: data.userAccount, operateDate: dateStr}],
+                name: data.name,
+                compilationId: data.compilationId,
+                compilationName: compilation.name,
+                billsLibId:billLib.billsLibId,
+                billsLibName:billLib.billsLibName,
+                deleted: false
+            };
+            newLib.ID = uuidV1();
+            return await materialLibModel.create(newLib);
+        }else {
+            throw  new Error("编办或清单规则有误!");
+        }
+    },
+    saveLib:async function(param) {
+        return await materialLibModel.findOneAndUpdate(param.query,param.data,{new:true});
+    },
+    deleteLibByID:async function(ID){
+        //删除材料数据
+        await replaceMaterialModel.deleteMany({libID:ID});
+        //删除清单数据
+        await replaceBillModel.deleteMany({libID:ID});
+        return await materialLibModel.deleteOne({ID:ID});
+    },
+    findGLJLibByComID:async function(compilationId){//跟据费用定额查找工料机库
+        return await stdGljLibModel.findOne({compilationId:compilationId},['ID']);
+    },
+    findBillsByLibID:async function(libID){
+        return await replaceBillModel.find({libID:libID});
+    },
+    saveBills:async function(data){
+        let [addList,updateList,deleteList]  = prepareDatas(data);
+        let p = await Promise.all([addBills(addList),updateBills(updateList),deleteBills(deleteList)]);
+        return p;
+    },
+    saveMaterial:async function(data){
+        let [addList,updateList,deleteList]  = prepareDatas(data);
+        let p = await Promise.all([addMaterial(addList),replaceMaterial(updateList),deleteMaterial(deleteList)]);
+        return p;
+    },
+    getMaterialByBillsID : async function(billsID){
+       return  await replaceMaterialModel.find({billsItemID:billsID});
+    }
+};
+function prepareDatas(data) {//整理数据
+    let addList = [],updateList =[],deleteList=[];
+    for(let d of data){
+        if(d.type == 'add') addList.push(d);
+        if(d.type == 'update') updateList.push(d);
+        if(d.type == 'delete') deleteList.push(d);
+    }
+    return [addList,updateList,deleteList]
+}
+async function addMaterial(datas) {
+    let newMaterial = [],missCodes=[];
+    for(let d of datas){
+        let stdGLJ = await stdGLJMode.findOne({repositoryId:d.gljLibID,code:d.code});
+        if(stdGLJ){
+            let temM = {
+                libID:d.libID,
+                billsItemID:d.billsItemID,
+                code:d.code,
+                name:stdGLJ.name,
+                specs:stdGLJ.specs,
+                type:stdGLJ.gljType,
+                unit:stdGLJ.unit
+            };
+            temM.ID = uuidV1();
+            newMaterial.push(temM);
+        }else {
+            missCodes.push(d.code);
+        }
+    }
+    if(newMaterial.length>0){
+        await replaceMaterialModel.create(newMaterial);
+    }
+    return {type:'add',list:newMaterial,missCodes:missCodes};
+}
+async function replaceMaterial(datas) {
+    let tasks = [],list=[],missCodes = [];
+    for(let d of datas){
+        let stdGLJ = await stdGLJMode.findOne({repositoryId:d.gljLibID,code:d.code});
+        if(stdGLJ){
+            let updateData =  {code:d.code, name:stdGLJ.name, specs:stdGLJ.specs, type:stdGLJ.gljType, unit:stdGLJ.unit};
+            let task = {
+                updateOne:{filter:{ID:d.ID}, update :updateData}
+            };
+            tasks.push(task);
+            list.push({ID:d.ID,updateData:updateData})
+        }else {
+            missCodes.push(d.code)
+        }
+    }
+    if(tasks.length > 0) await replaceMaterialModel.bulkWrite(tasks);
+    return {type:'update',list:list,missCodes:missCodes};
+}
+async function deleteMaterial(datas) {
+    let IDList = [];
+    for(let d of datas){
+        if(d.ID) IDList.push(d.ID);
+    }
+    await replaceMaterialModel.deleteMany({ID:{"$in": IDList}});
+    return {type:'delete',list:IDList};
+}
+async function addBills(datas) {
+    let newBills = [],missCodes=[];
+    for(let d of datas){
+        //先查找清单规则库找到对应的清单
+        let stdBill = await stdBillsModel.findOne({billsLibId:d.billsLibId,code:d.code});
+        if(stdBill){
+            let temBill = {
+                libID:d.libID,
+                code:d.code,
+                name:stdBill.name,
+            };
+            temBill.ID = uuidV1();
+            newBills.push(temBill);
+        }else {
+            missCodes.push(d.code);
+        }
+    }
+    if(newBills.length>0){
+        await replaceBillModel.create(newBills);
+    }
+    return {type:'add',list:newBills,missCodes:missCodes};
+}
+async function updateBills(datas) {
+    let tasks = [],list=[],missCodes = [];
+    for(let d of datas){
+        let updateData = null;
+        let filter = {libID:d.libID, code:d.oldCode};
+        if(d.newCode && d.newCode !=''){//说明是要替换清单
+            let stdBill = await stdBillsModel.findOne({billsLibId:d.billsLibId,code:d.newCode});
+            if(stdBill){
+                 updateData = {code:d.newCode, name:stdBill.name};
+            }else {
+                missCodes.push(d.newCode);
+            }
+        }else {
+            updateData = d.updateData;
+        }
+        if(updateData){
+            let task = {
+                updateOne:{filter:filter, update :updateData}
+            };
+            tasks.push(task);
+            list.push({code:d.oldCode,updateData:updateData})
+        }
+    }
+    if(tasks.length > 0) await replaceBillModel.bulkWrite(tasks);
+    return {type:'update',list:list,missCodes:missCodes};
+}
+async function deleteBills(datas) {
+    let IDList = [];
+    for(let d of datas){
+        if(d.ID) IDList.push(d.ID)
+    }
+    await replaceMaterialModel.deleteMany({billsItemID:{"$in": IDList}});//删除清单时要把对应的材料也删除了
+    await replaceBillModel.deleteMany({ID:{"$in": IDList}});
+    return {type:'delete',list:IDList};
+}
+
+
+export default materialReplaceLib

+ 24 - 0
modules/material_replace_lib/routes/material_replace_router.js

@@ -0,0 +1,24 @@
+/**
+ * Created by zhang on 2018/8/22.
+ */
+
+let express = require("express");
+let repRouter =express.Router();
+import ReplaceController from "../controllers/material_replace_controller";
+let replaceController = new ReplaceController();
+
+module.exports =function (app){
+
+    repRouter.get("/main", replaceController.auth, replaceController.init, replaceController.main);
+    repRouter.get("/edit/:libID", replaceController.auth, replaceController.init, replaceController.edit);
+    repRouter.post("/findLib", replaceController.auth, replaceController.init, replaceController.findLib);
+    repRouter.post("/addLib", replaceController.auth, replaceController.init, replaceController.addLib);
+    repRouter.post("/saveLib", replaceController.auth, replaceController.init, replaceController.saveLib);
+    repRouter.post("/deleteLibByID", replaceController.auth, replaceController.init, replaceController.deleteLibByID);
+    repRouter.post("/saveBills", replaceController.auth, replaceController.init, replaceController.saveBills);
+    repRouter.post("/saveMaterial", replaceController.auth, replaceController.init, replaceController.saveMaterial);
+    repRouter.post("/findMaterial", replaceController.auth, replaceController.init, replaceController.findMaterial);
+    app.use("/materialReplace", repRouter);
+};
+
+

+ 99 - 0
modules/project_feature_lib/controllers/project_feature_controller.js

@@ -0,0 +1,99 @@
+/**
+ * Created by zhang on 2018/9/3.
+ */
+import BaseController from "../../common/base/base_controller";
+import featureFacade from "../facade/project_feature_facade";
+let config = require("../../../config/config.js");
+
+class FeatureController extends BaseController{
+    async main(request, response) {
+        let featureLibs = await featureFacade.findByCondition({},{feature:0},false);
+        let randerData = {
+            title:'工程特征库',
+            userAccount: request.session.managerData.username,
+            userID: request.session.managerData.userID,
+            featureLibs:featureLibs,
+            layout: 'maintain/common/html/layout'
+        };
+        response.render("maintain/project_feature_lib/html/main", randerData);
+    }
+    async addLib(request, response){
+        try {
+            await featureFacade.addLib(request.body);
+        }catch (error) {
+            console.log(error);
+        }
+        response.redirect(request.headers.referer);
+    }
+    async findLib(request, response){
+        let result={
+            error:0
+        };
+        try {
+            let data = request.body.data;
+            data = JSON.parse(data);
+            let conditions={'ID' : data.ID};
+            let resultData = await featureFacade.findByCondition(conditions);
+            result.data=resultData;
+        }catch (err){
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        response.json(result);
+    }
+    async saveLib(request, response){
+        let result={
+            error:0
+        };
+        try {
+            let data = request.body.data;
+            data = JSON.parse(data);
+            let resultData= await featureFacade.saveLib(data);
+            result.data=resultData;
+        }catch (err){
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        response.json(result);
+    }
+    async deleteLibByID(request,response){
+        let result={
+            error:0
+        };
+        try {
+            let data = request.body.data;
+            data = JSON.parse(data);
+            let resultData= await featureFacade.deleteLibByID(data.ID);
+            result.data=resultData;
+        }catch (err){
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        response.json(result);
+    }
+    async edit(request,response){
+        //先取出替换库信息:
+        let libID = request.params.libID;
+        let featureLib = await featureFacade.findByCondition({'ID':libID});
+        if(featureLib){
+            let randerData = {
+                title:'工程特征库',
+                mainURL:'/projectFeature/main',
+                libName:featureLib.name,
+                userAccount: request.session.managerData.username,
+                userID: request.session.managerData.userID,
+                featureList:JSON.stringify(featureLib.feature),
+                libID:libID,
+                LicenseKey:config.getLicenseKey(process.env.NODE_ENV),
+                layout: 'maintain/common/html/edit_layout'
+            };
+            response.render("maintain/project_feature_lib/html/edit", randerData);
+        }else {
+            response.redirect(request.headers.referer);
+        }
+    }
+}
+export default FeatureController;

+ 38 - 0
modules/project_feature_lib/facade/project_feature_facade.js

@@ -0,0 +1,38 @@
+/**
+ * Created by zhang on 2018/9/3.
+ */
+import mongoose from "mongoose";
+const uuidV1 = require('uuid/v1');
+let moment = require("moment");
+let projectFeatureModel = mongoose.model('std_project_feature_lib');
+
+
+let projectFeatureLib = {
+    findByCondition:async function(conditions,options,single=true){
+        if(single == true){
+            return await projectFeatureModel.findOne(conditions,options);
+        }else {
+            return await  projectFeatureModel.find(conditions,options);
+        }
+    },
+    addLib : async function (data){
+        let now = new Date().getTime();
+        let dateStr = moment(now).format('YYYY-MM-DD HH:mm:ss');
+        let newLib = {
+            creator: data.userAccount,
+            createDate: now,
+            recentOpr: [{operator: data.userAccount, operateDate: dateStr}],
+            name: data.name,
+        };
+        newLib.ID = uuidV1();
+        return await projectFeatureModel.create(newLib);
+    },
+    saveLib:async function(param) {
+        return await projectFeatureModel.findOneAndUpdate(param.query,param.data,{new:true});
+    },
+    deleteLibByID:async function(ID){
+        return await projectFeatureModel.deleteOne({ID:ID});
+    },
+};
+
+export default projectFeatureLib

+ 21 - 0
modules/project_feature_lib/routes/project_feature_router.js

@@ -0,0 +1,21 @@
+/**
+ * Created by zhang on 2018/9/3.
+ */
+
+let express = require("express");
+let featureRouter =express.Router();
+import FeatureController from "../controllers/project_feature_controller";
+let featureController = new FeatureController();
+
+module.exports =function (app){
+
+    featureRouter.get("/main", featureController.auth, featureController.init, featureController.main);
+    featureRouter.post("/addLib", featureController.auth, featureController.init, featureController.addLib);
+    featureRouter.post("/findLib", featureController.auth, featureController.init, featureController.findLib);
+    featureRouter.post("/saveLib", featureController.auth, featureController.init, featureController.saveLib);
+    featureRouter.post("/deleteLibByID", featureController.auth, featureController.init, featureController.deleteLibByID);
+    featureRouter.get("/edit/:libID", featureController.auth, featureController.init, featureController.edit);
+    app.use("/projectFeature", featureRouter);
+};
+
+

+ 2 - 1
modules/ration_repository/controllers/ration_controller.js

@@ -12,10 +12,11 @@ class RationController extends BaseController{
     async getRationItemsByLib(req, res){
         try{
             let data = JSON.parse(req.body.data);
-            let rationItems = await rationItem.getRationItemsByLib(data.rationLibId);
+            let rationItems = await rationItem.getRationItemsByLib(data.rationLibId, data.showHint, data.returnFields);
             callback(req, res, 0, '', rationItems);
         }
         catch(err){
+            console.log(err);
             callback(req, res, 1, err, null);
         }
     }

+ 65 - 19
modules/ration_repository/controllers/ration_repository_controller.js

@@ -16,6 +16,9 @@ const fs = require("fs");
 // excel解析
 const excel = require("node-xlsx");
 const rationItem = require("../models/ration_item");
+const rationLibModel = mongoose.model('std_ration_lib_map');
+const rationItemModel = mongoose.model('std_ration_lib_ration_items');
+import STDGLJListModel from '../../std_glj_lib/models/gljModel';
 
 class RationRepositoryController extends baseController {
     async getRationLibsByCompilation(req, res){
@@ -159,12 +162,13 @@ class RationRepositoryController extends baseController {
             msg: ''
         };
         const allowHeader = ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];
-        try {
-            const uploadOption = {
-                uploadDir: './public'
-            };
-            const form = new multiparty.Form(uploadOption);
-            form.parse(request, async function(err, fields, files) {
+        const uploadOption = {
+            uploadDir: './public'
+        };
+        const form = new multiparty.Form(uploadOption);
+        let uploadFullName
+        form.parse(request, async function(err, fields, files) {
+            try{
                 const rationRepId = fields.rationRepId !== undefined && fields.rationRepId.length > 0 ?
                     fields.rationRepId[0] : 0;
                 const type = fields.type !== undefined && fields.type.length > 0 ?
@@ -181,7 +185,7 @@ class RationRepositoryController extends baseController {
                     throw '不支持该类型';
                 }
                 // 重命名文件名
-                const uploadFullName = uploadOption.uploadDir + '/' + file.originalFilename;
+                uploadFullName = uploadOption.uploadDir + '/' + file.originalFilename;
                 fs.renameSync(file.path, uploadFullName);
 
                 const sheet = excel.parse(uploadFullName);
@@ -191,27 +195,30 @@ class RationRepositoryController extends baseController {
                 const result = type === 'source_file' ?
                     await rationItem.batchAddFromExcel(rationRepId, sheet[0].data) :
                     await rationItem.batchUpdateSectionIdFromExcel(sheet[0].data);
-
-                if (rationItem.failGLJList.length > 0) {
+                if (rationItem.failGLJList && rationItem.failGLJList.length > 0) {
                     responseData.msg = rationItem.failGLJList.join("\r\n");
                 }
                 // 删除文件
-                if (result) {
+                if(uploadFullName && fs.existsSync(uploadFullName)){
                     fs.unlink(uploadFullName);
                 }
                 response.json(responseData);
-            });
-        } catch (error) {
-            responseData.err = 1;
-            responseData.msg = error;
-            response.json(responseData);
-        }
-
+            }
+            catch (error){
+                console.log(error);
+                if(uploadFullName && fs.existsSync(uploadFullName)){
+                    fs.unlink(uploadFullName);
+                }
+                responseData.err = 1;
+                responseData.msg = error;
+                response.json(responseData);
+            }
+        });
         return;
     }
 
     /**
-     * 导出内数据
+     * 导出内数据
      *
      * @param {Object} request
      * @param {Object} response
@@ -240,7 +247,46 @@ class RationRepositoryController extends baseController {
         } catch (error) {
             response.end(error);
         }
-
+    }
+    //一键重新计算所有定额数据
+    async reCalcAll(req, res){
+        try{
+            let data = JSON.parse(req.body.data);
+            let rationRepId = data.rationRepId;
+            let rationLib = await rationLibModel.findOne({ID: rationRepId, $or: [{deleted: null},{deleted: false}]});
+            if(!rationLib){
+                throw '不存在此定额库';
+            }
+            let task = [];
+            // 获取标准工料机库数据
+            const stdBillsLibListsModel = new STDGLJListModel();
+            const stdGLJData = await stdBillsLibListsModel.getGljItemsByRepId(rationLib.gljLib);
+            // 整理标准工料机库数据
+            let stdGLJList = {};
+            let stdGLJListByID = {};
+            for (const tmp of stdGLJData) {
+                stdGLJList[tmp.code.toString()] = tmp.ID;
+                stdGLJListByID[tmp.ID] = tmp;
+            }
+            //获取所有的定额
+            let allRations = await rationItemModel.find({rationRepId: rationRepId, $or: [{isDeleted: false}, {isDeleted: null}]});
+            for(let ration of allRations){
+                rationItem.calcForRation(stdGLJListByID, ration);
+                task.push({
+                    updateOne: {
+                        filter: {ID: ration.ID},
+                        update: {$set: {labourPrice: ration.labourPrice, materialPrice: ration.materialPrice, machinePrice: ration.machinePrice, basePrice: ration.basePrice}}
+                    }
+                });
+            }
+            if(task.length > 0){
+                await rationItemModel.bulkWrite(task);
+            }
+            res.json({error: 0, message: 'success', data: null});
+        }
+        catch (err){
+            res.json({error: 1, message: err, data: null});
+        }
     }
 
 }

+ 35 - 7
modules/ration_repository/controllers/repository_views_controller.js

@@ -2,6 +2,11 @@
  * Created by Zhong on 2017/8/3.
  */
 import BaseController from "../../common/base/base_controller";
+let config = require("../../../config/config.js");
+import mongoose from 'mongoose';
+const compilationModel = mongoose.model('compilation');
+const rationLibModel = mongoose.model('std_ration_lib_map');
+const fs = require('fs');
 class ViewsController extends BaseController{
     redirectMain(req, res){
         res.render('maintain/ration_repository/main.html',
@@ -10,33 +15,54 @@ class ViewsController extends BaseController{
                 userID: req.session.managerData.userID
             });
     }
-    //rationRepository/lmm rationRepository/coeList rationRepository/installation
-    redirectRation(req, res){
+    async redirectRation(req, res){
         const repId = req.query.repository;
         const redirectGlj = `/rationRepository/lmm?repository=${repId}`;
         const redirectCoe = `/rationRepository/coeList?repository=${repId}`;
         const redirectInstallation = `/rationRepository/installation?repository=${repId}`;
+        let overWriteUrl = null;
+        let priceProperties = [];
+        let stdRationLib = await rationLibModel.findOne({ID: repId});
+        if(stdRationLib){
+            let compilation = await compilationModel.findOne({_id: mongoose.Types.ObjectId(stdRationLib.compilationId)});
+            priceProperties = compilation.priceProperties ? compilation.priceProperties : [];
+            let absoluteUrl = compilation.overWriteUrl ? req.app.locals.rootDir + compilation.overWriteUrl : req.app.locals.rootDir;
+            overWriteUrl = fs.existsSync(absoluteUrl) && fs.statSync(absoluteUrl).isFile()? compilation.overWriteUrl : null;
+        }
         res.render('maintain/ration_repository/dinge.html',
             {
                 userAccount: req.session.managerData.username,
                 userID: req.session.managerData.userID,
                 redirectGlj: redirectGlj,
                 redirectCoe: redirectCoe,
-                redirectInstallation: redirectInstallation
+                redirectInstallation: redirectInstallation,
+                LicenseKey:config.getLicenseKey(process.env.NODE_ENV),
+                priceProperties: JSON.stringify(priceProperties)
             });
     }
-    redirectGlj(req, res){
+    async redirectGlj(req, res){
         const repId = req.query.repository;
         const redirectRation = `/rationRepository/ration?repository=${repId}`;
         const redirectCoe = `/rationRepository/coeList?repository=${repId}`;
         const redirectInstallation = `/rationRepository/installation?repository=${repId}`;
+        let overWriteUrl = null;
+        let priceProperties = [];
+        let stdRationLib = await rationLibModel.findOne({ID: repId});
+        if(stdRationLib){
+            let compilation = await compilationModel.findOne({_id: mongoose.Types.ObjectId(stdRationLib.compilationId)});
+            priceProperties = compilation.priceProperties ? compilation.priceProperties : [];
+            let absoluteUrl = compilation.overWriteUrl ? req.app.locals.rootDir + compilation.overWriteUrl : req.app.locals.rootDir;
+            overWriteUrl = fs.existsSync(absoluteUrl) && fs.statSync(absoluteUrl).isFile()? compilation.overWriteUrl : null;
+        }
         res.render('maintain/ration_repository/gongliao.html',
             {
                 userAccount: req.session.managerData.username,
                 userID: req.session.managerData.userID,
                 redirectRation: redirectRation,
                 redirectCoe: redirectCoe,
-                redirectInstallation: redirectInstallation
+                redirectInstallation: redirectInstallation,
+                LicenseKey:config.getLicenseKey(process.env.NODE_ENV),
+                priceProperties: JSON.stringify(priceProperties)
             });
     }
     redirectCoeList(req, res){
@@ -50,7 +76,8 @@ class ViewsController extends BaseController{
                 userID: req.session.managerData.userID,
                 redirectGlj: redirectGlj,
                 redirectRation: redirectRation,
-                redirectInstallation: redirectInstallation
+                redirectInstallation: redirectInstallation,
+                LicenseKey:config.getLicenseKey(process.env.NODE_ENV)
             });
     }
     redirectInstallation(req, res){
@@ -64,7 +91,8 @@ class ViewsController extends BaseController{
                 userID: req.session.managerData.userID,
                 redirectGlj: redirectGlj,
                 redirectCoe: redirectCoe,
-                redirectRation: redirectRation
+                redirectRation: redirectRation,
+                LicenseKey:config.getLicenseKey(process.env.NODE_ENV)
             });
     }
 }

+ 270 - 24
modules/ration_repository/models/ration_item.js

@@ -9,13 +9,67 @@ let gljDao = require('./glj_repository');
 let rationRepositoryDao = require('./repository_map');
 const scMathUtil = require('../../../public/scMathUtil').getUtil();
 const rationItemModel = mongoose.model('std_ration_lib_ration_items');
+const stdRationLibModel = mongoose.model('std_ration_lib_map');
 const compleRationModel = mongoose.model('complementary_ration_items');
 import STDGLJListModel from '../../std_glj_lib/models/gljModel';
 
 var rationItemDAO = function(){};
 
-rationItemDAO.prototype.getRationItemsByLib = async function (rationRepId) {
-    return await rationItemModel.find({rationRepId: rationRepId, $or: [{isDeleted: null}, {isDeleted: false}]});
+rationItemDAO.prototype.getRationItemsByLib = async function (rationRepId, showHint = null, returnFields = '') {
+    let rationLib = await stdRationLibModel.findOne({ID: rationRepId, deleted: false});
+    if(!rationLib){
+        return [];
+    }
+    let startDate = new Date();
+    let rations = await rationItemModel.find({rationRepId: rationRepId}, returnFields);
+    console.log(`Date: ${new Date() - startDate}====================================`);
+    if(!showHint){
+        return rations;
+    }
+    else {
+        const stdBillsLibListsModel = new STDGLJListModel();
+        const stdGLJData = await stdBillsLibListsModel.getGljItemsByRepId(rationLib.gljLib, '-_id ID code name unit gljType');
+        let gljMapping = {};
+        for(let glj of stdGLJData){
+            gljMapping[glj.ID] = glj;
+        }
+        //设置悬浮
+        for(let ration of rations){
+            let hintsArr = [];
+            //对人材机进行排序
+            ration.rationGljList.sort(function (a, b) {
+                let gljA = gljMapping[a.gljId],
+                    gljB = gljMapping[b.gljId];
+                if(gljA && gljB){
+                    let aV = gljA.gljType + gljA.code,
+                        bV = gljB.gljType + gljB.code;
+                    if(aV > bV) {
+                        return 1;
+                    } else if(aV < bV) {
+                        return -1;
+                    }
+                }
+                return 0;
+            });
+            for(let rationGlj of ration.rationGljList){
+                let subGlj = gljMapping[rationGlj.gljId];
+                if(subGlj){
+                    hintsArr.push(` ${subGlj.code} ${subGlj.name}${subGlj.specs ? '&nbsp;&nbsp;&nbsp;' + subGlj.specs : ''}&nbsp;&nbsp&nbsp;${subGlj.unit}&nbsp;&nbsp;&nbsp;${rationGlj.consumeAmt}`);
+                }
+            }
+            hintsArr.push(`基价 元 ${ration.basePrice}`);
+            if(ration.jobContent && ration.jobContent.toString().trim() !== ''){
+                hintsArr.push(`工作内容:`);
+                hintsArr = hintsArr.concat(ration.jobContent.split('\n'));
+            }
+            if(ration.annotation && ration.annotation.toString().trim() !== ''){
+                hintsArr.push(`附注:`);
+                hintsArr = hintsArr.concat(ration.annotation.split('\n'));
+            }
+            ration._doc.hint = hintsArr.join('<br>');
+        }
+        return rations;
+    }
 };
 
 rationItemDAO.prototype.sortToNumber = function (datas) {
@@ -320,11 +374,30 @@ rationItemDAO.prototype.updateRationBasePrc = function (basePrcArr, callback) {
                                     if(theGlj.gljType > 300 && theGlj.gljType < 400){
                                         gljParentType = 3;
                                     }
+                                    //管理费
+                                    if(theGlj.gljType === 6){
+                                        gljParentType = 6;
+                                    }
+                                    //利润
+                                    if(theGlj.gljType === 7){
+                                        gljParentType = 7;
+                                    }
+                                    //风险费
+                                    if(theGlj.gljType === 8){
+                                        gljParentType = 8;
+                                    }
+                                    if(theGlj)
                                     if(theGlj.ID === adjGljId){
                                         gljArr.push({gljId: theGlj.ID, basePrice: adjBasePrice, gljParentType: gljParentType});
                                     }
                                     else {
-                                        gljArr.push({gljId: theGlj.ID, basePrice: parseFloat(theGlj.basePrice), gljParentType: gljParentType});
+                                        if(theGlj.priceProperty && Object.keys(theGlj.priceProperty).length > 0){
+                                            let priceKeys = Object.keys(theGlj.priceProperty);
+                                            gljArr.push({gljId: theGlj.ID, basePrice: parseFloat(theGlj.priceProperty[priceKeys[0]]), gljParentType: gljParentType});
+                                        }
+                                        else {
+                                            gljArr.push({gljId: theGlj.ID, basePrice: parseFloat(theGlj.basePrice), gljParentType: gljParentType});
+                                        }
                                     }
                                 }
                             }
@@ -336,7 +409,14 @@ rationItemDAO.prototype.updateRationBasePrc = function (basePrcArr, callback) {
                                 })
                             });
                             //recalculate the price of ration
-                            let labourPrc = [], materialPrc = [], machinePrc = [], singlePrc, updatePrc = {labourPrice: 0, materialPrice: 0, machinePrice: 0, basePrice: 0};
+                            let labourPrc = [],
+                                materialPrc = [],
+                                machinePrc = [],
+                                managePrc = [],
+                                profitPrc = [],
+                                riskPrc = [],
+                                singlePrc,
+                                updatePrc = {labourPrice: 0, materialPrice: 0, machinePrice: 0, managePrice: 0, profitPrice: 0, riskPrice: 0, basePrice: 0};
                             gljArr.forEach(function (gljItem) {
                                 if(gljItem.gljParentType !== -1){
                                     singlePrc = scMathUtil.roundTo(gljItem.basePrice * gljItem.consumeAmt, -3);
@@ -346,9 +426,18 @@ rationItemDAO.prototype.updateRationBasePrc = function (basePrcArr, callback) {
                                     else if(gljItem.gljParentType ===2){
                                         materialPrc.push(singlePrc);
                                     }
-                                    else{
+                                    else if(gljItem.gljParentType === 3){
                                         machinePrc.push(singlePrc);
                                     }
+                                    else if(gljItem.gljParentType === 6){
+                                        managePrc.push(singlePrc);
+                                    }
+                                    else if(gljItem.gljParentType === 7){
+                                        profitPrc.push(singlePrc);
+                                    }
+                                    else if(gljItem.gljParentType === 8){
+                                        riskPrc.push(singlePrc);
+                                    }
                                 }
                             });
                             if(labourPrc.length > 0){
@@ -372,7 +461,29 @@ rationItemDAO.prototype.updateRationBasePrc = function (basePrcArr, callback) {
                                 }
                                 updatePrc.machinePrice = scMathUtil.roundTo(sumMaP, -2);
                             }
-                            updatePrc.basePrice = scMathUtil.roundTo(updatePrc.labourPrice + updatePrc.materialPrice + updatePrc.machinePrice, -2);
+                            if(managePrc.length > 0){
+                                let sumMgP = 0;
+                                for(let i =0; i< managePrc.length; i++){
+                                    sumMgP = scMathUtil.roundTo(sumMgP + managePrc[i], processDecimal);
+                                }
+                                updatePrc.managePrice = scMathUtil.roundTo(sumMgP, -2);
+                            }
+                            if(profitPrc.length > 0){
+                                let sumPfP = 0;
+                                for(let i =0; i< profitPrc.length; i++){
+                                    sumPfP = scMathUtil.roundTo(sumPfP + profitPrc[i], processDecimal);
+                                }
+                                updatePrc.profitPrice = scMathUtil.roundTo(sumPfP, -2);
+                            }
+                            if(riskPrc.length > 0){
+                                let sumRkP = 0;
+                                for(let i =0; i< riskPrc.length; i++){
+                                    sumRkP = scMathUtil.roundTo(sumRkP + riskPrc[i], processDecimal);
+                                }
+                                updatePrc.riskPrice = scMathUtil.roundTo(sumRkP, -2);
+                            }
+                            updatePrc.basePrice = scMathUtil.roundTo(
+                                updatePrc.labourPrice + updatePrc.materialPrice + updatePrc.machinePrice + updatePrc.managePrice + updatePrc.profitPrice + updatePrc.riskPrice, -2);
                             let task = {
                                 updateOne: {
                                     filter: {
@@ -525,8 +636,14 @@ rationItemDAO.prototype.updateAnnotation = function (lastOpr, repId, updateArr,
 rationItemDAO.prototype.calcForRation = function (stdGljList, ration) {
     const processDecimal = -6;
     //根据工料机类型划分价格
-    const labour = [1],  material = [201, 202, 203, 204, 205, 206], machine = [301, 302, 303];
-    let labourPrc = [], materialPrc = [], machinePrc = [], singlePrc, updatePrc = {labourPrice: 0, materialPrice: 0, machinePrice: 0, basePrice: 0};
+    const labour = [1],
+        material = [201, 202, 203, 204, 205, 206, 207],
+        machine = [301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312],
+        manage = [6],
+        profit = [7],
+        risk = [8];
+    let labourPrc = [], materialPrc = [], machinePrc = [], managePrc = [], profitPrc = [], riskPrc = [],
+        singlePrc, updatePrc = {labourPrice: 0, materialPrice: 0, machinePrice: 0, basePrice: 0, managePrice: 0, profitPrice: 0, riskPrice: 0};
     let rationGljList = ration.rationGljList;
     for(let rationGlj of rationGljList){
         let glj = stdGljList[rationGlj.gljId];
@@ -542,6 +659,15 @@ rationItemDAO.prototype.calcForRation = function (stdGljList, ration) {
             else if(prcType === 'machine'){
                 machinePrc.push(singlePrc);
             }
+            else if(prcType === 'manage'){
+                managePrc.push(singlePrc);
+            }
+            else if(prcType === 'profit'){
+                profitPrc.push(singlePrc);
+            }
+            else if(prcType === 'risk'){
+                riskPrc.push(singlePrc);
+            }
         }
     }
     //计算人工费
@@ -568,8 +694,33 @@ rationItemDAO.prototype.calcForRation = function (stdGljList, ration) {
         }
         updatePrc.machinePrice = scMathUtil.roundTo(sumMaP, -2);
     }
+    //管理费
+    if(managePrc.length > 0){
+        let sumMgP = 0;
+        for(let i = 0, len  = managePrc.length; i < len; i++){
+            sumMgP = scMathUtil.roundTo(sumMgP + managePrc[i], processDecimal);
+        }
+        updatePrc.managePrice = scMathUtil.roundTo(sumMgP, -2);
+    }
+    //利润
+    if(profitPrc.length > 0){
+        let sumPfP = 0;
+        for(let i = 0, len  = profitPrc.length; i < len; i++){
+            sumPfP = scMathUtil.roundTo(sumPfP + profitPrc[i], processDecimal);
+        }
+        updatePrc.profitPrice = scMathUtil.roundTo(sumPfP, -2);
+    }
+    //风险费
+    if(riskPrc.length > 0){
+        let sumRkP = 0;
+        for(let i = 0, len  = riskPrc.length; i < len; i++){
+            sumRkP = scMathUtil.roundTo(sumRkP + riskPrc[i], processDecimal);
+        }
+        updatePrc.riskPrice = scMathUtil.roundTo(sumRkP, -2);
+    }
     //基价
-    updatePrc.basePrice = scMathUtil.roundTo(updatePrc.labourPrice + updatePrc.materialPrice + updatePrc.machinePrice, -2);
+    updatePrc.basePrice = scMathUtil.roundTo(
+        updatePrc.labourPrice + updatePrc.materialPrice + updatePrc.machinePrice + updatePrc.managePrice + updatePrc.profitPrice + updatePrc.riskPrice, -2);
     //更新定额数据
     ration.labourPrice = updatePrc.labourPrice.toString();
     ration.materialPrice = updatePrc.materialPrice.toString();
@@ -581,15 +732,24 @@ rationItemDAO.prototype.calcForRation = function (stdGljList, ration) {
     }
     //是否属于人工、材料、机械类型
     function getParentType(type){
-        if(labour.indexOf(type) !== -1){
+        if(labour.includes(type)){
             return 'labour';
         }
-        if(material.indexOf(type) !== -1){
+        if(material.includes(type)){
             return 'material';
         }
-        if(machine.indexOf(type) !== -1){
+        if(machine.includes(type)){
             return 'machine';
         }
+        if(manage.includes(type)){
+            return 'manage';
+        }
+        if(profit.includes(type)){
+            return 'profit';
+        }
+        if(risk.includes(type)){
+            return 'risk'
+        }
         return null;
     }
 
@@ -710,12 +870,31 @@ rationItemDAO.prototype.batchAddFromExcel = async function(rationRepId, data) {
     const existCodeList = await this.getRationItemByCondition(condition, ['code'], 'code');
     // 过滤插入数据
     let insertData = [];
+    //已存在定额,则更新价格及rationGLjList字段
+    let updateData = [];
     for (const ration of rationData) {
         if (existCodeList[ration.code] !== undefined) {
+            updateData.push(ration);
             continue;
         }
         insertData.push(ration);
     }
+    //更新定额
+    let updateBulk = [];
+    for(let ration of updateData){
+        this.calcForRation(stdGLJListByID, ration);
+        updateBulk.push({
+            updateOne: {
+                filter: {rationRepId: rationRepId, code: ration.code},
+                update: {$set: {rationGljList: ration.rationGljList, labourPrice: ration.labourPrice, materialPrice: ration.materialPrice,
+                    machinePrice: ration.machinePrice, basePrice: ration.basePrice}}
+            }
+        });
+    }
+    //更新数据库
+    if(updateBulk.length > 0){
+        await rationItemModel.bulkWrite(updateBulk);
+    }
     // 如果都已经存在,直接返回
     if (insertData.length <= 0) {
         return true;
@@ -751,17 +930,80 @@ rationItemDAO.prototype.exportExcelData = async function(rationRepId) {
         rationRepId: rationRepId
     };
     // @todo 限制导出的数量以防内存溢出
-    const rationDataList = await this.getRationItemByCondition(condition, ['name', 'code', 'ID', 'sectionId', 'feeType']);
+    const rationDataList = await this.getRationItemByCondition(condition, ['name', 'code', 'ID', 'sectionId', 'feeType', 'caption', 'basePrice']);
 
     // 整理数据
     let rationData = [];
     for (const tmp of rationDataList) {
         const sectionId = tmp.sectionId <= 0 || tmp.sectionId === undefined ? null : tmp.sectionId;
         const feeType = tmp.feeType === '' || tmp.feeType === undefined ? null : tmp.feeType;
-        const ration = [sectionId, feeType, tmp.ID, tmp.code, tmp.name];
+        const ration = [sectionId, feeType, tmp.ID, tmp.code, tmp.name, tmp.caption, tmp.basePrice];
         rationData.push(ration);
     }
-    const excelData = [['树ID', '取费专业', '定额ID', '定额编码', '定额名']];
+    //根据编号排序,优先级:number-number-..., number, Anumber....
+    /*let regConnector = /-/g,
+        regLetter = /[a-z,A-Z]/g,
+        regNum = /\d+/g;
+    rationData.sort(function (a, b) {
+        let aCode = a[3],
+            bCode = b[3],
+            rst = 0;
+        function compareConnector(arrA, arrB) {
+            let lessArr = arrA.length <= arrB ? arrA : arrB;
+            for (let i = 0; i < lessArr.length; i++) {
+                let result = compareUnit(arrA[i], arrB[i]);
+                if (result !== 0) {
+                    return result;
+                } else {
+
+                }
+            }
+            function compareUnit(uA, uB) {
+                let uAV = parseFloat(uA),
+                    uBV = parseFloat(uB);
+                if (uAV > uBV) {
+                    return 1;
+                } else if (uAV < uBV) {
+                    return -1;
+                }
+                return 0;
+            }
+        }
+        if (regConnector.test(a) && !regConnector.test(b)) {
+            rst = -1;
+        } else if (!regConnector.test(a) && regConnector.test(b)) {
+            rst = 1;
+        } else if (regConnector.test(a) && regConnector.test(b)) {
+
+        }
+    });
+    rationData.sort(function (a, b) {
+        let aCode = a[3],
+            bCode = b[3],
+            rst = 0,
+            splitA = aCode.split('-'),
+            splitB = bCode.split('-');
+        if(splitA[0] > splitB[0]){
+            rst = 1;
+        }
+        else if(splitA[0] < splitB[0]){
+            rst = -1;
+        }
+        else {
+            if(splitA[1] && splitB[1]){
+                let floatA = parseFloat(splitA[1]),
+                    floatB = parseFloat(splitB[1]);
+                if(floatA > floatB){
+                    rst = 1;
+                }
+                else if(floatA < floatB){
+                    rst = -1;
+                }
+            }
+        }
+        return rst;
+    });*/
+    const excelData = [['树ID', '取费专业', '定额ID', '定额编码', '定额名', '定额显示名称', '基价']];
     excelData.push.apply(excelData, rationData);
 
     return excelData;
@@ -778,24 +1020,28 @@ rationItemDAO.prototype.batchUpdateSectionIdFromExcel = async function(data) {
         return false;
     }
     // 批量执行update
-    const bulk = rationItemModel.collection.initializeOrderedBulkOp();
+    let bulkOprs = [];
     for (const tmp of data) {
         let rationId = parseInt(tmp[2]);
         rationId = isNaN(rationId) || rationId <= 0 ? 0 : rationId;
         let sectionId = parseInt(tmp[0]);
         sectionId = isNaN(sectionId) || sectionId <= 0 ? 0 : sectionId;
         // 取费专业
-        let feeType = parseInt(tmp[1]);
-        feeType = isNaN(feeType) || feeType <= 0 ? 0 : feeType;
-        if (sectionId <= 0 || rationId <= 0 || feeType <= 0) {
+        let feeType = tmp[1] ? parseInt(tmp[1]) : null;
+        feeType = isNaN(feeType) || feeType <= 0 ? null : feeType;
+        let name = tmp[4];
+        name = name ? name : '';
+        let caption = tmp[5];
+        caption = caption ? caption : '';
+        if (sectionId <= 0 || rationId <= 0) {
             continue;
         }
-
-        bulk.find({"ID": rationId}).update({$set: { sectionId: sectionId, feeType: feeType }});
+        bulkOprs.push({updateOne: {filter: {ID: rationId}, update: {$set: {sectionId: sectionId, feeType: feeType, name: name, caption: caption}}}});
     }
-
-    const result = await bulk.execute();
-    return result.isOk();
+    if(bulkOprs.length <= 0){
+        throw '无有效数据(树ID、定额ID不为空、且为数值)';
+    }
+    await rationItemModel.bulkWrite(bulkOprs);
 };
 
 module.exports = new rationItemDAO();

+ 1 - 0
modules/ration_repository/routes/ration_rep_routes.js

@@ -84,6 +84,7 @@ module.exports =  function (app) {
     apiRouter.post('/getRationItem',searchController.auth, searchController.init, searchController.getRationItem);
     apiRouter.post('/findRation', searchController.auth, searchController.init, searchController.findRation);
 
+    apiRouter.post('/reCalcAll', rationRepositoryController.auth, rationRepositoryController.init, rationRepositoryController.reCalcAll);
     // 导入导出定额库相关
     apiRouter.post('/upload', rationRepositoryController.auth, rationRepositoryController.init, rationRepositoryController.uploadSourceData);
     apiRouter.get('/export', rationRepositoryController.auth, rationRepositoryController.init, rationRepositoryController.exportRationData);

+ 30 - 29
modules/reports/controllers/rpt_tpl_controller.js

@@ -9,6 +9,7 @@ import counter from "../../../public/counter/counter";
 
 let RptTplModel = mongoose.model('rpt_templates');
 let TreeNodeModel = mongoose.model('rpt_tpl_tree');
+let rptExtCodeModel = mongoose.model('rpt_ext_code_tpl');
 
 let rptCustCfgFacade = require("../facade/rpt_cust_cfg_facade");
 
@@ -22,7 +23,7 @@ let callback = function(req, res, err, message, data){
 };
 
 let mExport = {
-    getCustomerCfg(req, res) {
+    getCustomerCfg: function(req, res) {
         let params = JSON.parse(req.body.params),
             userId = params.userId,
             me = this;
@@ -82,7 +83,7 @@ let mExport = {
             }
         })
     },
-    getDftTemplates(req, res) {
+    getDftTemplates: function(req, res) {
         let filter = {"userId": "-100", "$or": [{"isDeleted": null}, {"isDeleted": false} ]};
         TreeNodeModel.find(filter, '-_id', function(err, data){
             if (err) {
@@ -92,6 +93,15 @@ let mExport = {
             }
         });
     },
+    getExtCodeTpl: function(req, res) {
+        rptExtCodeModel.find({}).exec().then(function(rstCodeTpl) {
+            if (rstCodeTpl) {
+                callback(req,res, false, "", rstCodeTpl);
+            } else {
+                callback(req, res, 'The report template was not found!', null);
+            }
+        })
+    },
     getCompilationList(req, res) {
         let compilationModel = new CompilationModel();
         let compilationList = compilationModel.getCompilationList();
@@ -132,6 +142,21 @@ let mExport = {
             }
         });
     },
+    getTplTreeByCompilation: function (req, res) {
+        let params = JSON.parse(req.body.params),
+            compilationId = params.compilationId;
+        // if (req.session.sessionUser && req.session.sessionUser.id) sessionUserId = req.session.sessionUser.id;
+        if (!compilationId) {
+            compilationId = req.session.sessionCompilation._id;
+        }
+        rttFacade.findTplTreeByCompilation(compilationId).then(function(result) {
+            if (result) {
+                callback(req,res,false,"", result);
+            } else {
+                callback(req,res, true,"no result", null);
+            }
+        });
+    },
     updateTreeNodes: function(req, res) {
         let params = JSON.parse(req.body.params),
             nodes = params.nodes;
@@ -268,30 +293,6 @@ let mExport = {
             })
         }
     },
-    // createTplTreeNode: function(req, res){
-    //     let params = JSON.parse(req.body.params),
-    //         lastNodeId = params.lastNodeId,
-    //         nodeData = params.rawNodeData;
-    //     counter.counterDAO.getIDAfterCount(counter.moduleName.report, 1, function(err, result){
-    //         nodeData.ID = result.value.sequence_value;
-    //         let node = new TreeNodeModel(nodeData);
-    //         node.save(function (err, result) {
-    //             if (err) {
-    //                 callback(req,res, "树节点错误!", "", null);
-    //             } else {
-    //                 if (lastNodeId > 0) {
-    //                     TreeNodeModel.update({ID: lastNodeId}, {"NextSiblingID": nodeData.ID}, function(err, rst){
-    //                         if (err) {
-    //                             callback(req,res, "树节点错误!", "", null);
-    //                         } else {
-    //                             callback(req,res, false, "", result);
-    //                         }
-    //                     });
-    //                 } else callback(req,res, false, "", result);
-    //             }
-    //         });
-    //     });
-    // },
     getNewNodeID: function(req, res) {
         let params = JSON.parse(req.body.params),
             scope = params.scope;
@@ -324,7 +325,6 @@ let mExport = {
                     if (err) {
                         callback(req,res, "报表模板创建错误", "", null);
                     } else {
-                        //TreeNodeModel.update();
                         let filter = {"compilationId": compilationId, "engineerId": engineerId, "userId": userId, "items.ID": subNode.ID, "$or": [{"isDeleted": null}, {"isDeleted": false}]};
                         let updateStatement = {$set: {"items.$": subNode}};
                         rttFacade.updateTreeInDetail(filter, updateStatement).then(function (rst) {
@@ -358,8 +358,9 @@ let mExport = {
     updateRptTpl: function (req, res) {
         let params = JSON.parse(req.body.params),
             rptTpl = JSON.parse(params.rptTpl);
-        let filter = {"ID": parseInt(rptTpl[JV.PROP_ID])};
-        RptTplModel.update(filter, rptTpl, function (err, rst) {
+        let filter = {"ID": parseInt(rptTpl[JV.PROP_ID])},
+            options = {"overwrite": true};
+        RptTplModel.update(filter, rptTpl, options, function (err, rst) {
             if (err) {
                 callback(req, res, true, 'The report template was updated failed!', false);
             } else {

+ 7 - 1
modules/reports/facade/rpt_template_facade.js

@@ -3,12 +3,18 @@
  */
 import mongoose from "mongoose";
 let rpt_tpl_mdl = mongoose.model("rpt_templates");
+let rpt_tpl_ext_code_mdl = mongoose.model("rpt_ext_code_tpl");
 
 module.exports = {
-    getRptTemplate: getRptTemplate
+    getRptTemplate: getRptTemplate,
+    getExtCodeTpl: getExtCodeTpl
 };
 
 async function getRptTemplate(tplId) {
     //console.log('templateId: ' + parseInt(tplId));
     return await  rpt_tpl_mdl.findOne({"ID": parseInt(tplId)}, '-_id');
 }
+
+async function getExtCodeTpl() {
+    return await  rpt_tpl_ext_code_mdl.find({});
+}

+ 9 - 1
modules/reports/facade/rpt_tpl_data_facade.js

@@ -14,6 +14,7 @@ module.exports = {
 
 function prepareProjectData(userId, prjId, filter, callback) {
     let rawDataObj = null;
+    let basicInformation = prjMdl.project.getBasicInfo(prjId);
     prjMdl.project.getUserProject(userId, prjId, function(err, msg, rst){
         if (!err) {
             rawDataObj = {};
@@ -21,7 +22,14 @@ function prepareProjectData(userId, prjId, filter, callback) {
             projectDataMdl.getFilterData(prjId, filter, function (results) {
                 if (results) {
                     rawDataObj.prjData = results;
-                    callback(false, 'Succeeded!', rawDataObj);
+                    basicInformation.then(function(basicRst) {
+                        if (basicRst !== null && basicRst !== undefined) {
+                            let doc = (rawDataObj.prj._doc)?rawDataObj.prj._doc:rawDataObj.prj;
+                            doc.property.basicInformation = basicRst;
+                        }
+                        callback(false, 'Succeeded!', rawDataObj);
+                    });
+                    // callback(false, 'Succeeded!', rawDataObj);
                 } else {
                     callback(true, 'No data were found!', null);
                 }

+ 13 - 7
modules/reports/facade/rpt_tpl_tree_node_facade.js

@@ -65,15 +65,21 @@ async function findTplTreeByOid(objectId) {
     } else return null;
 }
 
+async function findTplTreeByCompilation(compilationId) {
+    let filter = {"compilationId": compilationId, "$or": [{"isDeleted": null}, {"isDeleted": false}]};
+    return await rpt_tpl_tree_mdl.find(filter);
+}
+
 
 let expObj = {
-    createNewTree:          createNewTree,
-    updateTree:             updateTree,
-    updateTreeInDetail:     updateTreeInDetail,
-    removeTree:             removeTree,
-    removeTreePhycically:   removeTreePhycically,
-    findTplTree:            findTplTree,
-    findTplTreeByOid:       findTplTreeByOid
+    createNewTree:              createNewTree,
+    updateTree:                 updateTree,
+    updateTreeInDetail:         updateTreeInDetail,
+    removeTree:                 removeTree,
+    removeTreePhycically:       removeTreePhycically,
+    findTplTree:                findTplTree,
+    findTplTreeByOid:           findTplTreeByOid,
+    findTplTreeByCompilation:   findTplTreeByCompilation
 };
 
 export {expObj as default};

+ 1 - 1
modules/reports/routes/report_router_operation.js

@@ -12,7 +12,7 @@ module.exports =function (app) {
             res.redirect('/login');
         }
         else {
-            res.render('maintain/report/rpt_test.html',
+            res.render('maintain/report/html/rpt_test.html',
                 {userAccount: req.session.userAccount,
                     userID: req.session.managerData.userID});
         }

+ 9 - 3
modules/reports/routes/rpt_tpl_router.js

@@ -1,5 +1,6 @@
 import express from "express";
 let rptTplRouter = express.Router();
+let config = require("../../../config/config.js");
 import reportTplController from "./../controllers/rpt_tpl_controller";
 import reportCfgController from "./../controllers/rpt_cfg_controller";
 
@@ -9,9 +10,12 @@ module.exports = function (app) {
             res.redirect('/login');
         }
         else {
-            res.render('maintain/report/rpt_tpl_main.html',
-                {userAccount: req.session.managerData.username,
-                    userID: req.session.managerData.userID});
+            res.render('maintain/report/html/rpt_tpl_main.html',
+                {   userAccount: req.session.managerData.username,
+                    userID: req.session.managerData.userID,
+                    LicenseKey:config.getLicenseKey(process.env.NODE_ENV)
+                }
+            );
         }
     });
 
@@ -21,6 +25,7 @@ module.exports = function (app) {
     rptTplRouter.post('/updateSubLevelOneNode', reportTplController.updateSubLevelOneNode);
     rptTplRouter.post('/removeTreeRootNode', reportTplController.removeTreeRootNode);
     rptTplRouter.post('/getRptTplTree', reportTplController.getRptTplTree);
+    rptTplRouter.post('/getTplTreeByCompilation', reportTplController.getTplTreeByCompilation);
     rptTplRouter.post('/getNewNodeID', reportTplController.getNewNodeID);
     rptTplRouter.post('/updateRptTplNodes', reportTplController.updateTreeNodes);
     rptTplRouter.post('/deleteRptTplNodes', reportTplController.deleteRptTplNodes);
@@ -31,6 +36,7 @@ module.exports = function (app) {
     rptTplRouter.post('/getCompilationList', reportTplController.getCompilationList);
     rptTplRouter.post('/getCustomizeCfg', reportTplController.getCustomerCfg);
     rptTplRouter.post('/saveCustomerCfg', reportTplController.saveCustomerCfg);
+    rptTplRouter.post('/getExtCodeTpl', reportTplController.getExtCodeTpl);
 
     rptTplRouter.post('/getUserRptCfg', reportCfgController.getReportUserCfg);
     rptTplRouter.post('/getMappingFields', reportCfgController.getAllMappingFields);

+ 43 - 18
modules/reports/rpt_component/helper/jpc_helper_common.js

@@ -1,4 +1,5 @@
 let JV = require('../jpc_value_define');
+let stringUtil = require('../../../../public/stringUtil');
 
 let JpcCommonHelper = {
     commonConstant: {},
@@ -132,31 +133,55 @@ let JpcCommonHelper = {
         }
         return rst;
     },
-    getStringLinesInArea: function(area, strVal, pdfDoc) {
-        let areaWidth = area[JV.PROP_RIGHT] - area[JV.PROP_LEFT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_LEFT] - 2;
-        let txtWidth = pdfDoc.widthOfString(strVal);
-        let rst = parseInt(txtWidth / areaWidth);
-        if (txtWidth % areaWidth > 0) {
-            rst++;
+    getStringLinesInArea: function(area, strVal, pdfDoc, chnW, otherW) {
+        //备注: 因后台的pdf kit判断字符串长度与前端的不一样,需要做些调整,不一次性地判断字符串长度。
+        //      分2种字符:中文与非中文,按照各种字符的数量分别乘以相关一个字符的宽度再累计。
+        //      另判断行数还不能直接用总长度除以宽度来计算,因每一行都会有不同的余量,所以得一行行走过来判断。
+        let rst = 0;
+        if (strVal) {
+            let areaWidth = area[JV.PROP_RIGHT] - area[JV.PROP_LEFT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_LEFT] - 1;
+            let txtWidth = 0, currentW = 0;
+            for (let sIdx = 0; sIdx < strVal.length; sIdx++) {
+                currentW = (strVal.charCodeAt(sIdx) > 127)?chnW:otherW;
+                txtWidth += currentW;
+                if (txtWidth > areaWidth) {
+                    rst++;
+                    txtWidth = currentW;
+                }
+                if (sIdx === strVal.length - 1) {
+                    rst++;
+                }
+            }
         }
         if (rst === 0) rst = 1; //即使是空字符串,也得有一行啊
         return rst;
-        //备注: 其实是想用canvas的,但node canvas装起来麻烦,暂时用PDF Kit来顶用一下,以后换新方法再用
+        //备注: 其实是想用canvas的,但node canvas装起来麻烦,暂时用PDF Kit来顶用一下,以后有更好的再换
     },
-    splitString: function (area, strVal, pdfDoc) {
+    splitString: function (area, strVal, pdfDoc, chnW, otherW) {
         let rst = [];
-        let areaWidth = area[JV.PROP_RIGHT] - area[JV.PROP_LEFT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_LEFT] - 2;
-        let preSIdx = 0, txtWidth = 0;
-        for (let sIdx = 1; sIdx <= strVal.length; sIdx++) {
-            txtWidth = pdfDoc.widthOfString(strVal.substr(preSIdx, sIdx - preSIdx));
-            if (txtWidth > areaWidth) {
-                rst.push(strVal.substr(preSIdx, sIdx - preSIdx - 1));
-                preSIdx = sIdx - 1;
-            }
-            if (sIdx === strVal.length) {
-                rst.push(strVal.substr(preSIdx, sIdx - preSIdx));
+        if (strVal) {
+            let areaWidth = area[JV.PROP_RIGHT] - area[JV.PROP_LEFT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_LEFT] - 1;
+            let preSIdx = 0, txtWidth = 0;
+            let currentW = 0;
+            for (let sIdx = 0; sIdx < strVal.length; sIdx++) {
+                currentW = (strVal.charCodeAt(sIdx) > 127)?chnW:otherW;
+                txtWidth += currentW;
+                if (txtWidth > areaWidth) {
+                    if (preSIdx < sIdx) {
+                        rst.push(strVal.substr(preSIdx, sIdx - preSIdx));
+                        preSIdx = sIdx;
+                    } else {
+                        rst.push(strVal.substr(preSIdx, 1));
+                        preSIdx = sIdx + 1;
+                    }
+                    txtWidth = currentW;
+                }
+                if (sIdx === strVal.length - 1) {
+                    rst.push(strVal.substr(preSIdx, strVal.length - preSIdx));
+                }
             }
         }
+        if (rst.length === 0) rst.push(''); //什么都没有,也得整个空串
         return rst;
     }
 };

+ 32 - 1
modules/reports/rpt_component/helper/jpc_helper_field.js

@@ -13,7 +13,23 @@ let JpcFieldHelper = {
             dataField[valueIdx] = newValue;
         }
     },
+    resetSumFormat: function(ref_tab_fields, sum_tab_field) {
+        let rst = false;
+        if (ref_tab_fields && ref_tab_fields.length > 0) {
+            for (let tab_field of ref_tab_fields) {
+                if (tab_field[JV.PROP_FIELD_ID] === sum_tab_field[JV.PROP_FIELD_ID]) {
+                    rst = true;
+                    if (tab_field[JV.PROP_FORMAT]) {
+                        sum_tab_field[JV.PROP_FORMAT] = tab_field[JV.PROP_FORMAT];
+                    }
+                    break;
+                }
+            }
+        }
+        return rst;
+    },
     resetFormat: function (tab_field, map_field, customizeCfg) {
+        let rst = false;
         if (map_field && map_field[JV.PROP_PRECISION] && map_field[JV.PROP_PRECISION].type === "fixed") {
             let formatStrs = ["#."], ffStr = (customizeCfg && customizeCfg.fillZero)?"0":"#";
             for (let idx = 0; idx < parseInt(map_field[JV.PROP_FIXED_PRECISION_AMT]); idx++) {
@@ -24,7 +40,10 @@ let JpcFieldHelper = {
             } else {
                 tab_field[JV.PROP_FORMAT] = formatStrs.join("");
             }
+            map_field[JV.PROP_FORMAT] = tab_field[JV.PROP_FORMAT]; //这里顺手把format也赋给map_field,后期统计用得着
+            rst = true;
         }
+        return rst;
     },
     resetFlexibleFormat: function (tab_field, ref_field_data, flexiblePrecisionRefObj, valueIdx, customizeCfg) {
         let precisionAmt = 2;
@@ -70,7 +89,19 @@ let JpcFieldHelper = {
                 }
                 if (!isFounded) {
                     if (rstFields) rstFields.push(tab_fields[i]);
-                    if (rstFieldsIdx) rstFieldsIdx.push(JV.BLANK_FIELD_INDEX);
+                    if (rstFieldsIdx) {
+                        if (rptTpl[JV.NODE_NO_MAPPING_FIELDS] && rptTpl[JV.NODE_NO_MAPPING_FIELDS].length > 0) {
+                            for (let discretField of rptTpl[JV.NODE_NO_MAPPING_FIELDS]) {
+                                if (discretField[JV.PROP_ID] === tab_fields[i]["FieldID"]) {
+                                    rstFieldsIdx.push(discretField);
+                                    isFounded = true;
+                                    break;
+                                }
+                            }
+                        } else {
+                            rstFieldsIdx.push(JV.BLANK_FIELD_INDEX);
+                        }
+                    }
                 }
             }
         }

+ 1 - 1
modules/reports/rpt_component/jpc_bill_tab.js

@@ -114,7 +114,7 @@ JpcBillTabSrv.prototype.createNew = function(){
                 for (let i = 0; i < tab_fields.length; i++) {
                     let tab_field = tab_fields[i];
                     let data_field = null, map_data_field = JE.F(tab_field[JV.PROP_FIELD_ID], $CURRENT_RPT);
-                    if (me.disp_fields_idx.length > i && me.disp_fields_idx[i] !== JV.BLANK_FIELD_INDEX) {
+                    if (me.disp_fields_idx.length > i && me.disp_fields_idx[i] !== JV.BLANK_FIELD_INDEX && (typeof me.disp_fields_idx[i] !== 'object')) {
                         data_field = data_details[me.disp_fields_idx[i]];
                     } else {
                         if (map_data_field) {

+ 3 - 3
modules/reports/rpt_component/jpc_ex.js

@@ -173,16 +173,16 @@ JpcExSrv.prototype.createNew = function(){
         //pre-condition: the data should be sorted in SQL/NoSQL level!
         //let dt1 = new Date();
         if (me.flowTab) {
-            me.flowTab.sorting(rptTpl, dataObj, dataHelper.dataSeq.slice(0));
+            me.flowTab.sorting(rptTpl, dataObj, dataHelper.dataSeq.slice(0), me);
             if (me.flowTabEx) {
                 me.flowTabEx.sorting(rptTpl, dataObj, dataHelper.exDataSeq.slice(0));
             }
         }
         if (me.billTab) {
-            me.billTab.sorting(rptTpl, dataObj, dataHelper.dataSeq.slice(0));
+            me.billTab.sorting(rptTpl, dataObj, dataHelper.dataSeq.slice(0), me);
         }
         if (me.crossTab) {
-            me.crossTab.sorting(rptTpl, dataObj, dataHelper.dataSeq.slice(0));
+            me.crossTab.sorting(rptTpl, dataObj, dataHelper.dataSeq.slice(0), me);
         }
         //let dt2 = new Date();
         //alert(dt2 - dt1);

+ 152 - 0
modules/reports/rpt_component/jpc_flow_tab.js

@@ -9,11 +9,18 @@ let JpcDiscreteHelper = require('./helper/jpc_helper_discrete');
 let JpcTextHelper = require('./helper/jpc_helper_text');
 let JpcCommonOutputHelper = require('./helper/jpc_helper_common_output');
 let JpcAreaHelper = require('./helper/jpc_helper_area');
+<<<<<<< HEAD
 // let PDFKit = require('pdfkit');
 // let fontUtil = require('../util/rpt_font_util');
 let fsUtil = require("../../../public/fsUtil");
 let strUtil = require("../../../public/stringUtil");
 let fontWidthMap = require('./helper/jpc_helper_font_width');
+=======
+let PDFKit = require('pdfkit');
+let fontUtil = require('../util/rpt_font_util');
+let fsUtil = require("../../../public/fsUtil");
+let strUtil = require("../../../public/stringUtil");
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
 
 
 let JpcFlowTabSrv = function(){};
@@ -173,6 +180,7 @@ JpcFlowTabSrv.prototype.createNew = function(){
         if (rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_SEG_SUM]) JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_SEG_SUM][JV.PROP_SUM_FIELDS], me.seg_sum_tab_fields, me.seg_sum_fields_idx, me.isEx);
         if (rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_PAGE_SUM]) JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_PAGE_SUM][JV.PROP_SUM_FIELDS], me.page_sum_tab_fields, me.page_sum_fields_idx, me.isEx);
         if (rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_GROUP]) {
+<<<<<<< HEAD
             JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_GROUP][JV.PROP_GROUP_FIELDS], me.group_check_fields, null, me.isEx);
             if (rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_GROUP][JV.PROP_GROUP_LINES]) {
                 for (let grpLine of rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_GROUP][JV.PROP_GROUP_LINES]) {
@@ -184,6 +192,10 @@ JpcFlowTabSrv.prototype.createNew = function(){
             for (let grp_sum_f of me.group_sum_fields) {
                 grp_sum_f[JV.PROP_SUM_KEY] = `grp_sum_key_` + grp_sum_f[JV.PROP_FIELD_ID];
             }
+=======
+            JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_GROUP][JV.PROP_GROUP_FIELDS], me.group_fields, null, me.isEx);
+            JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_GROUP][JV.PROP_SUM_FIELDS], me.group_sum_fields, null, me.isEx);
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
         }
         JpcFieldHelper.findAutoHeightFieldIdx(rptTpl, rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_CONTENT][JV.PROP_FLOW_FIELDS], me.auto_height_fields_idx, me.isEx);
         for (let si = 0; si < dataSeq.length; si++) {
@@ -197,10 +209,17 @@ JpcFlowTabSrv.prototype.createNew = function(){
         let data_details = me.isEx?dataObj[JV.DATA_DETAIL_DATA_EX]:dataObj[JV.DATA_DETAIL_DATA],
             data_fields = [];
         for (let i = 0; i < me.seg_sum_fields_idx.length; i++) {
+<<<<<<< HEAD
             if (typeof(me.seg_sum_fields_idx[i]) === "object") {
                 let exField = JE.F(me.seg_sum_fields_idx[i][JV.PROP_ID], $CURRENT_RPT);
                 if (exField) {
                     data_fields.push(exField[JV.PROP_AD_HOC_DATA]);
+=======
+            if (typeof(me.seg_sum_fields_idx[i])=="object") {
+                let exField = JE.F(me.seg_sum_fields_idx[i][JV.PROP_ID], $CURRENT_RPT);
+                if (exField) {
+                    data_fields.push(exField["data_field"]);
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
                 } else {
                     data_fields.push(null);
                 }
@@ -230,17 +249,54 @@ JpcFlowTabSrv.prototype.createNew = function(){
                     for (let j = 0; j < me.segments[i].length; j++) {
                         //3. start to sum
                         // rowGrandTotal[di] = rowGrandTotal[di] + 1.0 * JpcFieldHelper.getValue(data_fields[di], me.segments[i][j]);
+<<<<<<< HEAD
                         let sv = JpcFieldHelper.getValue(data_fields[di], me.segments[i][j]);
                         if (sv) {
                             rowGrandTotal[di] = rowGrandTotal[di] + parseFloat(parseFloat(sv).toFixed(precisionAmt));
                         }
+=======
+                        rowGrandTotal[di] = rowGrandTotal[di] + parseFloat(parseFloat(JpcFieldHelper.getValue(data_fields[di], me.segments[i][j])).toFixed(precisionAmt));
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
                     }
                 }
                 me.segSumValLst.push(rowGrandTotal);
             }
         }
     };
+<<<<<<< HEAD
 
+=======
+    JpcFlowTabResult.getFlowFieldById = function (fieldKey, rptTpl) {
+        let rst = null;
+        if (rptTpl[JV.NODE_FLOW_INFO][JV.NODE_FLOW_GROUP] && rptTpl[JV.NODE_FLOW_INFO][JV.NODE_FLOW_GROUP][JV.PROP_GROUP_LINES] && rptTpl[JV.NODE_FLOW_INFO][JV.NODE_FLOW_GROUP][JV.PROP_GROUP_LINES].length > 0) {
+            for (let grpLine of rptTpl[JV.NODE_FLOW_INFO][JV.NODE_FLOW_GROUP][JV.PROP_GROUP_LINES]) {
+                if (grpLine[JV.PROP_GROUP_SUM_KEYS] && grpLine[JV.PROP_GROUP_SUM_KEYS].length > 0) {
+                    for (let sumKey of grpLine[JV.PROP_GROUP_SUM_KEYS]) {
+                        if (sumKey[JV.PROP_SUM_KEY] === fieldKey) {
+                            rst = sumKey;
+                            break;
+                        }
+                    }
+                }
+                if (rst) break;
+            }
+        }
+        if (!(rst) && rptTpl[JV.NODE_FLOW_INFO_EX] && rptTpl[JV.NODE_FLOW_INFO_EX][JV.NODE_FLOW_GROUP] && rptTpl[JV.NODE_FLOW_INFO_EX][JV.NODE_FLOW_GROUP][JV.PROP_GROUP_LINES] && rptTpl[JV.NODE_FLOW_INFO_EX][JV.NODE_FLOW_GROUP][JV.PROP_GROUP_LINES].length > 0) {
+            for (let grpLine of rptTpl[JV.NODE_FLOW_INFO_EX][JV.NODE_FLOW_GROUP][JV.PROP_GROUP_LINES]) {
+                if (grpLine[JV.PROP_GROUP_SUM_KEYS] && grpLine[JV.PROP_GROUP_SUM_KEYS].length > 0) {
+                    for (let sumKey of grpLine[JV.PROP_GROUP_SUM_KEYS]) {
+                        if (sumKey[JV.PROP_SUM_KEY] === fieldKey) {
+                            rst = sumKey;
+                            break;
+                        }
+                    }
+                }
+                if (rst) break;
+            }
+        }
+        return rst;
+    };
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
     JpcFlowTabResult.sumUpGrp = function (rptTpl, $CURRENT_RPT, dataObj, segIdx, preGrpIdx, nexGrpIdx) {
         let me = this, segDataIdx = me.segments[segIdx];
         for (let j = 0; j < me.group_sum_fields.length; j++) {
@@ -257,7 +313,11 @@ JpcFlowTabSrv.prototype.createNew = function(){
                     if (sum_field[JV.PROP_PRECISION] && sum_field[JV.PROP_PRECISION].type === `fixed`) {
                         precisionAmt = sum_field[JV.PROP_FIXED_PRECISION_AMT];
                     } else {
+<<<<<<< HEAD
                         let flowF = me.group_sum_fields[j]; //调整后,me.group_sum_fields[j]就是group lines下数组SumKey_S的元素值,无需再找
+=======
+                        let flowF = me.getFlowFieldById(me.group_sum_fields[j][JV.PROP_SUM_KEY], rptTpl);
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
                         if (flowF && !strUtil.isEmptyString(flowF[JV.PROP_FORMAT])) {
                             let idx = flowF[JV.PROP_FORMAT].indexOf('.');
                             if (idx >= 0) {
@@ -403,6 +463,7 @@ JpcFlowTabSrv.prototype.createNew = function(){
                         let chkFontName = '宋体';
                         let chkFontHeight = 12;
                         if (font) {
+<<<<<<< HEAD
                             chkFontName = font[JV.FONT_PROPS[0]];
                             chkFontHeight = font[JV.FONT_PROPS[JV.FONT_PROP_IDX_HEIGHT]];
                         }
@@ -412,6 +473,20 @@ JpcFlowTabSrv.prototype.createNew = function(){
                         let chnW = fontWidthMap.getFontWidth(chkFontName, chkFontHeight, '宽'), otherW = fontWidthMap.getFontWidth(chkFontName, chkFontHeight, '窄');
                         for (let i = 0; i < values.length; i++) {
                             let amt = JpcCommonHelper.getStringLinesInArea(area, values[i], chnW, otherW);
+=======
+                            let fontFile = __dirname.slice(0, __dirname.length - 14) + '/util/pdf_base_files/' + fontUtil.getActualFont(font[JV.FONT_PROPS[0]], (font[JV.FONT_PROPS[3]] === 'T'), (font[JV.FONT_PROPS[4]] === 'T')) + '.ttf';
+                            doc.font(fontFile);
+                            doc.fontSize(parseInt(font[JV.FONT_PROPS[JV.FONT_PROP_IDX_HEIGHT]]));
+                        } else {
+                            doc.font(__dirname.slice(0, __dirname.length - 14) + '/util/pdf_base_files/Smart.ttf');
+                            doc.fontSize(12);
+                        }
+                        let hasSplitStr = false, splitStrArr = [];
+                        let accAmt = 0;
+                        let chnW = doc.widthOfString('一'), otherW = doc.widthOfString('_');
+                        for (let i = 0; i < values.length; i++) {
+                            let amt = JpcCommonHelper.getStringLinesInArea(area, values[i], doc, chnW, otherW);
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
                             accAmt += amt;
                             if (amt > 1) {
                                 hasSplitStr = true;
@@ -419,14 +494,21 @@ JpcFlowTabSrv.prototype.createNew = function(){
                             }
                         }
                         if (accAmt > rst) rst = accAmt;
+<<<<<<< HEAD
                         // if (hasSplitStr && outputType !== JV.OUTPUT_TYPE_EXCEL) {
+=======
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
                         if (hasSplitStr) {
                             let newValArr = [];
                             for (let i = 0; i < values.length; i++) {
                                 if (splitStrArr.indexOf(i) < 0) {
                                     newValArr.push(values[i]);
                                 } else {
+<<<<<<< HEAD
                                     newValArr = newValArr.concat(JpcCommonHelper.splitString(area, values[i], chnW, otherW));
+=======
+                                    newValArr = newValArr.concat(JpcCommonHelper.splitString(area, values[i], doc, chnW, otherW));
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
                                 }
                             }
                             JpcFieldHelper.setValue(data_field, theRecIdx, newValArr.join('|'));
@@ -514,7 +596,10 @@ JpcFlowTabSrv.prototype.createNew = function(){
                 maxRowRec = JpcFlowTabHelper.getMaxRowsPerPage(bands, rptTpl, me.isEx);
             }
             let handledRowAmt = 0; //handledRowAmt纪录的是真正处理过的显示行数,包含了空白行,主要是为分页用(自动行高)
+<<<<<<< HEAD
             let segRestRecAmt = 0; //一般情况下为0,只有在特殊情况下才需要,如09-x表中,正常流水一页显示不了的时候,就有大问题了
+=======
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
             function private_addPage(segIdx, grpSeqInfo, isFollow, isMix, mixSplitPoint) {
                 private_resetBandArea();
                 me.pageStatusLst.push(pageStatus.slice(0));
@@ -573,7 +658,10 @@ JpcFlowTabSrv.prototype.createNew = function(){
                 let grpRecAmtEx = 0;
                 let accAutoHeightAmt = 0; //累计的自动行高数量
                 handledRowAmt = 0; //初始化每一段的已处理纪录行数
+<<<<<<< HEAD
                 segRestRecAmt = 0;
+=======
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
                 if (followTabEx && followTabEx.group_node_info) {
                     grpRecAmtEx = followTabEx.group_node_info.length * followTabEx.group_lines_amt;
                 }
@@ -713,7 +801,11 @@ JpcFlowTabSrv.prototype.createNew = function(){
                     }
                     //检测是否可退出
                     // if ((currentRecAmt + accAutoHeightAmt + adHocAutoHeightAmt >= ttlSegRecAmt) && (pageIdx % me.multiCols === 0)) {
+<<<<<<< HEAD
                     if (handledRowAmt >= (ttlSegRecAmt + segRestRecAmt) && (pageIdx % me.multiCols === 0)) {
+=======
+                    if (handledRowAmt >= ttlSegRecAmt && (pageIdx % me.multiCols === 0)) {
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
                         //备注:这里必须得考虑多栏的情况,否则会造成pageStatus出界的问题
                         break;
                     }
@@ -988,10 +1080,17 @@ JpcFlowTabSrv.prototype.createNew = function(){
                 let contentValuesIdx = me.dispValueIdxLst[page - 1];
                 let page_sum_data_fields = [];
                 for (let i = 0; i < me.page_sum_fields_idx.length; i++) {
+<<<<<<< HEAD
                     if (typeof(me.page_sum_fields_idx[i]) === "object") {
                         let exField = JE.F(me.page_sum_fields_idx[i][JV.PROP_ID], $CURRENT_RPT);
                         if (exField) {
                             page_sum_data_fields.push(exField[JV.PROP_AD_HOC_DATA]);
+=======
+                    if (typeof(me.page_sum_fields_idx[i])=="object") {
+                        let exField = JE.F(me.page_sum_fields_idx[i][JV.PROP_ID], $CURRENT_RPT);
+                        if (exField) {
+                            page_sum_data_fields.push(exField["data_field"]);
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
                         } else {
                             page_sum_data_fields.push(null);
                         }
@@ -1069,7 +1168,11 @@ JpcFlowTabSrv.prototype.createNew = function(){
                         for (let idx_ex = 0; idx_ex < tab_fields_ex.length; idx_ex++) {
                             let tab_fieldex = tab_fields_ex[idx_ex];
                             let data_fieldex = null, map_data_fieldex = JE.F(tab_fieldex[JV.PROP_FIELD_ID], $CURRENT_RPT);
+<<<<<<< HEAD
                             if (me.disp_fields_ex_idx[idx_ex] !== JV.BLANK_FIELD_INDEX && (typeof me.disp_fields_ex_idx[idx_ex] !== 'object')) {
+=======
+                            if (me.disp_fields_ex_idx[idx_ex] !== JV.BLANK_FIELD_INDEX && (typeof me.disp_fields_idx[idx_ex] !== 'object')) {
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
                                 data_fieldex = data_details_ex[me.disp_fields_ex_idx[idx_ex]];
                             } else {
                                 if (map_data_fieldex) {
@@ -1114,10 +1217,14 @@ JpcFlowTabSrv.prototype.createNew = function(){
                             }
                             // rowGrandTotal[di] = rowGrandTotal[di] + 1.0 * JpcFieldHelper.getValue(page_sum_data_fields[di], contentValuesIdx[rowIdx][2]);
                             if (contentValuesIdx[rowIdx][2] >= 0) {
+<<<<<<< HEAD
                                 let psv = JpcFieldHelper.getValue(page_sum_data_fields[di], contentValuesIdx[rowIdx][2]);
                                 if (psv) {
                                     rowGrandTotal[di] = rowGrandTotal[di] + parseFloat(parseFloat(psv).toFixed(precisionAmt));
                                 }
+=======
+                                rowGrandTotal[di] = rowGrandTotal[di] + parseFloat(parseFloat(JpcFieldHelper.getValue(page_sum_data_fields[di], contentValuesIdx[rowIdx][2])).toFixed(precisionAmt));
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
                             }
                         }
                     }
@@ -1131,16 +1238,31 @@ JpcFlowTabSrv.prototype.createNew = function(){
                             if (contentValuesIdx[rowIdx][3] === grpIdx) {
                                 let grp_line = rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_GROUP][JV.PROP_GROUP_LINES][grpIdx];
                                 if (page === 1) {
+<<<<<<< HEAD
                                     if (grp_line[JV.PROP_GROUP_SUM_KEYS] && grp_line[JV.PROP_GROUP_SUM_KEYS].length > 0) {
                                         for (let grp_sum_field of grp_line[JV.PROP_GROUP_SUM_KEYS]) {
                                             if (grp_sum_field[JV.PROP_FIELD_ID]) {
                                                 let map_field = JE.F(grp_sum_field[JV.PROP_FIELD_ID], $CURRENT_RPT);
                                                 JpcFieldHelper.resetFormat(grp_sum_field, map_field, customizeCfg);
+=======
+                                    let sumFields = rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_GROUP][JV.PROP_SUM_FIELDS];
+                                    for (let sumF of sumFields) {
+                                        if (grp_line[JV.PROP_GROUP_SUM_KEYS] && grp_line[JV.PROP_GROUP_SUM_KEYS].length > 0) {
+                                            for (let grp_sum_field of grp_line[JV.PROP_GROUP_SUM_KEYS]) {
+                                                if (grp_sum_field[JV.PROP_SUM_KEY] === sumF[JV.PROP_SUM_KEY]) {
+                                                    let map_field = JE.F(sumF[JV.PROP_FIELD_ID], $CURRENT_RPT);
+                                                    JpcFieldHelper.resetFormat(grp_sum_field, map_field, customizeCfg);
+                                                }
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
                                             }
                                         }
                                     }
                                 }
+<<<<<<< HEAD
                                 let lineRst = me.outputTabGrpLine(band, grp_line, page, contentValuesIdx[rowIdx], contentValuesIdx.length, rowIdx, 1, 0, unitFactor, true, controls, multiColIdx, grpCntIdx, $CURRENT_RPT);
+=======
+                                let lineRst = me.outputTabGrpLine(band, grp_line, page, contentValuesIdx[rowIdx], contentValuesIdx.length, rowIdx, 1, 0, unitFactor, true, controls, multiColIdx);
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
                                 rst = rst.concat(lineRst);
                             }
                         }
@@ -1153,9 +1275,12 @@ JpcFlowTabSrv.prototype.createNew = function(){
         for (let idIdx = eliminateCells.length - 1; idIdx >= 0; idIdx--) {
             rst.splice(eliminateCells[idIdx], 1);
         }
+<<<<<<< HEAD
         me.checkCombineEvent(JV.RUN_TYPE_BEFORE_COMBINE, verticalCombinePos, horizonCombinePos, rst, $CURRENT_RPT);
         me.combinePageCells(rst, verticalCombinePos, horizonCombinePos);
         me.checkCombineEvent(JV.RUN_TYPE_AFTER_COMBINE, verticalCombinePos, horizonCombinePos, rst, $CURRENT_RPT);
+=======
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
         return rst;
     };
     JpcFlowTabResult.checkCombineEvent = function ($RUN_TYPE, $VER_COMB_ARRAY, $HOR_COMB_ARRAY, $CURRENT_CELL_ITEMS, $CURRENT_RPT) {
@@ -1237,7 +1362,11 @@ JpcFlowTabSrv.prototype.createNew = function(){
                     }
                     //备注:考虑到有时候会出现没有数据可合计的scenario,得有容错处理
                     let cellItem = JpcCommonOutputHelper.createCommonOutput(tab_fields[i], sumVal, controls);
+<<<<<<< HEAD
                     cellItem[JV.PROP_AREA] = JpcAreaHelper.outputArea(tab_fields[i][JV.PROP_AREA], band, unitFactor, 1, 0, 1, 0, me.multiCols, 0, false, false);
+=======
+                    cellItem[JV.PROP_AREA] = JpcAreaHelper.outputArea(tab_fields[i][JV.PROP_AREA], band, unitFactor, 1, 0, 1, 0, me.multiCols, 0, true, false);
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
                     rst.push(cellItem);
                 }
                 rst = rst.concat(me.commonTabRestOutput(dataObj, page, segIdx, bands, band, unitFactor, tab, 0));
@@ -1267,7 +1396,11 @@ JpcFlowTabSrv.prototype.createNew = function(){
                         }
                     }
                     let cellItem = JpcCommonOutputHelper.createCommonOutput(tab_fields[i], me.pageSumValLst[page - 1][i], controls);
+<<<<<<< HEAD
                     cellItem[JV.PROP_AREA] = JpcAreaHelper.outputArea(tab_fields[i][JV.PROP_AREA], band, unitFactor, 1, 0, 1, 0, me.multiCols, 0, false, false);
+=======
+                    cellItem[JV.PROP_AREA] = JpcAreaHelper.outputArea(tab_fields[i][JV.PROP_AREA], band, unitFactor, 1, 0, 1, 0, me.multiCols, 0, true, false);
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
                     rst.push(cellItem);
                 }
                 rst = rst.concat(me.commonTabRestOutput(dataObj, page, segIdx, bands, band, unitFactor, tab, 0));
@@ -1397,18 +1530,29 @@ function setupControl(mergeCell, controls) {
         orgCtrl = controls[mergeCell[JV.PROP_CONTROL]];
         mergeCell[JV.PROP_CONTROL] = {
             "Shrink": "T",
+<<<<<<< HEAD
             "ShrinkFirst": orgCtrl.ShrinkFirst,
             "CloseOutput": orgCtrl.CloseOutput,
             "ShowZero": orgCtrl.ShowZero,
             "Horizon": orgCtrl.Horizon,
             "Vertical": orgCtrl.Vertical,
             "Wrap": (mergeCell[JV.PROP_IS_AUTO_HEIGHT])?'T':'F',
+=======
+            "ShowZero": orgCtrl.ShowZero,
+            "Horizon": orgCtrl.Horizon,
+            "Vertical": orgCtrl.Vertical,
+            "Wrap": "T",
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
             "VerticalForExcel": "center"
         };
     } else {
         mergeCell[JV.PROP_CONTROL].Shrink = "T";
         mergeCell[JV.PROP_CONTROL].Vertical = "top";
+<<<<<<< HEAD
         mergeCell[JV.PROP_CONTROL].Wrap = (mergeCell[JV.PROP_IS_AUTO_HEIGHT])?'T':'F',
+=======
+        mergeCell[JV.PROP_CONTROL].Wrap = "T";
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
         mergeCell[JV.PROP_CONTROL].VerticalForExcel = "center";
         orgCtrl = mergeCell[JV.PROP_CONTROL];
     }
@@ -1447,13 +1591,21 @@ function combineAutoHeightCells(prepareObj, page, controls) {
                         rst.push(sameColCells[i].cellIdx); //记下Cell的位置,在函数外消除
                         //如果到了最后一条数据,得判断firstMergeCell是否满格(即数据是满的,有可能有些格数据也有折行但不是自动行高判断指标)
                         //不满格的cell的Vertical强制设置为 'center'
+<<<<<<< HEAD
                         if (i === sameColCells.length - 1 && validValueAmt !== fullValidValueAmt && (fullValidValueAmt / validValueAmt > 2.5) ) {
+=======
+                        if (i === sameColCells.length - 1 && validValueAmt !== fullValidValueAmt) {
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
                             firstMergeCell[JV.PROP_CONTROL].VerticalForExcel = 'center';
                         }
                     } else {
                         //碰到新开始的自动行高行,判断原先的firstMergeCell是否满格(即数据是满的,有可能有些格数据也有折行但不是自动行高判断指标)
                         //不满格的cell的Vertical强制设置为 'center'
+<<<<<<< HEAD
                         if (validValueAmt !== fullValidValueAmt && (fullValidValueAmt / validValueAmt > 2.5)) {
+=======
+                        if (validValueAmt !== fullValidValueAmt) {
+>>>>>>> 262eb9acfcb39a05bd5ed6c3e171b036cc7dabd0
                             firstMergeCell[JV.PROP_CONTROL].VerticalForExcel = 'center';
                         }
                         firstMergeCell = sameColCells[i].cell;

+ 26 - 4
modules/reports/rpt_component/jpc_rte.js

@@ -63,8 +63,8 @@ let JE = {
             dataObj[field.DataNodeName][field.DataSeq][valIdx] = newValue;
         }
     },
-    getFieldValue: function (field, dataObj, valIdx, dftVal) {
-        let rst = dftVal;
+    getFieldValue: function (field, dataObj, valIdx, newVal) {
+        let rst = newVal;
         if (field.DataNodeName === "NA") {
             if (!field[JV.PROP_AD_HOC_DATA]) {
                 field[JV.PROP_AD_HOC_DATA] = [];
@@ -72,7 +72,7 @@ let JE = {
             if (field[JV.PROP_AD_HOC_DATA].length > valIdx) {
                 rst = field[JV.PROP_AD_HOC_DATA][valIdx];
             } else {
-                if (dftVal === null && field[JV.PROP_AD_HOC_DATA].length > 0) {
+                if (newVal === null && field[JV.PROP_AD_HOC_DATA].length > 0) {
                     rst = field[JV.PROP_AD_HOC_DATA][field[JV.PROP_AD_HOC_DATA].length - 1];
                 }
             }
@@ -86,12 +86,34 @@ let JE = {
             if (dataObj[field.DataNodeName][field.DataSeq].length > valIdx) {
                 rst = dataObj[field.DataNodeName][field.DataSeq][valIdx];
             } else {
-                if (dftVal === null && dataObj[field.DataNodeName][field.DataSeq].length > 0) {
+                if (newVal === null && dataObj[field.DataNodeName][field.DataSeq].length > 0) {
                     rst = dataObj[field.DataNodeName][field.DataSeq][dataObj[field.DataNodeName][field.DataSeq].length - 1];
                 }
             }
         }
         return rst;
+    },
+    removeFieldValue: function (field, dataObj, valIdx) {
+        if (field.DataNodeName === "NA") {
+            if (field[JV.PROP_AD_HOC_DATA].length > valIdx && valIdx >= 0) {
+                field[JV.PROP_AD_HOC_DATA].splice(valIdx, 1);
+            }
+        } else {
+            if (dataObj[field.DataNodeName][field.DataSeq].length > valIdx && valIdx >= 0) {
+                dataObj[field.DataNodeName][field.DataSeq].splice(valIdx, 1);
+            }
+        }
+    },
+    insertFieldValue: function (field, dataObj, valIdx, newValue) {
+        if (field.DataNodeName === "NA") {
+            if (field[JV.PROP_AD_HOC_DATA].length > valIdx && valIdx >= 0) {
+                field[JV.PROP_AD_HOC_DATA].splice(valIdx, 0, newValue);
+            }
+        } else {
+            if (dataObj[field.DataNodeName][field.DataSeq].length > valIdx && valIdx >= 0) {
+                dataObj[field.DataNodeName][field.DataSeq].splice(valIdx, 0, newValue);
+            }
+        }
     }
 };
 

二进制
modules/reports/util/pdf_base_files/Smart-italic.ttf


二进制
modules/reports/util/pdf_base_files/Smart.ttf


二进制
modules/reports/util/pdf_base_files/hwxsb.ttf


+ 206 - 21
modules/reports/util/rpt_construct_data_util.js

@@ -13,6 +13,9 @@ let treeUtil = require('../../../public/treeUtil');
 let projectConst = consts.projectConst;
 let projectConstList = consts.projectConstList;
 const gljUtil = require('../../../public/gljUtil');
+const gljType = require('../../common/const/glj_type_const');
+// const pm_facade = require('../../pm/facade/pm_facade');
+const GLJID_PRE = `gljId_`, COMPONENT_GLJID_PRE = `componetGljId_`;
 
 
 const GLJ_TYPE = {
@@ -20,8 +23,14 @@ const GLJ_TYPE = {
     Material: 2,
     Machine: 3,
     Main_Material: 4,
-    Equipment: 5
+    Equipment: 5,
+
+    OTHER_MATERIAL: 207,
+    MACHINE_LABOUR: 303,
+    INSTRUMENT: 304,
+    FUEL_POWER_FEE: 305
 };
+const oprMachines = [306, 307, 308, 309, 310, 311, 312];
 
 class Rpt_Common{
     initialize(rpt_tpl, currentDataObj) {
@@ -124,7 +133,7 @@ class Rpt_Data_Extractor {
     };
 
     //-- 根据报表模板映射指标(非离散指标)的定义,罗列出所有需要用到的data对象key,作为数据请求的过滤依据
-    getDataRequestFilter() {
+    getDataRequestFilter(summaryRst) {
         let rst = [];
         let tpl = this.rptTpl;
         let pri_func_chk_filter = function (field) {
@@ -157,6 +166,17 @@ class Rpt_Data_Extractor {
                     }
                 }
             }
+            if (summaryRst instanceof Array) {
+                for (let key of consts.summaryConstList) {
+                    if (summaryRst.indexOf(key) < 0) {
+                        if (field[JV.PROP_FIELD_EXP_MAP]) {
+                            if (field[JV.PROP_FIELD_EXP_MAP].indexOf('.' + key + '.') >= 0) {
+                                summaryRst.push(key);
+                            }
+                        }
+                    }
+                }
+            }
         };
         let pri_setup_filter = function (FIELD_LIST_KEY) {
             if (tpl[JV.NODE_FIELD_MAP][FIELD_LIST_KEY]) {
@@ -212,19 +232,49 @@ class Rpt_Data_Extractor {
         let tpl = this.rptTpl;
         this.COMMON.initialize(tpl, rawDataObj);
         $PROJECT.COMMON = this.COMMON;
-        $PROJECT.MAIN["myOwnRawDataObj"] = rawDataObj.prj._doc;
-        $PROJECT.MAIN.getProperty = ext_mainGetPropety;
-        $PROJECT.MAIN.getFee = ext_mainGetFee;
-        for (let item of rawDataObj.prjData) {
-            setupFunc($PROJECT.DETAIL, item.moduleName, item);
-        }
-        let projectGLJDatas = getModuleDataByKey(rawDataObj.prjData, "projectGLJ");
-        let rationGLJDatas = getModuleDataByKey(rawDataObj.prjData, "ration_glj");
-        let rationDatas = getModuleDataByKey(rawDataObj.prjData, "ration");
-        let billsDatas = getModuleDataByKey(rawDataObj.prjData, "bills");
-        let decimal = rawDataObj.prj.property.decimal.glj.quantity;
-        if (projectGLJDatas && rationGLJDatas && rationDatas && billsDatas) {
-            gljUtil.calcProjectGLJQuantity(projectGLJDatas.data, rationGLJDatas.data, rationDatas.data, billsDatas.data, decimal);
+        if (rawDataObj.hasOwnProperty(`prj`)) {
+            setupMainFunc($PROJECT, `MAIN`, rawDataObj.prj._doc);
+            // $PROJECT.MAIN["myOwnRawDataObj"] = rawDataObj.prj._doc;
+            // $PROJECT.MAIN.getProperty = ext_mainGetPropety;
+            // $PROJECT.MAIN.getFee = ext_mainGetFee;
+            for (let item of rawDataObj.prjData) {
+                setupFunc($PROJECT.DETAIL, item.moduleName, item);
+            }
+            let projectGLJDatas = getModuleDataByKey(rawDataObj.prjData, "projectGLJ");
+            if (projectGLJDatas) {
+                //增加一个辅助对象,方便通过gljid key直接获取数据,省去循环时间
+                let gljAssitantObj = {"gljIds": {}, "componentGljIds": {}};
+                projectGLJDatas.data.gljAssistant = gljAssitantObj;
+                for (let gljItem of projectGLJDatas.data.gljList) {
+                    gljAssitantObj.gljIds[GLJID_PRE + gljItem.id] = gljItem;
+                    if (!(gljItem.ratio_data) || gljItem.ratio_data.length === 0) {
+                        gljAssitantObj.componentGljIds[COMPONENT_GLJID_PRE + gljItem.glj_id] = gljItem;
+                    }
+                }
+            }
+            let rationGLJDatas = getModuleDataByKey(rawDataObj.prjData, "ration_glj");
+            let rationDatas = getModuleDataByKey(rawDataObj.prjData, "ration");
+            let billsDatas = getModuleDataByKey(rawDataObj.prjData, "bills");
+            let decimal = rawDataObj.prj.property.decimal.glj.quantity;
+            if (projectGLJDatas && rationGLJDatas && rationDatas && billsDatas) {
+                gljUtil.calcProjectGLJQuantity(projectGLJDatas.data, rationGLJDatas.data, rationDatas.data, billsDatas.data, decimal);
+            }
+        }
+        //还有汇总的...
+        if (rawDataObj.hasOwnProperty(`Construct`) || rawDataObj.hasOwnProperty(`ConstructDetail`) || rawDataObj.hasOwnProperty(`Segment`) || rawDataObj.hasOwnProperty(`SegmentDetail`)) {
+            $PROJECT.SUMMARY = {};
+            if (rawDataObj.Construct) {
+                setupMainFunc($PROJECT.SUMMARY, `Construct`, rawDataObj.Construct);
+            }
+            if (rawDataObj.ConstructDetail) {
+                setupFunc($PROJECT.SUMMARY, `ConstructDetail`, {"data": rawDataObj.ConstructDetail});
+            }
+            if (rawDataObj.Segment) {
+                setupMainFunc($PROJECT.SUMMARY, `Segment`, rawDataObj.Construct);
+            }
+            if (rawDataObj.SegmentDetail) {
+                setupFunc($PROJECT.SUMMARY, `SegmentDetail`, {"data": rawDataObj.SegmentDetail});
+            }
         }
 
         if (tpl[JV.NODE_MAP_DATA_HANDLE_INFO]) {
@@ -239,7 +289,7 @@ class Rpt_Data_Extractor {
                         break;
                     case JV.PROP_HANDLE_TYPE_SUM:
                         // fsUtil.writeObjToFile(rawDataObj, "D:/GitHome/ConstructionCost/tmp/insertedRawDataData10.jsp");
-                        summaryData(srcData, preHandle, rawDataObj.prjData);
+                        summaryData(srcData, preHandle, rawDataObj.prjData, rawDataObj.prj);
                         // fsUtil.writeObjToFile(rawDataObj, "D:/GitHome/ConstructionCost/tmp/insertedRawDataData11.jsp");
                         break;
                     case JV.PROP_HANDLE_TYPE_ADD_DUMMY:
@@ -249,8 +299,13 @@ class Rpt_Data_Extractor {
                         adjustData(srcData, preHandle);
                         break;
                     case JV.PROP_HANDLE_TYPE_BILLS_DATA_MOVE:
+                        //把显示在清单中的量材转移到工料机去
                         moveRationData(srcData, rawDataObj);
                         break;
+                    case JV.PROP_HANDLE_TYPE_COMPONENT_REPLACEMENT:
+                        //重庆2018 09-x表专用,机械数据用 组成物替换掉 ^_^!
+                        componentReplacement(rawDataObj);
+                        break;
                     default:
                         break;
                 }
@@ -316,9 +371,24 @@ function getModuleDataByKey(prjData, key) {
     return rst;
 }
 
-function summaryData(sourceData, handleCfg, prjData){
+function summaryData(sourceData, handleCfg, prjData, prjMain){
     let rstArr = [], tempRstArr = [];
     let curParentPrjData = {};
+    let precision = 6;
+    if (handleCfg[JV.PROP_HANDLE_TYPE_PRECISION]) {
+        if (isNaN(parseInt(handleCfg[JV.PROP_HANDLE_TYPE_PRECISION]))) {
+            let properties = handleCfg[JV.PROP_HANDLE_TYPE_PRECISION].split('.');
+            let currentProperty = prjMain[properties[0]];
+            for (let idx = 1; idx < properties.length; idx++) {
+                currentProperty = currentProperty[properties[idx]];
+            }
+            if (!isNaN(parseInt(currentProperty))) {
+                precision = parseInt(currentProperty);
+            }
+        } else {
+            precision = parseInt(handleCfg[JV.PROP_HANDLE_TYPE_PRECISION]);
+        }
+    }
     for (let item of getActDataArr(sourceData)) {
         if (item._doc) {
             tempRstArr.push(item._doc);
@@ -344,10 +414,10 @@ function summaryData(sourceData, handleCfg, prjData){
                                     dtlItem[sumKey] = parseFloat(dtlItem[sumKey]) - parseFloat(data[calcAheadObj["calc_property"]]);
                                     break;
                                 case "*":
-                                    dtlItem[sumKey] = dtlItem[sumKey] * parseFloat(data[calcAheadObj["calc_property"]]).toFixed(4);
+                                    dtlItem[sumKey] = dtlItem[sumKey] * parseFloat(data[calcAheadObj["calc_property"]]);
                                     break;
                                 case "/":
-                                    dtlItem[sumKey] = dtlItem[sumKey] / parseFloat(data[calcAheadObj["calc_property"]]).toFixed(4);
+                                    dtlItem[sumKey] = dtlItem[sumKey] / parseFloat(data[calcAheadObj["calc_property"]]);
                                     break;
                                 default:
                                     break;
@@ -390,7 +460,7 @@ function summaryData(sourceData, handleCfg, prjData){
             for (let sumKey of handleCfg[JV.PROP_SUM_SUM_KEYS]) {
                 if (dtl[sumKey]) {
                     // sumObj[grpKey][sumKey] += dtl[sumKey];
-                    sumObj[grpKey][sumKey] = parseFloat(sumObj[grpKey][sumKey]) + parseFloat(dtl[sumKey]);
+                    sumObj[grpKey][sumKey] = parseFloat(parseFloat(sumObj[grpKey][sumKey]).toFixed(precision)) + parseFloat(parseFloat(dtl[sumKey]).toFixed(precision));
                 }
             }
         }
@@ -637,6 +707,65 @@ function adjustData(sourceData, adjustCfg) {
     replaceActDataArr(sourceData, rstArr);
 }
 
+function componentReplacement(rawDataObj) {
+    let rationGljData = getModuleDataByKey(rawDataObj.prjData, projectConst.RATION_GLJ),
+        prjGljData = getModuleDataByKey(rawDataObj.prjData, projectConst.PROJECTGLJ),
+        gljAssistant = prjGljData.data.gljAssistant;
+    let connectStrArr = [], dataArr = getActDataArr(rationGljData), rvRGljIds = [], replaceGljItems = [];
+    for (let idx = 0; idx < dataArr.length; idx++) {
+        let rationGljItem = dataArr[idx];
+        if (rationGljItem.type === 301 || rationGljItem.type === 302) {
+            connectStrArr[0] = rationGljItem.code;
+            connectStrArr[1] = rationGljItem.name;
+            connectStrArr[2] = rationGljItem.specs;
+            connectStrArr[3] = rationGljItem.unit;
+            connectStrArr[4] = rationGljItem.type;
+            let mapData = prjGljData.data.mixRatioMap[connectStrArr.join('|-|')];
+            if (mapData) {
+                rvRGljIds.push(idx);
+                let rgItems = [];
+                replaceGljItems.push(rgItems);
+                for (let mapItem of mapData) {
+                    let copyItem = {};
+                    rgItems.push(copyItem);
+                    copyItem.ID = `Replace_` + rationGljItem.ID;
+                    copyItem.projectID = rationGljItem.projectID;
+                    copyItem.GLJID = rationGljItem.GLJID;
+                    copyItem.rationID = rationGljItem.rationID;
+                    if (gljAssistant.componentGljIds[COMPONENT_GLJID_PRE + mapItem.glj_id]) {
+                        copyItem.projectGLJID = gljAssistant.componentGljIds[COMPONENT_GLJID_PRE + mapItem.glj_id].id; //关键key
+                    } else {
+                        copyItem.projectGLJID = mapItem.glj_id; //不应该走到这一步,否则就是错误!!!
+                    }
+                    copyItem.rationItemQuantity = rationGljItem.rationItemQuantity;
+                    // copyItem.quantity = rationGljItem.quantity;
+                    copyItem.quantity = rationGljItem.quantity * mapItem.consumption;
+                    copyItem.name = mapItem.name;
+                    copyItem.code = mapItem.code;
+                    copyItem.original_code = mapItem.code;
+                    copyItem.unit = mapItem.unit;
+                    copyItem.specs = mapItem.specs;
+                    copyItem.model = gljAssistant.gljIds['gljId_'+rationGljItem.projectGLJID].model;
+                    copyItem.shortName = mapItem.shortName;
+                    copyItem.billsItemID = rationGljItem.billsItemID;
+                    copyItem.type = mapItem.type; //关键key
+                }
+            }
+        }
+    }
+    if (rvRGljIds.length > 0) {
+        for (let lIdx = rvRGljIds.length - 1; lIdx >= 0; lIdx--) {
+            dataArr.splice(rvRGljIds[lIdx],1);
+        }
+        for (let componentItems of replaceGljItems) {
+            for (let compItem of componentItems) {
+                dataArr.push(compItem);
+                //gljAssistant[COMPONENT_GLJID_PRE + mapItem.glj_id]
+            }
+        }
+    }
+}
+
 function moveRationData(rationData, rawDataObj) {
     if (rawDataObj) {
         // let rationData = getModuleDataByKey(rawDataObj.prjData, projectConst.RATION);
@@ -756,6 +885,54 @@ function addDummyData(sourceData, addCfg) {
     replaceActDataArr(sourceData, rstArr);
 }
 
+function getGLJBizType2018(orgType, orgCode, orgName) {
+    let rst = orgType;
+    if (orgType === GLJ_TYPE.Labour) {
+        rst = 11;
+        if (orgCode === "000000") rst = 10;
+    } else if (orgType === GLJ_TYPE.Material || (orgType >= 200 && orgType < 300)) {
+        //材料
+        if (orgCode === "000000") {
+            rst = 200; //2.材料
+        } else if (orgCode === "000000_1") {
+            rst = 230; //(1) 计价材料
+        } else if (orgCode === "000000_2") {
+            rst = 240; //(2) 其他材料费
+        } else {
+            if (orgType === GLJ_TYPE.OTHER_MATERIAL) {
+                //其他材料
+                rst = 245;
+            } else {
+                //计价材料
+                rst = 235;
+            }
+        }
+    } else if (orgType === GLJ_TYPE.Machine || (orgType >= 300 && orgType < 400)) {
+        //机械
+        if (orgCode === "000000") {
+            rst = 300; //3.机械
+        } else if (orgCode === "000000_1") {
+            rst = 330; //(1) 机上人工
+        } else if (orgCode === "000000_2") {
+            rst = 340; //(2) 燃油动力费
+        } else if (orgCode === "000000_3") {
+            rst = 350; //(2) 施工机具摊销费
+        } else {
+            if (orgType === GLJ_TYPE.MACHINE_LABOUR) {
+                //机上人工
+                rst = 335;
+            } else if (orgType === GLJ_TYPE.FUEL_POWER_FEE) {
+                //燃油动力费
+                rst = 345;
+            } else if (oprMachines.indexOf(orgType) >= 0) {
+                //施工机具摊销费
+                rst = 355 + oprMachines.indexOf(orgType);
+            }
+        }
+    }
+    return rst;
+}
+
 function getGLJBizType(orgType, orgCode, orgName) {
     let rst = orgType;
     if (orgName.indexOf("其他材料费") >= 0) {
@@ -896,6 +1073,7 @@ function sortData(sourceData, sortCfg, prjData) {
                 newTopArr = rst;
             }
             let destArr = [];
+            // fsUtil.writeObjToFile(newTopArr, "D:/GitHome/ConstructionCost/tmp/sortedAndFlattedRstBefore.jsp");
             treeUtil.getFlatArray(newTopArr, destArr);
             replaceActDataArr(sourceData, destArr);
             // fsUtil.writeObjToFile(sourceData.data, "D:/GitHome/ConstructionCost/tmp/sortedAndFlattedRst.jsp");
@@ -958,6 +1136,13 @@ function sortData(sourceData, sortCfg, prjData) {
     return rst;
 }
 
+function setupMainFunc(obj, prop, ownRawObj) {
+    obj[prop] = {};
+    obj[prop]["myOwnRawDataObj"] = ownRawObj;
+    obj[prop].getProperty = ext_mainGetPropety;
+    obj[prop].getFee = ext_mainGetFee;
+}
+
 function setupFunc(obj, prop, ownRawObj) {
     obj[prop] = {};
     obj[prop]["myOwnRawDataObj"] = ownRawObj;
@@ -1196,7 +1381,7 @@ function pri_getFee(dItem, feeKey, dtlFeeKey) {
         }
     } else if (dItem[feeKey]) {
         hasValue = true;
-        rst = dItem[feeKey];
+        rst = parseFloat(dItem[feeKey]);
     } else {
         hasValue = true;
         rst = 0;

+ 31 - 5
modules/reports/util/rpt_excel_util.js

@@ -9,6 +9,7 @@ let jpcCmnHelper = require('../rpt_component/helper/jpc_helper_common');
 let DPI = jpcCmnHelper.getScreenDPI()[0];
 let fsUtil = require('../../../public/fsUtil');
 const dftHeadXml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
+const uuidV1 = require('uuid/v1');
 
 function writeContentTypes(sheets, isSinglePage) {
     let rst = [];
@@ -157,6 +158,9 @@ function writeStyles(stylesObj){
         if (strUtil.convertStrToBoolean(font[JV.FONT_PROPS[3]])) {
             rst.push('<b/>');
         }
+        if (strUtil.convertStrToBoolean(font[JV.FONT_PROPS[4]])) {
+            rst.push('<i/>');
+        }
         if (strUtil.convertStrToBoolean(font[JV.FONT_PROPS[5]])) {
             rst.push('<u/>');
         }
@@ -204,6 +208,9 @@ function writeStyles(stylesObj){
         let textRotation = 0;
         let newHorizontal = excelStyle[JV.CONTROL_PROPS[2]];
         let newVertical = excelStyle[JV.CONTROL_PROPS[3]];
+        if (excelStyle[JV.CONTROL_PROPS[5]]) {
+            newVertical = excelStyle[JV.CONTROL_PROPS[5]];
+        }
         if (parseInt(excelStyle.fontAngle) !== 0) {
             let tmpH = newHorizontal, tmpV = newVertical;
             if (excelStyle.fontAngle > 0) {
@@ -219,6 +226,8 @@ function writeStyles(stylesObj){
                     tmpH = 'right';
                 } else if (newVertical === "bottom") {
                     tmpH = 'left';
+                } else if (newVertical === "justify") {
+                    tmpH = 'justify';
                 } else {
                     tmpH = 'center';
                 }
@@ -269,9 +278,12 @@ function writeSharedString(sharedStrList){
     if (sharedStrList && sharedStrList.length > 0) {
         rst.push(dftHeadXml + '\r\n');
         rst.push('<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="' + sharedStrList.length + '" uniqueCount="' + sharedStrList.length + '">');
+        let regExp = new RegExp("<", "gm");
         for (let i = 0; i < sharedStrList.length; i++) {
             //rst.push('<si><t>' + sharedStrList[i] + '</t></si>');
             if (typeof sharedStrList[i] === 'string') {
+                //转换特殊字符,如 < , 则需要转义一下
+                sharedStrList[i] = sharedStrList[i].replace(regExp, "&lt;");
                 if (sharedStrList[i].indexOf('|') >= 0) {
                     //rst.push('<si><t>' + sharedStrList[i].split('|').join('\r\n') + '</t></si>');
                     rst.push('<si><t>' + sharedStrList[i].split('|').join('\n') + '</t></si>');
@@ -342,7 +354,8 @@ function writeSheets(pageData, paperSize, sharedStrList, stylesObj, isSinglePage
     return rst;
 }
 function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj, appointedMergeBand){
-    let rst = [], xPos = [], yPos = [], yMultiPos = [], headerStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+    let rst = [], xPos = [], yPos = [], yMultiPos = [], currentMergeBorder = null,
+        headerStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
     let currentPageMergePos = null; //在 JV.PAGING_OPTION_INFINITY 场合应用
     let private_pre_analyze_pos = function(){
         let cell, pos;
@@ -443,7 +456,8 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj, ap
             if (sheetFont) {
                 // if (font[JV.FONT_PROPS[0]] === sheetFont[JV.FONT_PROPS[0]] && font.size === Math.round(sheetFont[JV.FONT_PROPS[1]] * 3 / 4)
                 if (font[JV.FONT_PROPS[0]] === sheetFont[JV.FONT_PROPS[0]] && font.size === Math.floor(sheetFont[JV.FONT_PROPS[1]] * 3 / 4)
-                    && font[JV.FONT_PROPS[3]] === sheetFont[JV.FONT_PROPS[3]] && font[JV.FONT_PROPS[5]] === sheetFont[JV.FONT_PROPS[5]]) {
+                    && font[JV.FONT_PROPS[3]] === sheetFont[JV.FONT_PROPS[3]] && font[JV.FONT_PROPS[4]] === sheetFont[JV.FONT_PROPS[4]]
+                    && font[JV.FONT_PROPS[5]] === sheetFont[JV.FONT_PROPS[5]] ) {
                     hasFont = true;
                     rst = i;
                     break;
@@ -460,6 +474,7 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj, ap
             font.charset = 134;
             font.colorIdx = "8";
             font[JV.FONT_PROPS[3]] = sheetFont[JV.FONT_PROPS[3]]; //font bold
+            font[JV.FONT_PROPS[4]] = sheetFont[JV.FONT_PROPS[4]]; //font italic
             font[JV.FONT_PROPS[5]] = sheetFont[JV.FONT_PROPS[5]]; //font underline
             stylesObj.fonts.push(font);
             rst = stylesObj.fonts.length - 1;
@@ -502,7 +517,8 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj, ap
     };
     let private_chkAndGetMergeLine = function(cell, sheetBorder, borderStr, needFurtherChk) {
         let rst = 0,
-            mergeBorder = (sheetData)?sheetData[JV.PROP_PAGE_MERGE_BORDER]:pageData[JV.BAND_PROP_MERGE_BAND],
+            // mergeBorder = (sheetData)?sheetData[JV.PROP_PAGE_MERGE_BORDER]:pageData[JV.BAND_PROP_MERGE_BAND],
+            mergeBorder = currentMergeBorder,
             mergeBand = pageData[JV.BAND_PROP_MERGE_BAND]
         ;
         if (appointedMergeBand !== null) {
@@ -752,6 +768,7 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj, ap
         if (sheetData) {
             //current sheet data
             currentPageMergePos = sheetData[JV.PAGE_SPECIAL_MERGE_POS];
+            currentMergeBorder = sheetData[JV.PROP_PAGE_MERGE_BORDER];
             self_setDataEx(sheetData, yPos, 0);
         } else {
             //total data in one sheet
@@ -759,6 +776,7 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj, ap
             for (let i = 0; i < pageData.items.length; i++) {
                 let shtItemData = pageData.items[i];
                 currentPageMergePos = shtItemData[JV.PAGE_SPECIAL_MERGE_POS];
+                currentMergeBorder = shtItemData[JV.PROP_PAGE_MERGE_BORDER];
                 let tmpPos = yMultiPos[i];
                 cellIdx = 0;
                 self_setDataEx(shtItemData, tmpPos, cnt);
@@ -818,7 +836,7 @@ function mergeProperties(orgObj, newObj) {
 module.exports = {
     exportExcel: function (pageData, paperSize, fName, options, custSheetNames, custSheetMergeBands, callback) {
         let rptOptions = ({singlePage: false, fileName: 'report'});
-        if (options === 'true') {
+        if (options === 'true' || options === true) {
             rptOptions.singlePage = true;
         }
         let isSinglePage = rptOptions.singlePage;
@@ -891,7 +909,9 @@ module.exports = {
         zip_xl.file(file, data.join(''), {compression: 'DEFLATE'});
 
         if (fName) {
-            let newName = '' + (new Date()).valueOf();
+            // let newName = '' + (new Date()).valueOf();
+            let newName = uuidV1();
+
             zip.generateNodeStream({type:'nodebuffer',streamFiles:true})
                 .pipe(fs.createWriteStream(__dirname.slice(0, __dirname.length - 21) + '/tmp/' + newName + '.xlsx'))
                 .on('finish', function () {
@@ -931,6 +951,7 @@ module.exports = {
                 let offsetY = 0;
                 let mergeBand = {};
                 custMergeBands.push(pageDataArray[i][JV.BAND_PROP_MERGE_BAND]);
+                //备注:不同的报表有可能有不同的边框,如封面表就是无边框的
                 mergeBand[JV.PROP_LEFT] = [];
                 mergeBand[JV.PROP_RIGHT] = [];
                 mergeBand[JV.PROP_TOP] = [];
@@ -947,6 +968,11 @@ module.exports = {
                         mergeBand[JV.PROP_TOP].push(pos);
                         pos = pageDataArray[i].items[j][JV.PAGE_SPECIAL_MERGE_POS][JV.PROP_BOTTOM][0] + offsetY;
                         mergeBand[JV.PROP_BOTTOM].push(pos);
+                    } else if (pageDataArray[i].items[j][JV.PROP_PAGE_MERGE_BORDER]) {
+                        let pos = pageDataArray[i].items[j][JV.PROP_PAGE_MERGE_BORDER][JV.PROP_TOP] + offsetY;
+                        mergeBand[JV.PROP_TOP].push(pos);
+                        pos = pageDataArray[i].items[j][JV.PROP_PAGE_MERGE_BORDER][JV.PROP_BOTTOM] + offsetY;
+                        mergeBand[JV.PROP_BOTTOM].push(pos);
                     } else {
                         mergeBand[JV.PROP_TOP].push(pageDataArray[i][JV.BAND_PROP_MERGE_BAND][JV.PROP_TOP] + offsetY);
                         mergeBand[JV.PROP_BOTTOM].push(pageDataArray[i][JV.BAND_PROP_MERGE_BAND][JV.PROP_BOTTOM] + offsetY);

+ 28 - 0
modules/reports/util/rpt_font_util.js

@@ -0,0 +1,28 @@
+/**
+ * Created by Tony on 2018/8/2.
+ */
+
+let fontMapObj = {
+    "宋体": "Smart"
+    ,"楷体": "simkai"
+    ,"黑体": "simhei"
+};
+//下划线在option中支持
+//另注意:PDFkit设置字体的时候会检测是否同源,也就是说,如果是同一种字体转换不同的特性(如粗体、斜体),那么在设置的时候会无效
+//      比如前一种是普通的字体,后来想设置这种字体的斜体,实际上这种设置会失效
+
+module.exports = {
+    getActualFont: getActualFont
+}
+
+function getActualFont(mapName, isBold, isItalic) {
+    let rst = ["Smart"];
+    if (fontMapObj[mapName]) rst[0] = fontMapObj[mapName];
+    if (isBold) {
+        rst.push("_bold");
+    }
+    if (isItalic) {
+        rst.push("_italic");
+    }
+    return rst.join("");
+}

+ 105 - 41
modules/reports/util/rpt_pdf_util.js

@@ -10,22 +10,22 @@ let fs = require('fs');
 let jpcCmnHelper = require('../rpt_component/helper/jpc_helper_common');
 let DPI = jpcCmnHelper.getScreenDPI()[0];
 let JV = require('../rpt_component/jpc_value_define');
+const uuidV1 = require('uuid/v1');
 
-// hwxsb.ttf: 华文中宋
-// Smart.ttf: 宋体(常规)
-// Smart-italic.ttf: 宋体(斜体)
-//目前不支持下划线
+let fontUtil = require('./rpt_font_util');
 
-module.exports ={
+// 目前不支持下划线
+
+module.exports = {
     export_pdf_file:export_pdf_file
 }
 
-
 function export_pdf_file (pageData, paperSize, fName, callback) {
     let offsetX= 10;
     let offsetY=10;
     let doc = new pdf({autoFirstPage: false});
-    let newName = '' + (new Date()).valueOf();
+    // let newName = '' + (new Date()).valueOf();
+    let newName = uuidV1();
     let stream = doc.pipe(fs.createWriteStream(__dirname.slice(0, __dirname.length - 21) + '/tmp/'+newName+'.pdf'));
     let pageObj = pageData;
     // doc.rect(5,5,1190,890).lineWidth(1).strokeColor('black').stroke();//边框
@@ -144,47 +144,28 @@ function export_pdf_file (pageData, paperSize, fName, callback) {
     function private_drawText(val, area, font, control) {
         let dftFontHeight = 12;
         let output = [];
+        let fontFile = __dirname + '/pdf_base_files/simkai.ttf';
         if (font) {
             dftFontHeight = 1 * font[JV.FONT_PROPS[1]];
-            let dftFontBold = font[JV.FONT_PROPS[3]];
-            let dftFontItalic = font[JV.FONT_PROPS[4]];
-            if (dftFontBold && dftFontBold === 'T') {
-                doc.font(__dirname+'/pdf_base_files/hwxsb.ttf');
-            }else if(dftFontItalic && dftFontItalic === 'T'){
-                doc.font(__dirname+'/pdf_base_files/Smart-italic.ttf');
-            }else {
-                doc.font(__dirname+'/pdf_base_files/Smart.ttf');
-            }
+            fontFile = __dirname + '/pdf_base_files/' + fontUtil.getActualFont(font[JV.FONT_PROPS[0]], (font[JV.FONT_PROPS[3]] === 'T'), (font[JV.FONT_PROPS[4]] === 'T')) + '.ttf';
             doc.fontSize(dftFontHeight);
         }
+        doc.font(fontFile);
         let options={};
-        if (control) {
-            private_setupAreaH(area, control.Horizon, font.FontAngle, dftFontHeight, output,options);
-            private_setupAreaV(area, control.Vertical, font.FontAngle, dftFontHeight, output);
-        } else {
-            private_setupAreaH(area, "left", font.FontAngle, dftFontHeight, output,options);
-            private_setupAreaV(area, "bottom", font.FontAngle, dftFontHeight, output);
-        }
+        let inner_setupControl = function (inArea, inFontHeight, inOutput) {
+            if (control) {
+                private_setupAreaH(inArea, control.Horizon, font.FontAngle, inFontHeight, inOutput, options);
+                private_setupAreaV(inArea, control.Vertical, font.FontAngle, inFontHeight, inOutput);
+            } else {
+                private_setupAreaH(inArea, "left", parseInt(font.FontAngle), inFontHeight, inOutput, options);
+                private_setupAreaV(inArea, "bottom", parseInt(font.FontAngle), inFontHeight, inOutput);
+            }
+        };
+        inner_setupControl(area, dftFontHeight, output);
         let w = area[JV.IDX_RIGHT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT] - area[JV.IDX_LEFT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT];
         if (parseInt(font.FontAngle) !== 0) {
             w = area[JV.IDX_BOTTOM] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM] - area[JV.IDX_TOP] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP];
         }
-        doc.save();
-        //doc.translate(output[0], output[1]);
-        if (w >= doc.widthOfString(val)) {
-            options.width=w;
-            options.height=dftFontHeight;
-        } else {
-            while (true) {
-                dftFontHeight--;
-                doc.fontSize(dftFontHeight);
-                if (w >= doc.widthOfString(val) || dftFontHeight < 6) {
-                    options.width=w;
-                    options.height=dftFontHeight;
-                    break;
-                }
-            }
-        }
 
         function private_drawUnderline() {
             //A. 暂不支持角度; B. PDF输出时,坐标没有translate
@@ -237,8 +218,63 @@ function export_pdf_file (pageData, paperSize, fName, callback) {
             }
             doc.rotate(font.FontAngle,rotateOptions);
         }
-        doc.text(val,output[0], output[1],options);
-        doc.restore();
+        if (w >= doc.widthOfString(val) || (control && control.Shrink !== 'T')) {
+            options.width = w;
+            options.height = dftFontHeight;
+            doc.text(val,output[0], output[1], options);
+            doc.font(__dirname + '/pdf_base_files/simhei_bold_italic.ttf');
+        } else {
+            while (true) {
+                //*/
+                let lines = Math.floor((area[JV.IDX_BOTTOM] - area[JV.IDX_TOP]) / (dftFontHeight + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM] + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP] + 4));
+                lines = (lines === 0)?1:lines;
+                let actLines = private_splitString(val, w, doc);
+                if (actLines.length > lines && dftFontHeight >= 6) {
+                    dftFontHeight--;
+                    doc.fontSize(dftFontHeight);
+                    options.width = w;
+                    options.height = dftFontHeight;
+                    doc.text(val,output[0], output[1], options);
+                } else {
+                    let aH = dftFontHeight + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM] + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP] + 4;
+                    if ((aH * actLines.length) < (area[JV.IDX_BOTTOM] - area[JV.IDX_TOP]) && (control && control.Vertical !== 'top')) {
+                        if (control.Vertical === 'bottom') {
+                            area[JV.IDX_TOP] = area[JV.IDX_BOTTOM] - (aH * actLines.length);
+                        } else {
+                            area[JV.IDX_TOP] = (area[JV.IDX_TOP] + area[JV.IDX_BOTTOM]) / 2 - (aH * actLines.length) / 2
+                            area[JV.IDX_BOTTOM] = area[JV.IDX_TOP] + (aH * actLines.length);
+                        }
+                    }
+                    let newArea = [], baseTop = area[JV.IDX_TOP];
+                    for (let ai = 0; ai < area.length; ai++) {
+                        newArea[ai] = area[ai];
+                    }
+                    options.width = w;
+                    options.height = dftFontHeight;
+                    for (let lIdx = 0; lIdx < actLines.length; lIdx++) {
+                        newArea[JV.IDX_TOP] = Math.round(aH * lIdx + baseTop);
+                        newArea[JV.IDX_BOTTOM] = Math.round(aH * (lIdx + 1) + baseTop);
+                        inner_setupControl(newArea, dftFontHeight, output);
+                        doc.text(actLines[lIdx], output[0], output[1], options);
+                    }
+                    break;
+                }
+                /*/
+                dftFontHeight--;
+                doc.fontSize(dftFontHeight);
+                if (w >= doc.widthOfString(val) || dftFontHeight < 6) {
+                    options.width = w;
+                    options.height = dftFontHeight;
+                    doc.text(val,output[0], output[1], options);
+                    doc.font(__dirname + '/pdf_base_files/simhei_bold_italic.ttf');
+                    break;
+                }
+                //*/
+            }
+        }
+        // doc.text(val,output[0], output[1], options);
+        doc.font(__dirname + '/pdf_base_files/simhei_bold_italic.ttf');
+        // doc.restore();
     }
 
     function private_setupAreaH(area, type, fontAngle, dftFontHeight, outputPoint,options) {
@@ -264,6 +300,34 @@ function export_pdf_file (pageData, paperSize, fName, callback) {
         }
     }
 
+    function private_splitString(strVal, areaWidth, doc) {
+        let rst = [];
+        if (strVal) {
+            let preSIdx = 0, txtWidth = 0;
+            let currentW = 0;
+            let chnW = doc.widthOfString('一'), otherW = doc.widthOfString('_');
+            for (let sIdx = 0; sIdx < strVal.length; sIdx++) {
+                currentW = (strVal.charCodeAt(sIdx) > 127)?chnW:otherW;
+                txtWidth += currentW;
+                if (txtWidth > areaWidth) {
+                    if (preSIdx < sIdx) {
+                        rst.push(strVal.substr(preSIdx, sIdx - preSIdx));
+                        preSIdx = sIdx;
+                    } else {
+                        rst.push(strVal.substr(preSIdx, 1));
+                        preSIdx = sIdx + 1;
+                    }
+                    txtWidth = currentW;
+                }
+                if (sIdx === strVal.length - 1) {
+                    rst.push(strVal.substr(preSIdx, strVal.length - preSIdx));
+                }
+            }
+        }
+        if (rst.length === 0) rst.push(''); //什么都没有,也得整个空串
+        return rst;
+    }
+
     function private_setupAreaRotateOption(area,w, type="top",dftFontHeight,outputPoint){
         let x = (area[JV.IDX_RIGHT] - area[JV.IDX_LEFT])/2+area[JV.IDX_LEFT];
         let y =(area[JV.IDX_BOTTOM] - area[JV.IDX_TOP])/2+ area[JV.IDX_TOP];

+ 106 - 9
modules/reports/util/rpt_svg_util.js

@@ -7,6 +7,7 @@ let JV = require('../rpt_component/jpc_value_define');
 let pdf = require('pdfkit');
 let jpcCmnHelper = require('../rpt_component/helper/jpc_helper_common');
 let SCREEN_DPI = jpcCmnHelper.getScreenDPI();
+let fontUtil = require('./rpt_font_util');
 
 module.exports = {
     exportSvgStr: function (pagesData, offsetX, offsetY) {
@@ -109,6 +110,9 @@ function buildText(destRst, cell, font, control, offsetX, offsetY, adjustY, pdf_
     let orgFontHeight = parseInt(font[JV.FONT_PROPS[JV.FONT_PROP_IDX_HEIGHT]]);
     let fontWeight = (font[JV.FONT_PROPS[JV.FONT_PROP_IDX_BOLD]] === 'T')?"bold":"normal";
     let fontStyle = (font[JV.FONT_PROPS[JV.FONT_PROP_IDX_ITALIC]] === 'T')?"italic":"normal";
+    let dftFontBold = font[JV.FONT_PROPS[3]];
+    let dftFontItalic = font[JV.FONT_PROPS[4]];
+    let fontFile = __dirname + '/pdf_base_files/' + fontUtil.getActualFont(font[JV.FONT_PROPS[0]], (dftFontBold === 'T'), (dftFontItalic === 'T')) + '.ttf';
     let left = parseInt(cell[JV.PROP_AREA][JV.PROP_LEFT]) + offsetX + 0.5,
         right = parseInt(cell[JV.PROP_AREA][JV.PROP_RIGHT]) + offsetX + 0.5,
         top = parseInt(cell[JV.PROP_AREA][JV.PROP_TOP]) + offsetY + adjustY,
@@ -139,18 +143,82 @@ function buildText(destRst, cell, font, control, offsetX, offsetY, adjustY, pdf_
             x = Math.round((left + right) / 2);
         }
     }
+    //*/
+    let height = cell[JV.PROP_AREA][JV.PROP_BOTTOM] - cell[JV.PROP_AREA][JV.PROP_TOP];
+    let area = [cell[JV.PROP_AREA][JV.PROP_LEFT] + offsetX, cell[JV.PROP_AREA][JV.PROP_TOP] + offsetY, cell[JV.PROP_AREA][JV.PROP_RIGHT] + offsetX, cell[JV.PROP_AREA][JV.PROP_BOTTOM] + offsetY];
+    let inner_draw_text = function (textValue) {
+        let dftFontHeight = orgFontHeight;
+        pdf_doc.font(fontFile);
+        pdf_doc.fontSize(dftFontHeight);
+        let actLines = private_splitString(textValue, (area[JV.PROP_RIGHT] - area[JV.PROP_LEFT]), pdf_doc);
+        function inner_build_text(innerTxt, innerArea) {
+            let innerDftFontHeight = (dftFontHeight * 3 / 4); //SVG的字体与canvas的字体大小的切换, 不用考虑取整
+            if (control) {
+                if (control[JV.CONTROL_PROPS[JV.CONTROL_PROP_IDX_VERTICAL]] === "top") {
+                    y = innerArea[JV.PROP_TOP] + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP];
+                } else if (control[JV.CONTROL_PROPS[JV.CONTROL_PROP_IDX_VERTICAL]] === "bottom") {
+                    y = innerArea[JV.PROP_BOTTOM] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM];
+                } else if (control[JV.CONTROL_PROPS[JV.CONTROL_PROP_IDX_VERTICAL]] === "center") {
+                    y = Math.round((innerArea[JV.PROP_TOP] + innerArea[JV.PROP_BOTTOM] + innerDftFontHeight) / 2 );
+                }
+            }
+            if (font[JV.PROP_NAME] === "宋体") {
+                y--;
+            }
+            destRst.push("<text style='fill:black;font-family:" + font[JV.PROP_NAME] +
+                ";font-weight:" + fontWeight +
+                ";font-style:" + fontStyle +
+                ";font-size:" + innerDftFontHeight + "pt' x='" +
+                x +"' y='" + y + "' text-anchor='" + text_anchor + "' xml:space='preserve'>" + innerTxt + "</text>");
+        }
+        if (actLines.length === 1 || (control && control.Shrink !== 'T')) {
+            inner_build_text(textValue, area);
+        } else {
+            while (true) {
+                if (dftFontHeight > 6) {
+                    dftFontHeight--;
+                    pdf_doc.fontSize(dftFontHeight);
+                    let lines = Math.floor((area[JV.IDX_BOTTOM] - area[JV.IDX_TOP]) / (dftFontHeight + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM] + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP] + 4));
+                    lines = (lines === 0)?1:lines;
+                    actLines = private_splitString(textValue, (area[JV.PROP_RIGHT] - area[JV.PROP_LEFT]), pdf_doc);
+                    if (lines >= actLines.length) {
+                        let aH = dftFontHeight + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM] + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP] + 4;
+                        if ((aH * actLines.length) < (area[JV.IDX_BOTTOM] - area[JV.IDX_TOP]) && (control && control.Vertical !== 'top')) {
+                            if (control.Vertical === 'bottom') {
+                                area[JV.IDX_TOP] = area[JV.IDX_BOTTOM] - (aH * actLines.length);
+                            } else {
+                                area[JV.IDX_TOP] = (area[JV.IDX_TOP] + area[JV.IDX_BOTTOM]) / 2 - (aH * actLines.length) / 2
+                                area[JV.IDX_BOTTOM] = area[JV.IDX_TOP] + (aH * actLines.length);
+                            }
+                        }
+                        let newArea = [], baseTop = area[JV.IDX_TOP];
+                        for (let ai = 0; ai < area.length; ai++) {
+                            newArea[ai] = area[ai];
+                        }
+                        for (let lIdx = 0; lIdx < actLines.length; lIdx++) {
+                            newArea[JV.IDX_TOP] = Math.round(aH * lIdx + baseTop);
+                            newArea[JV.IDX_BOTTOM] = Math.round(aH * (lIdx + 1) + baseTop);
+                            inner_build_text(actLines[lIdx], newArea);
+                        }
+                        break;
+                    }
+                } else {
+                    inner_build_text(textValue, area);
+                    break;
+                }
+            }
+        }
+    };
+    for (let vidx = 0; vidx < values.length; vidx++) {
+        area[JV.IDX_TOP] = cell[JV.PROP_AREA][JV.PROP_TOP] + vidx * (height / values.length);
+        area[JV.IDX_BOTTOM] = cell[JV.PROP_AREA][JV.PROP_TOP] + (vidx + 1) * (height / values.length);
+        inner_draw_text(values[vidx], area, font, control);
+    }
+    /*/
     for (let vidx = 0; vidx < values.length; vidx++) {
         //check whether need to adjust the font size
         let dftFontHeight = orgFontHeight;
-        let dftFontBold = font[JV.FONT_PROPS[3]];
-        let dftFontItalic = font[JV.FONT_PROPS[4]];
-        if (dftFontBold && dftFontBold === 'T') {
-            pdf_doc.font(__dirname+'/pdf_base_files/hwxsb.ttf');
-        }else if(dftFontItalic && dftFontItalic === 'T'){
-            pdf_doc.font(__dirname+'/pdf_base_files/Smart-italic.ttf');
-        }else {
-            pdf_doc.font(__dirname+'/pdf_base_files/Smart.ttf');
-        }
+        pdf_doc.font(fontFile);
         pdf_doc.fontSize(dftFontHeight);
         while ((right - left) <= pdf_doc.widthOfString(values[vidx])) {
             if (dftFontHeight > 6) {
@@ -179,6 +247,35 @@ function buildText(destRst, cell, font, control, offsetX, offsetY, adjustY, pdf_
             ";font-size:" + dftFontHeight + "pt' x='" +
             x +"' y='" + y + "' text-anchor='" + text_anchor + "' xml:space='preserve'>" + values[vidx] + "</text>");
     }
+    //*/
+}
+
+function private_splitString(strVal, areaWidth, doc) {
+    let rst = [];
+    if (strVal) {
+        let preSIdx = 0, txtWidth = 0;
+        let currentW = 0;
+        let chnW = doc.widthOfString('一'), otherW = doc.widthOfString('_');
+        for (let sIdx = 0; sIdx < strVal.length; sIdx++) {
+            currentW = (strVal.charCodeAt(sIdx) > 127)?chnW:otherW;
+            txtWidth += currentW;
+            if (txtWidth > areaWidth) {
+                if (preSIdx < sIdx) {
+                    rst.push(strVal.substr(preSIdx, sIdx - preSIdx));
+                    preSIdx = sIdx;
+                } else {
+                    rst.push(strVal.substr(preSIdx, 1));
+                    preSIdx = sIdx + 1;
+                }
+                txtWidth = currentW;
+            }
+            if (sIdx === strVal.length - 1) {
+                rst.push(strVal.substr(preSIdx, strVal.length - preSIdx));
+            }
+        }
+    }
+    if (rst.length === 0) rst.push(''); //什么都没有,也得整个空串
+    return rst;
 }
 
 function getPixelSize(pagesData) {

+ 53 - 0
modules/reports/util/rpt_tmp_file_sweep.js

@@ -0,0 +1,53 @@
+/**
+ * Created by Tony on 2018/7/27.
+ */
+
+let fs = require('fs');
+let cron = require('node-schedule');
+let remove_file_types = [".xlsx", ".pdf", ".jsp"];
+function chkIsRemoveType(file) {
+    let rst = false;
+    for (let fType of remove_file_types) {
+        if (file.indexOf(fType) === (file.length - fType.length)) {
+            rst = true;
+            break;
+        }
+    }
+    return rst;
+}
+let jobObj = {
+    createJob: function (rule, rootPath) {
+        let localRule = rule;
+        if (!localRule) {
+            // setup schedule rule
+            localRule = new cron.RecurrenceRule();
+            // 3:15AM, everyday
+            localRule.dayOfWeek = [1,2,3,4,5,6,0];
+            localRule.hour = 3;
+            localRule.minute = 15;
+        }
+        cron.scheduleJob(localRule, function(){
+            let path = rootPath;
+            fs.exists(path, function (exists) {
+                if (exists) {
+                    fs.readdir(path, function (err, files) {
+                        let currentTime = (new Date()).valueOf();
+                        let timeGap = currentTime - (1000 * 60 * 60 * 24 * 0.5); //half day before
+                        files.forEach(function(file,index){
+                            if (chkIsRemoveType(file)) {
+                                let curPath = path + "/" + file;
+                                fs.stat(curPath,function(err,data){
+                                    if (timeGap > data.mtime) {
+                                        fs.unlink(curPath);
+                                    }
+                                });
+                            }
+                        });
+                    })
+                }
+            })
+        });
+    }
+};
+
+export default jobObj;

+ 12 - 0
modules/std_billsGuidance_lib/controllers/libController.js

@@ -87,6 +87,18 @@ class BillsGuideLibController extends BaseController{
             callback(req, res, 1, err, null);
         }
     }
+
+    async testItems(req, res){
+        try{
+            let data = JSON.parse(req.body.data);
+            let rst = await billsGuidanceFacade.testItems(data.libID);
+            res.json({error: 0, data: rst, message: ''});
+        }
+        catch (error){
+            res.json({error: 1, data: null, message: error});
+
+        }
+    }
 }
 
 export default BillsGuideLibController;

+ 3 - 2
modules/std_billsGuidance_lib/controllers/viewController.js

@@ -8,7 +8,7 @@
  * @version
  */
 import BaseController from "../../common/base/base_controller";
-
+let config = require("../../../config/config.js");
 class ViewsController extends BaseController{
     redirectMain(req, res){
         res.render('maintain/billsGuidance_lib/html/main.html',
@@ -19,7 +19,8 @@ class ViewsController extends BaseController{
     redirectGuidance(req, res){
         res.render('maintain/billsGuidance_lib/html/zhiyin.html',
             {
-                userAccount: req.session.managerData.username
+                userAccount: req.session.managerData.username,
+                LicenseKey:config.getLicenseKey(process.env.NODE_ENV)
             });
     }
 }

+ 0 - 0
modules/std_billsGuidance_lib/facade/facades.js


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