Преглед на файлове

report code first check in

TonyKang преди 5 години
родител
ревизия
d3eb3db8cb
променени са 42 файла, в които са добавени 10537 реда и са изтрити 0 реда
  1. 1 0
      report/.eslintignore
  2. 29 0
      report/.eslintrc.js
  3. 21 0
      report/.gitignore
  4. 29 0
      report/README.md
  5. 19 0
      report/dist/index.cjs.js
  6. 1 0
      report/dist/index.d.ts
  7. 2994 0
      report/package-lock.json
  8. 47 0
      report/package.json
  9. 6 0
      report/prettier.config.js
  10. 28 0
      report/rollup.config.js
  11. 96 0
      report/src/core/helper/jpc_helper_area.ts
  12. 123 0
      report/src/core/helper/jpc_helper_band.ts
  13. 180 0
      report/src/core/helper/jpc_helper_common.ts
  14. 91 0
      report/src/core/helper/jpc_helper_common_output.ts
  15. 257 0
      report/src/core/helper/jpc_helper_cross_tab.ts
  16. 132 0
      report/src/core/helper/jpc_helper_discrete.ts
  17. 156 0
      report/src/core/helper/jpc_helper_field.ts
  18. 66 0
      report/src/core/helper/jpc_helper_flow_tab.ts
  19. 513 0
      report/src/core/helper/jpc_helper_font_width.ts
  20. 22 0
      report/src/core/helper/jpc_helper_text.ts
  21. 57 0
      report/src/core/jpc_band.ts
  22. 165 0
      report/src/core/jpc_bill_tab.ts
  23. 832 0
      report/src/core/jpc_cross_tab.ts
  24. 116 0
      report/src/core/jpc_data.ts
  25. 38 0
      report/src/core/jpc_event.ts
  26. 399 0
      report/src/core/jpc_ex.ts
  27. 74 0
      report/src/core/jpc_field.ts
  28. 1505 0
      report/src/core/jpc_flow_tab.ts
  29. 22 0
      report/src/core/jpc_function.ts
  30. 30 0
      report/src/core/jpc_param.ts
  31. 182 0
      report/src/core/jpc_rte.ts
  32. 331 0
      report/src/core/jpc_value_define.ts
  33. 281 0
      report/src/excel_util/excel_base_files/theme1.xml
  34. 1089 0
      report/src/excel_util/rpt_excel_util.ts
  35. 16 0
      report/src/excel_util/test/demo.test.ts
  36. 15 0
      report/src/excel_util/test/demo.ts
  37. 4 0
      report/src/index.ts
  38. 36 0
      report/src/public/fsUtil.ts
  39. 215 0
      report/src/public/scMathUtil.ts
  40. 137 0
      report/src/public/stringUtil.ts
  41. 166 0
      report/src/public/treeUtil.ts
  42. 16 0
      report/tsconfig.json

+ 1 - 0
report/.eslintignore

@@ -0,0 +1 @@
+/dist

+ 29 - 0
report/.eslintrc.js

@@ -0,0 +1,29 @@
+module.exports = {
+  env: {
+    browser: true,
+    es2021: true,
+    node: true,
+  },
+  extends: ['airbnb-base', 'plugin:@typescript-eslint/recommended', 'prettier'],
+  parser: '@typescript-eslint/parser',
+  parserOptions: {
+    ecmaVersion: 12,
+    sourceType: 'module',
+  },
+  plugins: ['@typescript-eslint', 'prettier'],
+  rules: {
+    'prettier/prettier': 'error',
+    'import/extensions': [
+      'error',
+      {
+        js: 'never',
+        jsx: 'never',
+        ts: 'never',
+        tsx: 'never',
+        json: 'always',
+      },
+    ],
+    'import/no-unresolved': 'off',
+    'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
+  },
+};

+ 21 - 0
report/.gitignore

@@ -0,0 +1,21 @@
+.DS_Store
+node_modules
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 29 - 0
report/README.md

@@ -0,0 +1,29 @@
+本项目是开发TypeScript npm包项目的模板,基于rollup打包,输出可被CommonJS、ESModule识别,并可直接被浏览器script引入的模块。
+### 开始
+基于本仓库进行开发,可以fork一个仓库或自行拷贝目录结构。
+### 初始化
+npm install
+### 构建
+npm run build
+### 代码风格
+ESLint + Airbnb config
+   
+Prettier
+
+### 发布
+package.json中name名称需要修改为相应包名称。为了将包发到npm私服上,name且必须以“@sc/”开头,如:@sc/test。
+
+**发布前应先执行构建指令**
+
+注册npm的地址为npm私服地址:
+
+`npm config  set registry http://192.168.1.90:4873 `(可使用nrm进行管理)
+
+发布
+
+`npm publish`
+
+若缺少权限,需要先进行用户验证:
+
+- 无私服账户: `npm adduser`
+- 有私服账户 `npm login`

+ 19 - 0
report/dist/index.cjs.js

@@ -0,0 +1,19 @@
+'use strict';
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+function testA() {
+    return 'testA';
+}
+
+function testB() {
+    return 'testB';
+}
+
+function justTest() {
+    return 'just a test';
+}
+
+exports.default = justTest;
+exports.testA = testA;
+exports.testB = testB;

+ 1 - 0
report/dist/index.d.ts

@@ -0,0 +1 @@
+export default function justTest(): string;

Файловите разлики са ограничени, защото са твърде много
+ 2994 - 0
report/package-lock.json


+ 47 - 0
report/package.json

@@ -0,0 +1,47 @@
+{
+  "name": "@sc/report",
+  "version": "3.0.0",
+  "description": "Smartcost Report Relative Module",
+  "main": "./dist/index.cjs.js",
+  "module": "./dist/index.esm.js",
+  "browser": "./dist/index.min.js",
+  "types": "./dist/index.d.ts",
+  "scripts": {
+    "test": "echo \"Error: no test specnpified\" && exit 1",
+    "build": "rollup -c"
+  },
+  "keywords": [],
+  "author": "Tony Kang",
+  "license": "ISC",
+  "devDependencies": {
+    "@types/chai": "^4.2.14",
+    "@types/jszip": "^3.4.1",
+    "@types/mocha": "^8.0.4",
+    "@types/ms": "^0.7.31",
+    "@types/node": "^14.14.8",
+    "@types/uuid": "^8.3.0",
+    "@typescript-eslint/eslint-plugin": "^4.4.1",
+    "@typescript-eslint/parser": "^4.4.1",
+    "chai": "^4.2.0",
+    "eslint": "^7.11.0",
+    "eslint-config-airbnb-base": "^14.2.0",
+    "eslint-config-prettier": "^6.12.0",
+    "eslint-plugin-import": "^2.22.1",
+    "eslint-plugin-prettier": "^3.1.4",
+    "jszip": "^3.5.0",
+    "mocha": "^8.2.1",
+    "prettier": "^2.1.2",
+    "rollup": "^2.30.0",
+    "rollup-plugin-commonjs": "^10.1.0",
+    "rollup-plugin-node-resolve": "^5.2.0",
+    "rollup-plugin-terser": "^7.0.2",
+    "rollup-plugin-typescript2": "^0.27.3",
+    "ts-node": "^9.0.0",
+    "tslib": "^2.0.3",
+    "typescript": "^4.0.5"
+  },
+  "dependencies": {
+    "stream": "0.0.2",
+    "uuid": "^8.3.0"
+  }
+}

+ 6 - 0
report/prettier.config.js

@@ -0,0 +1,6 @@
+module.exports = {
+  singleQuote: true, // 单引号
+  trailingComma: 'es5', // 对象末尾以逗号结束
+  arrowParens: 'avoid', // 箭头函数只有一个参数的时候,不使用()
+  endOfLine: 'auto', // CRLF,LF都可以
+};

+ 28 - 0
report/rollup.config.js

@@ -0,0 +1,28 @@
+// import resolve from 'rollup-plugin-node-resolve';
+// import commonjs from 'rollup-plugin-commonjs';
+import typescript from 'rollup-plugin-typescript2'; // 一定要是typescript2,如果使用typescript,没法自动生成.d.ts文件
+// import { terser } from 'rollup-plugin-terser';
+import pkg from './package.json';
+
+export default [
+  // UMD for browser
+  /* {
+    input: 'src/index.ts',
+    output: {
+      name: 'howLongUntilLunch',
+      file: pkg.browser,
+      format: 'umd',
+    },
+    plugins: [resolve(), commonjs(), typescript(), terser()], // 浏览器使用的代码文件进行简化
+  }, */
+  // CommonJS for Node and ES module for bundlers build
+  {
+    input: 'src/index.ts',
+    external: ['ms'],
+    plugins: [typescript()],
+    output: [
+      { file: pkg.main, format: 'cjs' },
+      // { file: pkg.module, format: 'es' },
+    ],
+  },
+];

+ 96 - 0
report/src/core/helper/jpc_helper_area.ts

@@ -0,0 +1,96 @@
+'use strict';
+
+import JV from '../jpc_value_define';
+
+const JpcAreaHelper = {
+    outputArea: function(areaNode: any, band: any, unitFactor: number, rowAmount: number, rowIdx: number, colAmount: number, colIdx: number, 
+                        multipleDispCol: number, multipleColIdx: number, syncHeight: boolean, syncWidth: boolean) {
+        let rst = {
+            [JV.PROP_LEFT]: 0,
+            [JV.PROP_RIGHT]: 0,
+            [JV.PROP_TOP]: 0,
+            [JV.PROP_BOTTOM]: 0
+        }, 
+            maxMultiColumns = 3;
+        if (multipleDispCol > 0 && multipleDispCol <= maxMultiColumns) {
+            //1. calculate left/right
+            let areaWidth = 1.0 * (band[JV.PROP_RIGHT] - band[JV.PROP_LEFT]) / multipleDispCol;
+            areaWidth = areaWidth / colAmount;
+            let innerLeft = 0.0, innerRight = areaWidth;
+            //
+            if (typeof areaNode[JV.PROP_H_CALCULATION]  === "string") {
+                switch (areaNode[JV.PROP_H_CALCULATION]) {
+                    case JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE] :
+                        innerLeft = (1.0 * areaNode[JV.PROP_LEFT] * areaWidth / JV.HUNDRED_PERCENT);
+                        innerRight = (1.0 * areaNode[JV.PROP_RIGHT] * areaWidth / JV.HUNDRED_PERCENT);
+                        break;
+                    case JV.CAL_TYPE[JV.CAL_TYPE_ABSTRACT] :
+                        innerLeft = 1.0 * areaNode[JV.PROP_LEFT] * unitFactor;
+                        innerRight = 1.0 * areaNode[JV.PROP_RIGHT] * unitFactor;
+                        break;
+                }
+            } else {
+                //颗粒度更加细化的控制,可能左右两边的计算坐标方式都不同
+                if (areaNode[JV.PROP_H_CALCULATION][JV.PROP_LEFT] === JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE]) {
+                    innerLeft = (1.0 * areaNode[JV.PROP_LEFT] * areaWidth / JV.HUNDRED_PERCENT);
+                } else {
+                    innerLeft = 1.0 * areaNode[JV.PROP_LEFT] * unitFactor;
+                }
+                if (areaNode[JV.PROP_H_CALCULATION][JV.PROP_RIGHT] === JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE]) {
+                    innerRight = (1.0 * areaNode[JV.PROP_RIGHT] * areaWidth / JV.HUNDRED_PERCENT);
+                } else {
+                    innerRight = 1.0 * areaNode[JV.PROP_RIGHT] * unitFactor;
+                }
+            }
+            //2. calculate top/bottom
+            let  areaHeight = 1.0 * (band[JV.PROP_BOTTOM] - band[JV.PROP_TOP]);
+            areaHeight = areaHeight / rowAmount;
+            let innerTop = 0.0, innerBottom = areaHeight;
+            if (typeof areaNode[JV.PROP_V_CALCULATION]  === "string") {
+                switch (areaNode[JV.PROP_V_CALCULATION]) {
+                    case JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE] :
+                        innerTop = (1.0 * areaNode[JV.PROP_TOP] * areaHeight / JV.HUNDRED_PERCENT);
+                        innerBottom = (1.0 * areaNode[JV.PROP_BOTTOM] * areaHeight / JV.HUNDRED_PERCENT);
+                        break;
+                    case JV.CAL_TYPE[JV.CAL_TYPE_ABSTRACT] :
+                        innerTop = 1.0 * areaNode[JV.PROP_TOP] * unitFactor;
+                        innerBottom = 1.0 * areaNode[JV.PROP_BOTTOM] * unitFactor;
+                        break;
+                }
+            } else {
+                //颗粒度更加细化的控制,可能上下两边的计算坐标方式都不同
+                if (areaNode[JV.PROP_V_CALCULATION][JV.PROP_TOP] === JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE]) {
+                    innerTop = (1.0 * areaNode[JV.PROP_TOP] * areaHeight / JV.HUNDRED_PERCENT);
+                } else {
+                    innerTop = 1.0 * areaNode[JV.PROP_TOP] * unitFactor;
+                }
+                if (areaNode[JV.PROP_V_CALCULATION][JV.PROP_BOTTOM] === JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE]) {
+                    innerBottom = (1.0 * areaNode[JV.PROP_BOTTOM] * areaHeight / JV.HUNDRED_PERCENT);
+                } else {
+                    innerBottom = 1.0 * areaNode[JV.PROP_BOTTOM] * unitFactor;
+                }
+            }
+            //
+            let rstLeft = 0.0, rstRight = 0.0, rstTop = 0.0, rstBottom = 0.0;
+            if (syncHeight) {
+                rstBottom = Math.round(1.0 * band[JV.PROP_TOP] + areaHeight * (rowIdx + 1) + innerTop);
+            } else {
+                rstBottom = Math.round(1.0 * band[JV.PROP_TOP] + areaHeight * rowIdx + innerBottom);
+            }
+            if (syncWidth) {
+                rstRight = Math.round(1.0 * band[JV.PROP_LEFT] + areaWidth * (colIdx + 1) + innerLeft + multipleColIdx * areaWidth);
+            } else {
+                rstRight = Math.round(1.0 * band[JV.PROP_LEFT] + areaWidth * colIdx + innerRight + multipleColIdx * areaWidth);
+            }
+            rstLeft = Math.round(1.0 * band[JV.PROP_LEFT] + areaWidth * colIdx + innerLeft + multipleColIdx * areaWidth);
+            rstTop = Math.round(1.0 * band[JV.PROP_TOP] + areaHeight * rowIdx + innerTop);
+            rst[JV.PROP_LEFT] = rstLeft;
+            rst[JV.PROP_RIGHT] = rstRight;
+            rst[JV.PROP_TOP] = rstTop;
+            rst[JV.PROP_BOTTOM] = rstBottom;
+        }
+        return rst;
+    }
+};
+
+export default JpcAreaHelper;

+ 123 - 0
report/src/core/helper/jpc_helper_band.ts

@@ -0,0 +1,123 @@
+'use strict';
+
+import JV from '../jpc_value_define';
+import JpcCommonHelper from './jpc_helper_common';
+
+let JpcBandHelper = {
+    getBandTypeValByString: function(bandType: string) {
+        let rst = JV.PAGE_STATUS.indexOf(bandType);
+        if (rst < 0) rst = JV.STATUS_NORMAL;
+        return rst;
+    },
+    setBandArea: function(bands: any, rptTpl: any, pageStatus: boolean[], isOnlyNormalStatus: boolean, isOnlyExStatus: boolean) {
+        let me = this;
+        if (rptTpl[JV.NODE_BAND_COLLECTION]) {
+            isOnlyNormalStatus = isOnlyNormalStatus||false;
+            isOnlyExStatus = isOnlyExStatus||false;
+
+            let unitFactor = JpcCommonHelper.getUnitFactor(rptTpl);
+            let orgArea = JpcCommonHelper.getReportArea(rptTpl, unitFactor);
+            for (let i = 0; i < rptTpl[JV.NODE_BAND_COLLECTION].length; i++) {
+                me.setBandPos(bands, rptTpl[JV.NODE_BAND_COLLECTION][i], orgArea, unitFactor, pageStatus, isOnlyNormalStatus, isOnlyExStatus);
+            }
+        }
+    },
+    setBandPos: function(bands: any, bandNode: any, orgArea: any, unitFactor: number, pageStatus: boolean[], isOnlyNormalStatus: boolean, isOnlyExStatus: boolean) {
+        let me = this, band = bands[bandNode[JV.BAND_PROP_NAME]];
+        //0. for multi flow purpose
+        if (isOnlyNormalStatus) {
+            if (bandNode.hasOwnProperty(JV.PROP_BAND_EX_ONLY) && JpcCommonHelper.getBoolean(bandNode[JV.PROP_BAND_EX_ONLY])) {
+                return;
+            }
+        }
+        if (isOnlyExStatus) {
+            if (bandNode.hasOwnProperty(JV.PROP_BAND_NORMAL_ONLY) && !(JpcCommonHelper.getBoolean(bandNode[JV.PROP_BAND_NORMAL_ONLY]))) {
+                return;
+            }
+        }
+        //1. initialize
+        band[JV.PROP_LEFT] = orgArea[JV.IDX_LEFT];
+        band[JV.PROP_TOP] = orgArea[JV.IDX_TOP];
+        band[JV.PROP_RIGHT] = orgArea[JV.IDX_RIGHT];
+        band[JV.PROP_BOTTOM] = orgArea[JV.IDX_BOTTOM];
+        //2. set this band
+        if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]]) {
+            switch (band[JV.BAND_PROP_ALIGNMENT]) {
+                case JV.LAYOUT_TOP:
+                    if (band[JV.PROP_CALCULATION] === JV.CAL_TYPE_ABSTRACT) {
+                        band.Bottom = band.Top + unitFactor * band[JV.BAND_PROP_HEIGHT];
+                    } else {
+                        band.Bottom = band.Top + (band.Bottom - band.Top) * band[JV.BAND_PROP_HEIGHT] / 100;
+                    }
+                    orgArea[JV.IDX_TOP] = band.Bottom;
+                    break;
+                case JV.LAYOUT_BOTTOM:
+                    if (band[JV.PROP_CALCULATION] === JV.CAL_TYPE_ABSTRACT) {
+                        band.Top = band.Bottom - unitFactor * band[JV.BAND_PROP_HEIGHT];
+                    } else {
+                        band.Top = band.Bottom - (band.Bottom - band.Top) * band[JV.BAND_PROP_HEIGHT] / 100;
+                    }
+                    orgArea[JV.IDX_BOTTOM] = band.Top;
+                    break;
+                case JV.LAYOUT_LEFT:
+                    if (band[JV.PROP_CALCULATION] === JV.CAL_TYPE_ABSTRACT) {
+                        band.Right = band.Left + unitFactor * band[JV.BAND_PROP_WIDTH];
+                    } else {
+                        band.Right = band.Left + (band.Right - band.Left) * band[JV.BAND_PROP_WIDTH] / 100;
+                    }
+                    orgArea[JV.IDX_LEFT] = band.Right;
+                    break;
+                case JV.LAYOUT_RIGHT:
+                    if (band[JV.PROP_CALCULATION] === JV.CAL_TYPE_ABSTRACT) {
+                        band.Left = band.Right - unitFactor * band[JV.BAND_PROP_WIDTH];
+                    } else {
+                        band.Left = band.Right - (band.Right - band.Left) * band[JV.BAND_PROP_WIDTH] / 100;
+                    }
+                    orgArea[JV.IDX_RIGHT] = band.Left;
+                    break;
+            }
+            //3. set sub-bands
+            if (bandNode[JV.BAND_PROP_SUB_BANDS]) {
+                let bandArea = [band.Left, band.Top, band.Right, band.Bottom];
+                for (let i = 0; i < bandNode[JV.BAND_PROP_SUB_BANDS].length; i++) {
+                    me.setBandPos(bands, bandNode[JV.BAND_PROP_SUB_BANDS][i], bandArea, unitFactor, pageStatus, isOnlyNormalStatus, isOnlyExStatus);
+                }
+            }
+        }
+    },
+    resetBandPos: function (bandCollection: any[], bands: any, contentBand: any, offsetX: number, offsetY: number) {
+        let orgX = contentBand.Right, orgY = contentBand.Bottom;
+        function _chkAndResetPos(targetBand: any) {
+            let band = bands[targetBand.Name];
+            if (band) {
+                if (band === contentBand) {
+                    band.Bottom += offsetY;
+                    band.Right += offsetX;
+                } else {
+                    if (band.Top >= orgY) {
+                        band.Top += offsetY;
+                        band.Bottom += offsetY;
+                    } else if (band.Bottom >= orgY && band.Top < orgY) {
+                        band.Bottom += offsetY;
+                    }
+                    if (band.Left >= orgX) {
+                        band.Left += offsetX;
+                        band.Right += offsetX;
+                    } else if (band.Right >= orgX && band.Le < orgX) {
+                        band.Right += offsetX;
+                    }
+                }
+                if (targetBand[JV.BAND_PROP_SUB_BANDS]) {
+                    for (let i = 0; i < targetBand[JV.BAND_PROP_SUB_BANDS].length; i++) {
+                        _chkAndResetPos(targetBand[JV.BAND_PROP_SUB_BANDS][i]);
+                    }
+                }
+            }
+        }
+        for (let i = 0; i < bandCollection.length; i++) {
+            _chkAndResetPos(bandCollection[i]);
+        }
+    }
+};
+
+export default JpcBandHelper;

+ 180 - 0
report/src/core/helper/jpc_helper_common.ts

@@ -0,0 +1,180 @@
+'use strict';
+
+import JV from '../jpc_value_define';
+
+const RESOLUTION = [96, 96];
+
+const JpcCommonHelper = {
+    getResultByID(KeyID: string, collectionList: any[]) {
+        let rst = null;
+        if (KeyID) {
+            for (let i = 0; i < collectionList.length; i++) {
+                let collection = collectionList[i];
+                if (collection && collection instanceof Array) {
+                    for (let j = 0; j < collection.length; j++) {
+                        if (collection[j][JV.PROP_ID] === KeyID) {
+                            rst = collection[j];
+                            break;
+                        }
+                    }
+                    if (rst) break;
+                }
+            }
+        }
+        return rst;
+    },
+    getFont(fontName: string, dftFonts: any[], rptTpl: any) {
+        let me = this, list = [];
+        if (rptTpl) list.push(rptTpl[JV.NODE_FONT_COLLECTION]);
+        list.push(dftFonts);
+        return me.getResultByID(fontName, list);
+    },
+    getStyle(styleName: string, dftStyles: any[], rptTpl: any) {
+        let me = this, list = [];
+        if (rptTpl) list.push(rptTpl[JV.NODE_STYLE_COLLECTION]);
+        list.push(dftStyles);
+        return me.getResultByID(styleName, list);
+    },
+    getControl(controlName: string, dftControls: any[], rptTpl: any) {
+        let me = this, list = [];
+        if (rptTpl) list.push(rptTpl[JV.NODE_CONTROL_COLLECTION]);
+        list.push(dftControls);
+        return me.getResultByID(controlName, list);
+    },
+    getLayoutAlignment(alignStr: string) {
+        let rst = JV.LAYOUT.indexOf(alignStr);
+        if (rst < 0) rst = JV.LAYOUT_FULFILL;
+        return rst;
+    },
+    getPosCalculationType(typeStr: string) {
+        let rst = JV.CAL_TYPE.indexOf(typeStr);
+        if (rst < 0) rst = JV.CAL_TYPE_ABSTRACT;
+        return rst;
+    },
+    getBoolean(bStr: any) {
+        let rst = false;
+        if (bStr !== null && bStr !== undefined) {
+            let valType = typeof(bStr);
+            if (valType === 'boolean') {
+                rst = bStr;
+            } else if (valType === 'string') {
+                let tS = bStr.toUpperCase();
+                rst = (tS === 'T' || tS === 'TRUE' || tS === 'YES' || tS === 'Y');
+            } else if (valType === 'number') {
+                rst = (bStr === 1);
+            }
+        }
+        return rst;
+    },
+    getScreenDPI() {
+        return RESOLUTION.slice(0);
+    },
+    getUnitFactor(rptTpl: any) {
+        let me = this;
+        return me.translateUnit(rptTpl[JV.NODE_MAIN_INFO][JV.PROP_UNITS]);
+    },
+    translateUnit(unitStr: string) {
+        let me = this, rst = 1.0;
+        if (unitStr) {
+            let sdpi = me.getScreenDPI();
+            if (JV.MEASUREMENT.PIXEL.indexOf(unitStr) >= 0) {
+                rst = 1.0;
+            } else if (JV.MEASUREMENT.CM.indexOf(unitStr) >= 0) {
+                rst = 1.0 * sdpi[0] / 2.54;
+            } else if (JV.MEASUREMENT.INCH.indexOf(unitStr) >= 0) {
+                rst = 1.0 * sdpi[0];
+            }
+        }
+        return rst;
+    },
+    getPageSize(rptTpl: any) {
+        let size = JV.SIZE_A4.slice(0);
+        let sizeStr = rptTpl[JV.NODE_MAIN_INFO][JV.NODE_PAGE_INFO][JV.PROP_PAGE_SIZE];
+        let sizeIdx = JV.PAGES_SIZE_STR.indexOf(sizeStr);
+        if (sizeIdx >= 0) {
+            size = JV.PAGES_SIZE[sizeIdx].slice(0);
+        } else if (sizeStr === JV.PAGE_SELF_DEFINE) {
+            //自定义规格,如实际业务需要则后期补充
+        }
+        let page_orientation = rptTpl[JV.NODE_MAIN_INFO][JV.NODE_PAGE_INFO][JV.PROP_ORIENTATION];
+        if (page_orientation === JV.ORIENTATION_LANDSCAPE || page_orientation === JV.ORIENTATION_LANDSCAPE_CHN) {
+            //swap x,y
+            let tmp = size[0];
+            size[0] = size[1];
+            size[1] = tmp;
+        }
+        return size;
+    },
+    getReportArea(rptTpl: any, unitFactor: number) {
+        let me = this, sdpi = me.getScreenDPI(), rst = [], size = me.getPageSize(rptTpl);
+        size[0] = sdpi[0] * size[0];
+        size[1] = sdpi[0] * size[1];
+        rst.push(unitFactor * rptTpl[JV.NODE_MAIN_INFO][JV.NODE_MARGINS][JV.PROP_LEFT]);
+        rst.push(unitFactor * rptTpl[JV.NODE_MAIN_INFO][JV.NODE_MARGINS][JV.PROP_TOP]);
+        rst.push(size[0] - unitFactor * rptTpl[JV.NODE_MAIN_INFO][JV.NODE_MARGINS][JV.PROP_RIGHT]);
+        rst.push(size[1] - unitFactor * rptTpl[JV.NODE_MAIN_INFO][JV.NODE_MARGINS][JV.PROP_BOTTOM]);
+        return rst;
+    },
+    getSegIdxByPageIdx(page: number, page_seg_map: any[]) {
+        let rst = -1;
+        for (let pIdx = 0; pIdx < page_seg_map.length; pIdx++) {
+            if (page_seg_map[pIdx][0] === page) {
+                rst = page_seg_map[pIdx][1];
+                break;
+            }
+        }
+        return rst;
+    },
+    getStringLinesInArea(area: any, strVal: string, chnW: number, otherW: number) {
+        //备注: 因后台的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;
+    },
+    splitString(area: any, strVal: string, chnW: number, otherW: number) {
+        let rst = [];
+        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;
+    }
+};
+
+export default JpcCommonHelper;

+ 91 - 0
report/src/core/helper/jpc_helper_common_output.ts

@@ -0,0 +1,91 @@
+'use strict';
+
+import JV from '../jpc_value_define';
+import JpcFieldHelper from './jpc_helper_field';
+const OFFSET_FLOAT = 0.0000000001;
+
+let JpcCommonOutputHelper = {
+    createCommonOutputWithoutDecorate: function (node: any, value: any, forceCombine: boolean) {
+        let me = this, 
+        //1. font/style/control
+        rst = {
+            [JV.PROP_FONT]: node[JV.PROP_FONT],
+            [JV.PROP_CONTROL]: node[JV.PROP_CONTROL],
+            [JV.PROP_STYLE]: node[JV.PROP_STYLE],
+            [JV.PROP_VALUE]: value
+        };
+        //2. value
+        me.formatCell(node[JV.PROP_FORMAT], rst);
+        // innerFormat(node[JV.PROP_FORMAT], rst);
+        if (node[JV.PROP_PREFIX] && rst[JV.PROP_VALUE] !== null) {
+            rst[JV.PROP_VALUE] = node[JV.PROP_PREFIX] + rst[JV.PROP_VALUE];
+        } else if (node[JV.PROP_PREFIX] && forceCombine) {
+            rst[JV.PROP_VALUE] = node[JV.PROP_PREFIX];
+        }
+        if (node[JV.PROP_SUFFIX] && rst[JV.PROP_VALUE] !== null) {
+            rst[JV.PROP_VALUE] = rst[JV.PROP_VALUE] + node[JV.PROP_SUFFIX];
+        } else if (node[JV.PROP_SUFFIX] && forceCombine) {
+            rst[JV.PROP_VALUE] = node[JV.PROP_SUFFIX];
+        }
+        return rst;
+    },
+    createCommonOutput: function (node: any, value: any, controls: any) {
+        let me = this,
+        //1. font/style/control
+        rst = {
+            [JV.PROP_FONT]: node[JV.PROP_FONT],
+            [JV.PROP_CONTROL]: node[JV.PROP_CONTROL],
+            [JV.PROP_STYLE]: node[JV.PROP_STYLE],
+            [JV.PROP_VALUE]: value
+        };
+        //2. value
+        JpcFieldHelper.decorateValue(rst, controls);
+        me.formatCell(node[JV.PROP_FORMAT], rst);
+        // innerFormat(node[JV.PROP_FORMAT], rst);
+        if (node[JV.PROP_PREFIX] && rst[JV.PROP_VALUE] !== null && rst[JV.PROP_VALUE] !== '') {
+            rst[JV.PROP_VALUE] = node[JV.PROP_PREFIX] + rst[JV.PROP_VALUE];
+        }
+        if (node[JV.PROP_SUFFIX] && rst[JV.PROP_VALUE] !== null && rst[JV.PROP_VALUE] !== '') {
+            rst[JV.PROP_VALUE] = rst[JV.PROP_VALUE] + node[JV.PROP_SUFFIX];
+        }
+        return rst;
+    },
+    formatCell: function(formatStr: string, rstCell: any) {
+        if (formatStr) {
+            const rstCellFloatVal = parseFloat(rstCell[JV.PROP_VALUE]);
+            if (!(isNaN(rstCellFloatVal))) {
+                const dotIdx = formatStr.indexOf('.');
+                if (dotIdx >= 0) {
+                    let tmpStr = (rstCellFloatVal + OFFSET_FLOAT).toFixed(formatStr.length - dotIdx - 1);
+                    const digStr = formatStr.substr(dotIdx + 1, formatStr.length - dotIdx);
+                    for (let sIdx = digStr.length - 1; sIdx >= 0; sIdx--) {
+                        if (digStr[sIdx] === '#') {
+                            if (tmpStr.length > 0 && tmpStr[tmpStr.length - 1] === '0') {
+                                tmpStr = tmpStr.substr(0, tmpStr.length - 1);
+                            } else {
+                                break;
+                            }
+                        } else {
+                            break;
+                        }
+                    }
+                    if (tmpStr[tmpStr.length - 1] === '.') tmpStr = tmpStr.substr(0, tmpStr.length - 1);
+                    rstCell[JV.PROP_VALUE] = tmpStr;
+                } else {
+                    rstCell[JV.PROP_VALUE] = (rstCellFloatVal + OFFSET_FLOAT).toFixed(0);
+                }
+                const commaIdx = formatStr.indexOf(',');
+                if (commaIdx >= 0) {
+                    rstCell[JV.PROP_VALUE] = comdify(rstCell[JV.PROP_VALUE].toString());
+                }
+            }
+        }
+    }
+};
+
+function comdify(numStr: string){
+    let re = /\d{1,3}(?=(\d{3})+$)/g;
+    return numStr.replace(/^(\d+)((\.\d+)?)$/,function(s,s1,s2){return s1.replace(re,'$&,')+s2;});
+}
+
+export default JpcCommonOutputHelper;

+ 257 - 0
report/src/core/helper/jpc_helper_cross_tab.ts

@@ -0,0 +1,257 @@
+import JV from '../jpc_value_define';
+import JE from '../jpc_rte';
+import JpcCommonHelper from './jpc_helper_common';
+
+let JpcCrossTabHelper = {
+    getColIDX: function(cl: any[], val: any[]) {
+        let rst = -1;
+        for (let i = 0; i < cl.length; i++) {
+            let ca = cl[i];
+            for (let j = 0; j < ca.length; j++) {
+                if (ca[j] == val) {
+                    rst = i;
+                    break;
+                }
+            }
+            if (rst != -1) {
+                break;
+            }
+        }
+        return rst;
+    },
+    pushToSeg: function(segArr: any[], dataSeq: any[], segIdx: number, sIdx: number, eIdx: number) {
+        let arrIdx = [];
+        for (let k = sIdx; k < eIdx; k++) {
+            arrIdx.push(dataSeq[segIdx][k]);
+        }
+        segArr.push(arrIdx);
+    },
+    sortFieldValue: function(sIDX: number, eIDX: number, sortOrder: any, dataField: any, dataValSeq: any[]) {
+        let tmpSeq = [];
+        if ((sortOrder) && (sortOrder !== JV.TAB_FIELD_PROP_SORT_VAL_NOSORT)) {
+            if (sIDX >= 0 && eIDX >= sIDX && dataValSeq.length > eIDX) {
+                let reversed = 1;
+                if (sortOrder === JV.TAB_FIELD_PROP_SORT_VAL_DESC) {
+                    reversed = -1;
+                }
+                for (let i = sIDX; i <= eIDX; i++) {
+                    tmpSeq.push(dataValSeq[i]);
+                }
+                tmpSeq.sort(function(idx1, idx2) {
+                    let rst = 0;
+                    // if (isNaN(parseFloat(dataField[idx1])) || isNaN(parseFloat(dataField[idx1]))) {
+                    if (typeof (dataField[idx1]) === 'string' || typeof (dataField[idx1]) === 'string' ) {
+                        if (dataField[idx1] > dataField[idx2]) {
+                            rst = reversed;
+                        } else if (dataField[idx1] < dataField[idx2]) {
+                            rst = -reversed;
+                        }
+                    } else {
+                        if ((1.0 * dataField[idx1]) > (1.0 * dataField[idx2])) {
+                            rst = reversed;
+                        } else if ((1.0 * dataField[idx1]) < (1.0 * dataField[idx2])) {
+                            rst = -reversed;
+                        }
+                    }
+                    return rst;
+                });
+            }
+        }
+        if (tmpSeq.length > 0) {
+            for (let i = sIDX; i <= eIDX; i++) {
+                dataValSeq[i] = tmpSeq[i - sIDX];
+            }
+        }
+        return tmpSeq;
+    },
+    checkIfEqual: function(dataFields: any[], seq1: any, seq2: any) {
+        let rst = true;
+        for (let i = 0; i < dataFields.length; i++) {
+            if ((dataFields[i][seq1] !== dataFields[i][seq2])) {
+                rst = false;
+                break;
+            }
+        }
+        return rst;
+    },
+    sortTabFields: function(tabFields: any[], fieldSeqs: any[], data_details: any[], dataSeq: any[], $CURRENT_RPT: any) {
+        let me = this;
+        let sIDX = 0, eIDX = -1, isFirstSort = true;
+        for (let i = 0; i < tabFields.length; i++) {
+            let tabField = tabFields[i];
+            if (tabField[JV.TAB_FIELD_PROP_SORT] !== JV.TAB_FIELD_PROP_SORT_VAL_NOSORT) {
+                if (isFirstSort) {
+                    isFirstSort = false;
+                    //first field, should sort all data items
+                    for (let j = 0; j < dataSeq.length; j++) {
+                        sIDX = 0;
+                        eIDX = dataSeq[j].length - 1;
+                        //sort the field value here
+                        if (typeof(fieldSeqs[i]) === "object") {
+                            let exFirstField = JE.F(fieldSeqs[i][JV.PROP_ID], $CURRENT_RPT);
+                            if (exFirstField) {
+                                me.sortFieldValue(sIDX, eIDX, tabField[JV.TAB_FIELD_PROP_SORT], exFirstField[JV.PROP_AD_HOC_DATA], dataSeq[j]);
+                            } else {
+                                //不排序(健壮性)
+                            }
+                        } else {
+                            me.sortFieldValue(sIDX, eIDX, tabField[JV.TAB_FIELD_PROP_SORT],data_details[fieldSeqs[i]], dataSeq[j]);
+                        }
+                        // me.sortFieldValue(sIDX, eIDX, tabField[JV.TAB_FIELD_PROP_SORT],data_details[fieldSeqs[i]], dataSeq[j]);
+                    }
+                } else {
+                    //then sort the rest fields one by one
+                    for (let j = 0; j < dataSeq.length; j++) {
+                        let chkFields = [];
+                        for (let k = 0; k < i; k++) {
+                            if (typeof(fieldSeqs[k]) === "object") {
+                                let exField = JE.F(fieldSeqs[k][JV.PROP_ID], $CURRENT_RPT);
+                                if (exField) {
+                                    chkFields.push(exField[JV.PROP_AD_HOC_DATA]);
+                                } else {
+                                    chkFields.push(null);
+                                }
+                            } else {
+                                chkFields.push(data_details[fieldSeqs[k]]);
+                            }
+                            // chkFields.push(data_details[fieldSeqs[k]]);
+                        }
+                        sIDX = 0, eIDX = -1;
+                        for (let m = 1; m < dataSeq[j].length; m++) {
+                            if (!(me.checkIfEqual(chkFields, dataSeq[j][m - 1], dataSeq[j][m]))) {
+                                eIDX = m - 1;
+                            } else if (m == dataSeq[j].length - 1) {
+                                eIDX = m;
+                            };
+                            if (eIDX >= sIDX) {
+                                if (eIDX != sIDX) {
+                                    if (typeof(fieldSeqs[i]) === "object") {
+                                        let exOtherField = JE.F(fieldSeqs[i][JV.PROP_ID], $CURRENT_RPT);
+                                        if (exOtherField) {
+                                            me.sortFieldValue(sIDX, eIDX, tabField[JV.TAB_FIELD_PROP_SORT], exOtherField[JV.PROP_AD_HOC_DATA], dataSeq[j]);
+                                        } else {
+                                            //不排序(健壮性)
+                                        }
+                                    } else {
+                                        me.sortFieldValue(sIDX, eIDX, tabField[JV.TAB_FIELD_PROP_SORT],data_details[fieldSeqs[i]], dataSeq[j]);
+                                    }
+                                    // me.sortFieldValue(sIDX, eIDX, tabField[JV.TAB_FIELD_PROP_SORT],data_details[fieldSeqs[i]], dataSeq[j]);
+                                }
+                                sIDX = m;
+                                eIDX = m - 1; //for protection purpose
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    },
+    getMaxRowsPerPage: function(bands: any, rptTpl: any) {
+        let rst = 1;
+        let band = bands[rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW][JV.PROP_BAND_NAME]];
+        if (band) {
+            rst =  getMaxTabCntPerPage(rptTpl, JV.NODE_CROSS_ROW, JV.PROP_CMN_HEIGHT, band.Bottom - band.Top);
+        }
+        return rst;
+    },
+    getMaxColsPerPage: function(bands: any, rptTpl: any) {
+        let rst = 1;
+        let band = bands[rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_COL][JV.PROP_BAND_NAME]];
+        if (band) {
+            rst =  getMaxTabCntPerPage(rptTpl, JV.NODE_CROSS_COL, JV.PROP_CMN_WIDTH, band.Right - band.Left);
+        }
+        return rst;
+    },
+    getActualRowsHeight: function(bands: any, rptTpl: any, segments: any[], page: number) {
+        let rst = 1;
+        let band = bands[rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW][JV.PROP_BAND_NAME]];
+        if (band) {
+            rst =  getActualContentAreaMeasurement(rptTpl, JV.NODE_CROSS_ROW, JV.PROP_CMN_HEIGHT, band.Bottom - band.Top, segments, page);
+        }
+        return rst;
+    },
+    getActualColsWidth: function(bands: any, rptTpl: any, segments: any[], page: number) {
+        let rst = 1;
+        let band = bands[rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_COL][JV.PROP_BAND_NAME]];
+        if (band) {
+            rst =  getActualContentAreaMeasurement(rptTpl, JV.NODE_CROSS_COL, JV.PROP_CMN_WIDTH, band.Right - band.Left, segments, page);
+        }
+        return rst;
+    },
+    chkTabEnd: function(tabType: string, rptTpl: any, bands: any, sortedSequence: any[], segIdx: number, preRec: number, nextRec: number) {
+        let me = this, rst = true;
+        let remainAmt = preRec + nextRec - sortedSequence[segIdx].length;
+        rst = me.hasEnoughSpace(tabType, rptTpl, bands, remainAmt);
+        return rst;
+    },
+    hasEnoughSpace: function (tabType: string, rptTpl: any, bands: any, remainAmt: number) {
+        if (remainAmt < 0) return false;
+        let rst = true, measurement = 1.0, douDiffForCompare = 0.00001;
+        let unitFactor = JpcCommonHelper.getUnitFactor(rptTpl);
+        let band = null;
+        if (rptTpl[JV.NODE_CROSS_INFO][tabType]) {
+            band = bands[rptTpl[JV.NODE_CROSS_INFO][tabType][JV.PROP_BAND_NAME]];
+        }
+        if (band != null && band != undefined) {
+            if (tabType === JV.NODE_CROSS_ROW_SUM || tabType === JV.NODE_CROSS_ROW_EXT) {
+                measurement = 1.0 * rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW][JV.PROP_CMN_HEIGHT] * unitFactor;
+                let spareHeight = measurement * remainAmt;
+                let douH = 1.0 * (band.Bottom - band.Top);
+                rst = (spareHeight >= douH) || (spareHeight - douH <= douDiffForCompare);
+            } else if (tabType === JV.NODE_CROSS_COL_SUM) {
+                measurement = 1.0 * rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_COL][JV.PROP_CMN_WIDTH] * unitFactor;
+                let spareWidth = measurement * remainAmt;
+                let douW = 1.0 * (band.Right - band.Left);
+                rst = (spareWidth >= douW) || (spareWidth - douW <= douDiffForCompare);
+            }
+        }
+        return rst;
+    },
+    initialPageStatus: function (pageStatus: boolean[]) {
+        pageStatus[JV.STATUS_NORMAL] = true;
+        pageStatus[JV.STATUS_REPORT_START] = false;
+        pageStatus[JV.STATUS_REPORT_END] = false;
+        pageStatus[JV.STATUS_SEGMENT_START] = false;
+        pageStatus[JV.STATUS_SEGMENT_END] = false;
+        pageStatus[JV.STATUS_GROUP] = false;
+        pageStatus[JV.STATUS_CROSS_ROW_END] = false;
+        pageStatus[JV.STATUS_CROSS_COL_END] = false;
+    }
+};
+
+function getMaxTabCntPerPage(rptTpl: any, tabNodeName: string, tabMeasurePropName: string, measureForCal: number) {
+    let rst = 1;
+    if (rptTpl[JV.NODE_CROSS_INFO][tabNodeName]) {
+        let tab = rptTpl[JV.NODE_CROSS_INFO][tabNodeName];
+        let maxFieldMeasure = 1.0;
+        if (JV.CAL_TYPE_ABSTRACT === JpcCommonHelper.getPosCalculationType(tab[JV.PROP_CALCULATION])) {
+            let unitFactor = JpcCommonHelper.getUnitFactor(rptTpl);
+            maxFieldMeasure = 1.0 * rptTpl[JV.NODE_CROSS_INFO][tabNodeName][tabMeasurePropName] * unitFactor;
+        } else {
+            maxFieldMeasure = measureForCal * rptTpl[JV.NODE_CROSS_INFO][tabNodeName][tabMeasurePropName] / JV.HUNDRED_PERCENT;
+        }
+        rst = Math.floor(measureForCal / maxFieldMeasure);
+    }
+    if (rst <= 0) rst = 1;
+    return rst;
+};
+
+function getActualContentAreaMeasurement(rptTpl: any, tabNodeName: string, tabMeasurePropName: string, measureForCal: number, segments: any[], page: number) {
+    let rst = 1;
+    if (rptTpl[JV.NODE_CROSS_INFO][tabNodeName]) {
+        let tab = rptTpl[JV.NODE_CROSS_INFO][tabNodeName];
+        let maxFieldMeasure = 1.0;
+        if (JV.CAL_TYPE_ABSTRACT === JpcCommonHelper.getPosCalculationType(tab[JV.PROP_CALCULATION])) {
+            let unitFactor = JpcCommonHelper.getUnitFactor(rptTpl);
+            maxFieldMeasure = 1.0 * rptTpl[JV.NODE_CROSS_INFO][tabNodeName][tabMeasurePropName] * unitFactor;
+        } else {
+            maxFieldMeasure = measureForCal * rptTpl[JV.NODE_CROSS_INFO][tabNodeName][tabMeasurePropName] / JV.HUNDRED_PERCENT;
+        }
+        if (segments.length >= page) {
+            rst = segments[page - 1].length * maxFieldMeasure;
+        }
+    }
+    return rst;
+};
+
+export default JpcCrossTabHelper;

+ 132 - 0
report/src/core/helper/jpc_helper_discrete.ts

@@ -0,0 +1,132 @@
+import JV from '../jpc_value_define';
+import JE from '../jpc_rte';
+import JpcTextHelper from './jpc_helper_text';
+import JpcCommonOutputHelper from './jpc_helper_common_output';
+import JpcAreaHelper from './jpc_helper_area';
+import JpcFieldHelper from './jpc_helper_field';
+
+const JpcDiscreteHelper = {
+    outputDiscreteInfo: function (discreteArray: any[], bands: any, dataObj: any, unitFactor: number, pageStatus: boolean[], segIdx: number, multiCols: number, multiColIdx: number, $CURRENT_RPT: any, customizeCfg: any) {
+        let rst = [];
+        if (discreteArray && dataObj) {
+            for (let i = 0; i < discreteArray.length; i++) {
+                let band = bands[discreteArray[i][JV.PROP_BAND_NAME]];
+                if (band && pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true) {
+                    if (discreteArray[i][JV.PROP_TEXT]) {
+                        rst.push(JpcTextHelper.outputText(discreteArray[i][JV.PROP_TEXT], band, unitFactor, 1, 0, 1, 0, multiCols, multiColIdx));
+                    }
+                    if (discreteArray[i][JV.PROP_TEXTS]) {
+                        for (let j = 0; j < discreteArray[i][JV.PROP_TEXTS].length; j++) {
+                            rst.push(JpcTextHelper.outputText(discreteArray[i][JV.PROP_TEXTS][j], band, unitFactor, 1, 0, 1, 0, multiCols, multiColIdx));
+                        }
+                    }
+                    if (discreteArray[i][JV.PROP_DISCRETE_FIELDS]) {
+                        let flexiblePrecisionRefObj: any = null, flexibleRefField = null;
+                        for (let j = 0; j < discreteArray[i][JV.PROP_DISCRETE_FIELDS].length; j++) {
+                            let df = discreteArray[i][JV.PROP_DISCRETE_FIELDS][j];
+                            let value = "";
+                            if (df[JV.PROP_FIELD_ID]) {
+                                // let map_data_field = JE.F(df[JV.PROP_FIELD_ID], $CURRENT_RPT);
+                                let map_data_field = JE.F(df[JV.PROP_FIELD_ID], $CURRENT_RPT);
+                                if (map_data_field.DataSeq !== JV.BLANK_FIELD_INDEX) {
+                                    let data = dataObj[map_data_field.DataNodeName][map_data_field.DataSeq];
+                                    if (data && data.length > 0) {
+                                        if (data.length > segIdx) {
+                                            value = data[segIdx];
+                                        } else {
+                                            value = data[0];
+                                        }
+                                    }
+                                } else {
+                                    if (map_data_field[JV.PROP_AD_HOC_DATA] && map_data_field[JV.PROP_AD_HOC_DATA].length > 0) {
+                                        value = map_data_field[JV.PROP_AD_HOC_DATA][0]
+                                    } else value = "";
+                                }
+                                if (map_data_field && map_data_field[JV.PROP_PRECISION] && map_data_field.flexiblePrecisionRefObj) {
+                                    let pre_ref_data = null;
+                                    if (flexiblePrecisionRefObj === null) {
+                                        flexiblePrecisionRefObj = {};
+                                        flexibleRefField = JE.F(map_data_field[JV.PROP_PRECISION][JV.PROP_FLEXIBLE_REF_FILED_ID], $CURRENT_RPT);
+                                        pre_ref_data = dataObj[map_data_field.DataNodeName][flexibleRefField.DataSeq]; //这里的引用指标必须得存在,哪怕不显示,也要存放在指标集合中!
+                                        for (let decimalObj of map_data_field.flexiblePrecisionRefObj) {
+                                            flexiblePrecisionRefObj["refUnit_" + decimalObj.unit] = decimalObj.decimal;
+                                        }
+                                    }
+                                    JpcFieldHelper.resetFlexibleFormat(df, pre_ref_data, flexiblePrecisionRefObj, segIdx, customizeCfg);
+                                } else {
+                                    JpcFieldHelper.resetFormat(df, map_data_field, customizeCfg);
+                                }
+                            } else if (df[JV.PROP_PARAM_ID]) {
+                                let param = JE.P(df[JV.PROP_PARAM_ID], $CURRENT_RPT);
+                                value = param[JV.PROP_DFT_VALUE];
+                            }
+                            let item = JpcCommonOutputHelper.createCommonOutputWithoutDecorate(df, value, true);
+                            //position
+                            item[JV.PROP_AREA] = JpcAreaHelper.outputArea(df[JV.PROP_AREA], band, unitFactor, 1, 0, 1, 0, multiCols, multiColIdx, false, false);
+                            rst.push(item);
+                        }
+                    }
+                }
+            }
+        }
+        return rst;
+    },
+    outputPreviewDiscreteInfo: function (discreteArray: any[], bands: any, unitFactor: number, pageStatus: boolean[]) {
+        let rst = [];
+        for (let i = 0; i < discreteArray.length; i++) {
+            let band = bands[discreteArray[i][JV.PROP_BAND_NAME]];
+            if (band && pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]]) {
+                if (discreteArray[i][JV.PROP_TEXT]) {
+                    rst.push(JpcTextHelper.outputText(discreteArray[i][JV.PROP_TEXT], band, unitFactor, 1, 0, 1, 0, 1, 0));
+                }
+                if (discreteArray[i][JV.PROP_TEXTS]) {
+                    for (let j = 0; j < discreteArray[i][JV.PROP_TEXTS].length; j++) {
+                        rst.push(JpcTextHelper.outputText(discreteArray[i][JV.PROP_TEXTS][j], band, unitFactor, 1, 0, 1, 0, 1, 0));
+                    }
+                }
+                if (discreteArray[i][JV.PROP_DISCRETE_FIELDS]) {
+                    for (let j = 0; j < discreteArray[i][JV.PROP_DISCRETE_FIELDS].length; j++) {
+                        let df = discreteArray[i][JV.PROP_DISCRETE_FIELDS][j];
+                        let value = "";
+                        if (df[JV.PROP_DFT_VALUE]) {
+                            value = df[JV.PROP_DFT_VALUE];
+                        }
+                        let item = JpcCommonOutputHelper.createCommonOutputWithoutDecorate(df, value, false);
+                        //position
+                        item[JV.PROP_AREA] = JpcAreaHelper.outputArea(df[JV.PROP_AREA], band, unitFactor, 1, 0, 1, 0, 1, 0, false, false);
+                        rst.push(item);
+                    }
+                }
+
+            }
+        }
+        return rst;
+    },
+    outputPreviewSumInfoOutput: function (band: any, unitFactor: number, tab: any) {
+        let rst = [];
+        if (tab[JV.PROP_TEXT]) {
+            rst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXT], band, unitFactor, 1, 0, 1, 0, 1, 0));
+        }
+        if (tab[JV.PROP_TEXTS]) {
+            for (let j = 0; j < tab[JV.PROP_TEXTS].length; j++) {
+                rst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXTS][j], band, unitFactor, 1, 0, 1, 0, 1, 0));
+            }
+        }
+        if (tab[JV.PROP_SUM_FIELDS]) {
+            for (let i = 0; i < tab[JV.PROP_SUM_FIELDS].length; i++) {
+                let tab_field = tab[JV.PROP_SUM_FIELDS][i];
+                if (!(tab_field[JV.PROP_HIDDEN])) {
+                    // rst.push(me.outputTabField(band, tab_field, null, -1, -1, 1, 0, 1, 0, unitFactor, true, controls, 0));
+                    let value = "";
+                    let item = JpcCommonOutputHelper.createCommonOutputWithoutDecorate(tab_field, value, false);
+                    //position
+                    item[JV.PROP_AREA] = JpcAreaHelper.outputArea(tab_field[JV.PROP_AREA], band, unitFactor, 1, 0, 1, 0, 1, 0, false, false);
+                    rst.push(item);
+                }
+            }
+        }
+        return rst;
+    }
+}
+
+export default JpcDiscreteHelper;

+ 156 - 0
report/src/core/helper/jpc_helper_field.ts

@@ -0,0 +1,156 @@
+'use strict';
+
+import JV from '../jpc_value_define';
+import strUtil from '../../public/stringUtil';
+
+let JpcFieldHelper = {
+    getValue: function(dataField: any, valueIdx: number) {
+        let rst = "";
+        if (dataField && (dataField.length > valueIdx) && (valueIdx >= 0)) {
+            rst = dataField[valueIdx];
+        }
+        return rst;
+    },
+    setValue: function(dataField: any, valueIdx: number, newValue: any) {
+        if (dataField && (dataField.length > valueIdx) && (valueIdx >= 0)) {
+            dataField[valueIdx] = newValue;
+        }
+    },
+    resetSumFormat: function(ref_tab_fields: any[], sum_tab_field: any) {
+        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: any, map_field: any, customizeCfg: any) {
+        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++) {
+                if (idx === 0) {
+                    formatStrs.push(".");
+                }
+                formatStrs.push(ffStr);
+            }
+            if (tab_field[JV.PROP_FORMAT] && tab_field[JV.PROP_FORMAT].indexOf(",") >= 0) {
+                tab_field[JV.PROP_FORMAT] = "," + formatStrs.join("");
+            } else {
+                tab_field[JV.PROP_FORMAT] = formatStrs.join("");
+            }
+            map_field[JV.PROP_FORMAT] = tab_field[JV.PROP_FORMAT]; //这里顺手把format也赋给map_field,后期统计用得着
+            rst = true;
+        } else if (tab_field[JV.PROP_FORMAT] && tab_field[JV.PROP_FORMAT].indexOf("#") >= 0 && (customizeCfg && customizeCfg.fillZero)) {
+            tab_field[JV.PROP_FORMAT] = strUtil.replaceAll(tab_field[JV.PROP_FORMAT], '#', '0');
+            map_field[JV.PROP_FORMAT] = tab_field[JV.PROP_FORMAT];
+            rst = true;
+        }
+        return rst;
+    },
+    resetFlexibleFormat: function (tab_field: any, ref_field_data: any, flexiblePrecisionRefObj: any, valueIdx: number, customizeCfg: any) {
+        let precisionAmt = 2;
+        if (ref_field_data && flexiblePrecisionRefObj && valueIdx >= 0) {
+            precisionAmt = flexiblePrecisionRefObj["refUnit_" + ref_field_data[valueIdx]];
+            if (precisionAmt === null || precisionAmt === undefined) precisionAmt = flexiblePrecisionRefObj["refUnit_其他未列单位"];
+        }
+        let formatStrs = ["#."], ffStr = (customizeCfg && customizeCfg.fillZero)?"0":"#";
+        for (let idx = 0; idx < precisionAmt; idx++) {
+            formatStrs.push(ffStr);
+        }
+        if (tab_field[JV.PROP_FORMAT] && tab_field[JV.PROP_FORMAT].indexOf(",") >= 0) {
+            tab_field[JV.PROP_FORMAT] = "," + formatStrs.join("");
+        } else {
+            tab_field[JV.PROP_FORMAT] = formatStrs.join("");
+        }
+    },
+    decorateValue: function (cell: any, controls: any) {
+        if (controls) {
+            const showZero = controls[cell[JV.PROP_CONTROL]][JV.PROP_SHOW_ZERO];
+            if (showZero && showZero === 'F') {
+                const val = parseFloat(cell[JV.PROP_VALUE]);
+                if (val === 0) {
+                    cell[JV.PROP_VALUE] = '';
+                }
+            }
+        }
+    },
+    findAndPutDataFieldIdx: function (rptTpl: any, tab_fields: any[], rstFields: any, rstFieldsIdx: any, isEx: boolean) {
+        //通过FieldID找到相关映射指标的位置IDX并记录下来,方便后续引用
+        if (tab_fields) {
+            let DTL_STR = isEx?JV.NODE_DETAIL_FIELDS_EX:JV.NODE_DETAIL_FIELDS;
+            let detail_fields = rptTpl[JV.NODE_FIELD_MAP][DTL_STR];
+            for (let i = 0; i < tab_fields.length; i++) {
+                let isFounded = false;
+                for (let j = 0; j < detail_fields.length; j++) {
+                    if (tab_fields[i]["FieldID"] === detail_fields[j]["ID"]) {
+                        isFounded = true;
+                        if (rstFields) rstFields.push(tab_fields[i]);
+                        if (rstFieldsIdx) rstFieldsIdx.push(j);
+                        break;
+                    }
+                }
+                if (!isFounded) {
+                    if (rstFields) rstFields.push(tab_fields[i]);
+                    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);
+                        }
+                    }
+                }
+            }
+        }
+    },
+    findAutoHeightFieldIdx: function(rptTpl: any, tab_fields: any[], rstFieldsIdx: any[], isEx: boolean) {
+        if (tab_fields) {
+            let DTL_STR = isEx?JV.NODE_DETAIL_FIELDS_EX:JV.NODE_DETAIL_FIELDS;
+            let detail_fields = rptTpl[JV.NODE_FIELD_MAP][DTL_STR];
+            for (let i = 0; i < tab_fields.length; i++) {
+                if (tab_fields[i][JV.PROP_IS_AUTO_HEIGHT]) {
+                    for (let j = 0; j < detail_fields.length; j++) {
+                        if (tab_fields[i]["FieldID"] === detail_fields[j]["ID"]) {
+                            let item = [];
+                            item[0] = j;
+                            item[1] = tab_fields[i];
+                            rstFieldsIdx.push(item);
+                            break;
+                        }
+                    }
+                }
+            }
+            if (!isEx) {
+                let dis_detail_fields = rptTpl[JV.NODE_NO_MAPPING_FIELDS];
+                for (let i = 0; i < tab_fields.length; i++) {
+                    if (tab_fields[i][JV.PROP_IS_AUTO_HEIGHT]) {
+                        for (let j = 0; j < dis_detail_fields.length; j++) {
+                            if (tab_fields[i]["FieldID"] === dis_detail_fields[j]["ID"]) {
+                                let item = [];
+                                item[0] = JV.BLANK_FIELD_INDEX;
+                                item[1] = tab_fields[i];
+                                rstFieldsIdx.push(item);
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+};
+
+export default JpcFieldHelper;

+ 66 - 0
report/src/core/helper/jpc_helper_flow_tab.ts

@@ -0,0 +1,66 @@
+'use strict';
+
+import JV from '../jpc_value_define';
+import JpcCommonHelper from './jpc_helper_common';
+
+let JpcFlowTabHelper = {
+    getMaxRowsPerPage: function(bands: any, rptTpl: any, isEx: boolean) {
+        let rst = 1;
+        let FLOW_INFO_STR = (!isEx)?JV.NODE_FLOW_INFO:JV.NODE_FLOW_INFO_EX;
+        let tab = rptTpl[FLOW_INFO_STR][JV.NODE_FLOW_CONTENT];
+        let band = bands[tab[JV.PROP_BAND_NAME]];
+        if (band) {
+            let maxFieldMeasure = 1.0;
+            if (JV.CAL_TYPE_ABSTRACT === JpcCommonHelper.getPosCalculationType(tab[JV.PROP_CALCULATION])) {
+                let unitFactor = JpcCommonHelper.getUnitFactor(rptTpl);
+                maxFieldMeasure = 1.0 * rptTpl[FLOW_INFO_STR][JV.NODE_FLOW_CONTENT][JV.PROP_CMN_HEIGHT] * unitFactor;
+            } else {
+                maxFieldMeasure = (band.Bottom - band.Top) * rptTpl[FLOW_INFO_STR][JV.NODE_FLOW_CONTENT][JV.PROP_CMN_HEIGHT] / JV.HUNDRED_PERCENT;
+            }
+            rst = Math.floor((band.Bottom - band.Top) / maxFieldMeasure);
+        }
+        return rst;
+    },
+    getActualContentAreaHeight: function(bands: any, rptTpl: any, segments: any[], rowAmt: number, page: number, isEx: boolean) {
+        let rst = 1;
+        let FLOW_INFO_STR = (!isEx)?JV.NODE_FLOW_INFO:JV.NODE_FLOW_INFO_EX;
+        let tab = rptTpl[FLOW_INFO_STR][JV.NODE_FLOW_CONTENT];
+        let band = bands[tab[JV.PROP_BAND_NAME]];
+        if (band) {
+            let maxFieldMeasure = 1.0;
+            if (JV.CAL_TYPE_ABSTRACT === JpcCommonHelper.getPosCalculationType(tab[JV.PROP_CALCULATION])) {
+                let unitFactor = JpcCommonHelper.getUnitFactor(rptTpl);
+                maxFieldMeasure = 1.0 * rptTpl[FLOW_INFO_STR][JV.NODE_FLOW_CONTENT][JV.PROP_CMN_HEIGHT] * unitFactor;
+            } else {
+                maxFieldMeasure = (band.Bottom - band.Top) * rptTpl[FLOW_INFO_STR][JV.NODE_FLOW_CONTENT][JV.PROP_CMN_HEIGHT] / JV.HUNDRED_PERCENT;
+            }
+            if (segments.length >= page) {
+                rst = rowAmt * maxFieldMeasure;
+            }
+        }
+        return rst;
+    },
+    chkSegEnd: function (bands: any, rptTpl: any, segmentLength: number, preRec: number, pageRecAmt: number, isEx: boolean) {
+        let me = this, rst = false;
+        let remainAmt = preRec + pageRecAmt - segmentLength;
+        rst = me.hasEnoughSpace(rptTpl, bands, remainAmt, isEx);
+        return rst;
+    },
+    hasEnoughSpace: function (rptTpl: any, bands: any, remainAmt: number, isEx: boolean) {
+        if (remainAmt < 0) return false;
+        let rst = true, measurement = 1.0, douDiffForCompare = 0.00001;
+        let unitFactor = JpcCommonHelper.getUnitFactor(rptTpl);
+        let FLOW_INFO_STR = (!isEx)?JV.NODE_FLOW_INFO:JV.NODE_FLOW_INFO_EX;
+        let tab = rptTpl[FLOW_INFO_STR][JV.NODE_FLOW_CONTENT];
+        let band = bands[tab[JV.PROP_BAND_NAME]];
+        if (band !== null && band !== undefined) {
+            measurement = 1.0 * tab[JV.PROP_CMN_HEIGHT] * unitFactor;
+            let spareHeight = measurement * remainAmt;
+            let douH = band.Bottom - band.Top;
+            rst = ((spareHeight >= douH) || ((spareHeight - douH) <= douDiffForCompare));
+        }
+        return rst;
+    }
+};
+
+export default JpcFlowTabHelper;

+ 513 - 0
report/src/core/helper/jpc_helper_font_width.ts

@@ -0,0 +1,513 @@
+/**
+ * Created by Tony on 2019/12/12.
+ */
+
+const fontWidthMap = {
+    '宋体': {
+        '宽': {
+            '_6': 6,
+            '_7': 7,
+            '_8': 8,
+            '_9': 9,
+            '_10': 10,
+            '_11': 11,
+            '_12': 12,
+            '_13': 13,
+            '_14': 14,
+            '_15': 15,
+            '_16': 16,
+            '_17': 17,
+            '_18': 18,
+            '_19': 19,
+            '_20': 20,
+            '_21': 21,
+            '_22': 22,
+            '_23': 23,
+            '_24': 24,
+            '_25': 25,
+            '_26': 26,
+            '_27': 27,
+            '_28': 28,
+            '_29': 29,
+            '_30': 30,
+            '_31': 31,
+            '_32': 32,
+            '_33': 33,
+            '_34': 34,
+            '_35': 35,
+            '_36': 36,
+            '_37': 37,
+            '_38': 38,
+            '_39': 39,
+            '_40': 40,
+            '_41': 41,
+            '_42': 42,
+            '_43': 43,
+            '_44': 44,
+            '_45': 45,
+            '_46': 46,
+            '_47': 47,
+            '_48': 48,
+            '_49': 49,
+            '_50': 50,
+            '_51': 51,
+            '_52': 52,
+            '_53': 53,
+            '_54': 54,
+            '_55': 55,
+            '_56': 56,
+            '_57': 57,
+            '_58': 58,
+            '_59': 59,
+            '_60': 60,
+            '_61': 61,
+            '_62': 62,
+            '_63': 63,
+            '_64': 64
+        },
+        '窄': {
+            '_6': 3,
+            '_7': 3.5,
+            '_8': 4,
+            '_9': 4.5,
+            '_10': 5,
+            '_11': 5.5,
+            '_12': 6,
+            '_13': 6.5,
+            '_14': 7,
+            '_15': 7.5,
+            '_16': 8,
+            '_17': 8.5,
+            '_18': 9,
+            '_19': 9.5,
+            '_20': 10,
+            '_21': 10.5,
+            '_22': 11,
+            '_23': 11.5,
+            '_24': 12,
+            '_25': 12.5,
+            '_26': 13,
+            '_27': 13.5,
+            '_28': 14,
+            '_29': 14.5,
+            '_30': 15,
+            '_31': 15.5,
+            '_32': 16,
+            '_33': 16.5,
+            '_34': 17,
+            '_35': 17.5,
+            '_36': 18,
+            '_37': 18.5,
+            '_38': 19,
+            '_39': 19.5,
+            '_40': 20,
+            '_41': 20.5,
+            '_42': 21,
+            '_43': 21.5,
+            '_44': 22,
+            '_45': 22.5,
+            '_46': 23,
+            '_47': 23.5,
+            '_48': 24,
+            '_49': 24.5,
+            '_50': 25,
+            '_51': 25.5,
+            '_52': 26,
+            '_53': 26.5,
+            '_54': 27,
+            '_55': 27.5,
+            '_56': 28,
+            '_57': 28.5,
+            '_58': 29,
+            '_59': 29.5,
+            '_60': 30,
+            '_61': 30.5,
+            '_62': 31,
+            '_63': 31.5,
+            '_64': 32
+        }
+    },
+    '黑体': {
+        '宽': {
+            '_6': 6,
+            '_7': 7,
+            '_8': 8,
+            '_9': 9,
+            '_10': 10,
+            '_11': 11,
+            '_12': 12,
+            '_13': 13,
+            '_14': 14,
+            '_15': 15,
+            '_16': 16,
+            '_17': 17,
+            '_18': 18,
+            '_19': 19,
+            '_20': 20,
+            '_21': 21,
+            '_22': 22,
+            '_23': 23,
+            '_24': 24,
+            '_25': 25,
+            '_26': 26,
+            '_27': 27,
+            '_28': 28,
+            '_29': 29,
+            '_30': 30,
+            '_31': 31,
+            '_32': 32,
+            '_33': 33,
+            '_34': 34,
+            '_35': 35,
+            '_36': 36,
+            '_37': 37,
+            '_38': 38,
+            '_39': 39,
+            '_40': 40,
+            '_41': 41,
+            '_42': 42,
+            '_43': 43,
+            '_44': 44,
+            '_45': 45,
+            '_46': 46,
+            '_47': 47,
+            '_48': 48,
+            '_49': 49,
+            '_50': 50,
+            '_51': 51,
+            '_52': 52,
+            '_53': 53,
+            '_54': 54,
+            '_55': 55,
+            '_56': 56,
+            '_57': 57,
+            '_58': 58,
+            '_59': 59,
+            '_60': 60,
+            '_61': 61,
+            '_62': 62,
+            '_63': 63,
+            '_64': 64
+        },
+        '窄': {
+            '_6': 3,
+            '_7': 3.5,
+            '_8': 4,
+            '_9': 4.5,
+            '_10': 5,
+            '_11': 5.5,
+            '_12': 6,
+            '_13': 6.5,
+            '_14': 7,
+            '_15': 7.5,
+            '_16': 8,
+            '_17': 8.5,
+            '_18': 9,
+            '_19': 9.5,
+            '_20': 10,
+            '_21': 10.5,
+            '_22': 11,
+            '_23': 11.5,
+            '_24': 12,
+            '_25': 12.5,
+            '_26': 13,
+            '_27': 13.5,
+            '_28': 14,
+            '_29': 14.5,
+            '_30': 15,
+            '_31': 15.5,
+            '_32': 16,
+            '_33': 16.5,
+            '_34': 17,
+            '_35': 17.5,
+            '_36': 18,
+            '_37': 18.5,
+            '_38': 19,
+            '_39': 19.5,
+            '_40': 20,
+            '_41': 20.5,
+            '_42': 21,
+            '_43': 21.5,
+            '_44': 22,
+            '_45': 22.5,
+            '_46': 23,
+            '_47': 23.5,
+            '_48': 24,
+            '_49': 24.5,
+            '_50': 25,
+            '_51': 25.5,
+            '_52': 26,
+            '_53': 26.5,
+            '_54': 27,
+            '_55': 27.5,
+            '_56': 28,
+            '_57': 28.5,
+            '_58': 29,
+            '_59': 29.5,
+            '_60': 30,
+            '_61': 30.5,
+            '_62': 31,
+            '_63': 31.5,
+            '_64': 32
+        }
+    },
+    '楷体': {
+        '宽': {
+            '_6': 6,
+            '_7': 7,
+            '_8': 8,
+            '_9': 9,
+            '_10': 10,
+            '_11': 11,
+            '_12': 12,
+            '_13': 13,
+            '_14': 14,
+            '_15': 15,
+            '_16': 16,
+            '_17': 17,
+            '_18': 18,
+            '_19': 19,
+            '_20': 20,
+            '_21': 21,
+            '_22': 22,
+            '_23': 23,
+            '_24': 24,
+            '_25': 25,
+            '_26': 26,
+            '_27': 27,
+            '_28': 28,
+            '_29': 29,
+            '_30': 30,
+            '_31': 31,
+            '_32': 32,
+            '_33': 33,
+            '_34': 34,
+            '_35': 35,
+            '_36': 36,
+            '_37': 37,
+            '_38': 38,
+            '_39': 39,
+            '_40': 40,
+            '_41': 41,
+            '_42': 42,
+            '_43': 43,
+            '_44': 44,
+            '_45': 45,
+            '_46': 46,
+            '_47': 47,
+            '_48': 48,
+            '_49': 49,
+            '_50': 50,
+            '_51': 51,
+            '_52': 52,
+            '_53': 53,
+            '_54': 54,
+            '_55': 55,
+            '_56': 56,
+            '_57': 57,
+            '_58': 58,
+            '_59': 59,
+            '_60': 60,
+            '_61': 61,
+            '_62': 62,
+            '_63': 63,
+            '_64': 64
+        },
+        '窄': {
+            '_6': 3,
+            '_7': 3.5,
+            '_8': 4,
+            '_9': 4.5,
+            '_10': 5,
+            '_11': 5.5,
+            '_12': 6,
+            '_13': 6.5,
+            '_14': 7,
+            '_15': 7.5,
+            '_16': 8,
+            '_17': 8.5,
+            '_18': 9,
+            '_19': 9.5,
+            '_20': 10,
+            '_21': 10.5,
+            '_22': 11,
+            '_23': 11.5,
+            '_24': 12,
+            '_25': 12.5,
+            '_26': 13,
+            '_27': 13.5,
+            '_28': 14,
+            '_29': 14.5,
+            '_30': 15,
+            '_31': 15.5,
+            '_32': 16,
+            '_33': 16.5,
+            '_34': 17,
+            '_35': 17.5,
+            '_36': 18,
+            '_37': 18.5,
+            '_38': 19,
+            '_39': 19.5,
+            '_40': 20,
+            '_41': 20.5,
+            '_42': 21,
+            '_43': 21.5,
+            '_44': 22,
+            '_45': 22.5,
+            '_46': 23,
+            '_47': 23.5,
+            '_48': 24,
+            '_49': 24.5,
+            '_50': 25,
+            '_51': 25.5,
+            '_52': 26,
+            '_53': 26.5,
+            '_54': 27,
+            '_55': 27.5,
+            '_56': 28,
+            '_57': 28.5,
+            '_58': 29,
+            '_59': 29.5,
+            '_60': 30,
+            '_61': 30.5,
+            '_62': 31,
+            '_63': 31.5,
+            '_64': 32
+        }
+    },
+    'Arial': {
+        '宽': {
+            '_6': 6,
+            '_7': 7,
+            '_8': 8,
+            '_9': 9,
+            '_10': 10,
+            '_11': 11,
+            '_12': 12,
+            '_13': 13,
+            '_14': 14,
+            '_15': 15,
+            '_16': 16,
+            '_17': 17,
+            '_18': 18,
+            '_19': 19,
+            '_20': 20,
+            '_21': 21,
+            '_22': 22,
+            '_23': 23,
+            '_24': 24,
+            '_25': 25,
+            '_26': 26,
+            '_27': 27,
+            '_28': 28,
+            '_29': 29,
+            '_30': 30,
+            '_31': 31,
+            '_32': 32,
+            '_33': 33,
+            '_34': 34,
+            '_35': 35,
+            '_36': 36,
+            '_37': 37,
+            '_38': 38,
+            '_39': 39,
+            '_40': 40,
+            '_41': 41,
+            '_42': 42,
+            '_43': 43,
+            '_44': 44,
+            '_45': 45,
+            '_46': 46,
+            '_47': 47,
+            '_48': 48,
+            '_49': 49,
+            '_50': 50,
+            '_51': 51,
+            '_52': 52,
+            '_53': 53,
+            '_54': 54,
+            '_55': 55,
+            '_56': 56,
+            '_57': 57,
+            '_58': 58,
+            '_59': 59,
+            '_60': 60,
+            '_61': 61,
+            '_62': 62,
+            '_63': 63,
+            '_64': 64
+        },
+        '窄': {
+            '_6': 3.3399999141693115,
+            '_7': 3.8899998664855957,
+            '_8': 4.449999809265137,
+            '_9': 5.009999752044678,
+            '_10': 5.559999942779541,
+            '_11': 6.119999885559082,
+            '_12': 6.670000076293945,
+            '_13': 7.230000019073486,
+            '_14': 7.789999961853027,
+            '_15': 8.34000015258789,
+            '_16': 8.899999618530273,
+            '_17': 9.449999809265136,
+            '_18': 10.010000228881836,
+            '_19': 10.569999694824218,
+            '_20': 11.119999885559082,
+            '_21': 11.679999351501464,
+            '_22': 12.239999771118164,
+            '_23': 12.789999961853027,
+            '_24': 13.34999942779541,
+            '_25': 13.899999618530273,
+            '_26': 14.460000038146972,
+            '_27': 15.019999504089355,
+            '_28': 15.569999694824218,
+            '_29': 16.1299991607666,
+            '_30': 16.68000030517578,
+            '_31': 17.239999771118164,
+            '_32': 17.799999237060547,
+            '_33': 18.350000381469726,
+            '_34': 18.90999984741211,
+            '_35': 19.469999313354492,
+            '_36': 20.020000457763672,
+            '_37': 20.579999923706054,
+            '_38': 21.1299991607666,
+            '_39': 21.689998626708984,
+            '_40': 22.25,
+            '_41': 22.799999237060547,
+            '_42': 23.35999870300293,
+            '_43': 23.90999984741211,
+            '_44': 24.469999313354492,
+            '_45': 25.029998779296875,
+            '_46': 25.579999923706054,
+            '_47': 26.139999389648437,
+            '_48': 26.69999885559082,
+            '_49': 27.25,
+            '_50': 27.809999465942383,
+            '_51': 28.35999870300293,
+            '_52': 28.920000076293945,
+            '_53': 29.479999542236328,
+            '_54': 30.029998779296875,
+            '_55': 30.59000015258789,
+            '_56': 31.139999389648437,
+            '_57': 31.69999885559082,
+            '_58': 32.2599983215332,
+            '_59': 32.80999755859375,
+            '_60': 33.369998931884766,
+            '_61': 33.93000030517578,
+            '_62': 34.47999954223633,
+            '_63': 35.040000915527344,
+            '_64': 35.59000015258789
+        }
+    },
+    getFontWidth: function(fontName: string, fontHeight: number, typeStr: string) {
+        let rst = 12;
+        if ((fontWidthMap as any)[fontName]) {
+            rst = (fontWidthMap as any)[fontName][typeStr]['_' + fontHeight];
+        } else {
+            rst = (fontWidthMap as any)['宋体'][typeStr]['_' + fontHeight];
+        }
+        return rst;
+    }
+};
+
+export default fontWidthMap;

+ 22 - 0
report/src/core/helper/jpc_helper_text.ts

@@ -0,0 +1,22 @@
+'use strict';
+
+import JV from '../jpc_value_define';
+import JpcCommonOutputHelper from './jpc_helper_common_output';
+import JpcAreaHelper from './jpc_helper_area';
+
+let JpcTextHelper = {
+    outputText: function (textNode: any, band: any, unitFactor: number, rows: number, rowIdx: number, cols: number, colIdx: number, multiCols: number, multiColIdx: number) {
+        let rst = JpcCommonOutputHelper.createCommonOutput(textNode, textNode[JV.PROP_LABEL], null);
+        //position
+        rst[JV.PROP_AREA] = JpcAreaHelper.outputArea(textNode[JV.PROP_AREA], band, unitFactor, rows, rowIdx, cols, colIdx, multiCols, multiColIdx, false, false);
+        return rst;
+    },
+    outputDirectValue: function (textNode: any, value: any, band: any, unitFactor: number, rows: number, rowIdx: number, cols: number, colIdx: number, multiCols: number, multiColIdx: number) {
+        let rst = JpcCommonOutputHelper.createCommonOutput(textNode, value, null);
+        //position
+        rst[JV.PROP_AREA] = JpcAreaHelper.outputArea(textNode[JV.PROP_AREA], band, unitFactor, rows, rowIdx, cols, colIdx, multiCols, multiColIdx, false, false);
+        return rst;
+    }
+};
+
+export default JpcTextHelper;

+ 57 - 0
report/src/core/jpc_band.ts

@@ -0,0 +1,57 @@
+'use strict';
+
+import JV from './jpc_value_define';
+import JpcCommonHelper from './helper/jpc_helper_common';
+let JpcBandHelper = require('./helper/jpc_helper_band');
+
+const JpcBand = {
+    createNew(rptTpl: any, defProperties: any) {
+        let me = this;
+        let JpcBandResult: any = {};
+        if (rptTpl[JV.NODE_BAND_COLLECTION]) {
+            for (let i = 0; i < rptTpl[JV.NODE_BAND_COLLECTION].length; i++) {
+                me.createSingle(rptTpl[JV.NODE_BAND_COLLECTION][i], JpcBandResult, rptTpl, defProperties);
+            }
+        }
+        return JpcBandResult;
+    },
+    createSingle(bandNode: any, parentObj: any, rptTpl: any, defProperties: any) {
+        let me = this;
+        if (bandNode && bandNode[JV.BAND_PROP_NAME]) {
+            let item = {
+                Left: 0, 
+                Right: 0, 
+                Top: 0, 
+                Bottom: 0,
+                [JV.BAND_PROP_STYLE]: JpcCommonHelper.getStyle(bandNode[JV.BAND_PROP_STYLE], defProperties.styles, null),
+                [JV.BAND_PROP_CONTROL]: JpcCommonHelper.getControl(bandNode[JV.BAND_PROP_CONTROL], defProperties.ctrls, null),
+                [JV.BAND_PROP_HEIGHT]: (bandNode[JV.BAND_PROP_HEIGHT]) ? (1.0 * bandNode[JV.BAND_PROP_HEIGHT]) : 0,
+                [JV.BAND_PROP_WIDTH]: (bandNode[JV.BAND_PROP_WIDTH]) ? 1.0 * bandNode[JV.BAND_PROP_WIDTH] : 0,
+                [JV.BAND_PROP_DISPLAY_TYPE]: JpcBandHelper.getBandTypeValByString(bandNode[JV.BAND_PROP_DISPLAY_TYPE]),
+                [JV.BAND_PROP_ALIGNMENT]: JpcCommonHelper.getLayoutAlignment(bandNode[JV.BAND_PROP_ALIGNMENT]),
+                [JV.PROP_CALCULATION]: JpcCommonHelper.getPosCalculationType(bandNode[JV.PROP_CALCULATION]),
+            };
+            if (bandNode.hasOwnProperty(JV.PROP_BAND_NORMAL_ONLY)) {
+                item[JV.PROP_BAND_NORMAL_ONLY] = JpcCommonHelper.getBoolean(bandNode[JV.PROP_BAND_NORMAL_ONLY]);
+            }
+            if (bandNode.hasOwnProperty(JV.PROP_BAND_EX_ONLY)) {
+                item[JV.PROP_BAND_EX_ONLY] = JpcCommonHelper.getBoolean(bandNode[JV.PROP_BAND_EX_ONLY]);
+            }
+
+            if (bandNode[JV.BAND_PROP_MERGE_BORDER]) {
+                item[JV.BAND_PROP_MERGE_BORDER] = bandNode[JV.BAND_PROP_MERGE_BORDER];
+            }
+            if (bandNode[JV.BAND_PROP_SUB_BANDS]) {
+                for (let i = 0; i < bandNode[JV.BAND_PROP_SUB_BANDS].length; i++) {
+                    me.createSingle(bandNode[JV.BAND_PROP_SUB_BANDS][i], parentObj, rptTpl, defProperties);
+                }
+            }
+            parentObj[bandNode[JV.BAND_PROP_NAME]] = item;
+            if (item[JV.BAND_PROP_MERGE_BORDER] !== null && item[JV.BAND_PROP_MERGE_BORDER] !== undefined && item[JV.BAND_PROP_MERGE_BORDER] === 'T') {
+                parentObj[JV.BAND_PROP_MERGE_BAND] = item;
+            }
+        }
+    }
+};
+
+export default JpcBand;

+ 165 - 0
report/src/core/jpc_bill_tab.ts

@@ -0,0 +1,165 @@
+import JV from './jpc_value_define';
+import JE from './jpc_rte';
+import JpcFieldHelper from './helper/jpc_helper_field';
+import JpcBandHelper from './helper/jpc_helper_band';
+import JpcCommonHelper from './helper/jpc_helper_common';
+import JpcDiscreteHelper from './helper/jpc_helper_discrete';
+import JpcTextHelper from './helper/jpc_helper_text';
+import JpcCommonOutputHelper from './helper/jpc_helper_common_output';
+import JpcAreaHelper from './helper/jpc_helper_area';
+
+class JpcBillTabSrvClass{
+    disp_fields_idx: any[];
+
+    constructor() {
+        this.disp_fields_idx = []; // typescript的class属性初始化都要放constructor里,不然总报错
+    };
+
+    sorting(rptTpl: any){
+        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_BILL_INFO][JV.NODE_BILL_CONTENT][JV.PROP_BILL_FIELDS], [], this.disp_fields_idx, false);
+    };
+
+    paging(rptTpl: any, dataObj: any) {
+        let rst = 0;
+        function _getDataLength(fields_str: string) {
+            let dataFields = dataObj[fields_str];
+            if (dataFields && dataFields.length > 0) {
+                rst = dataFields[0].length;
+            }
+        }
+        if (rptTpl[JV.NODE_FIELD_MAP]) {
+            if (rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS]) {
+                _getDataLength(JV.DATA_DETAIL_DATA);
+            } else if (rptTpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS]) {
+                _getDataLength(JV.DATA_MASTER_DATA);
+            } else if (rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DISCRETE_FIELDS]) {
+                _getDataLength(JV.DATA_DISCRETE_DATA);
+            } else if (rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DISCRETE_PARAMS]) {
+                rst = 1;
+            }
+        }
+        return rst;
+    };
+
+    outputAsPreviewPage(rptTpl: any, bands: any, controls: any, $CURRENT_RPT: any) {
+        let me = this, rst: any[] = [], vIdx = [];
+        //只预览第一页的数据
+        let pageStatus = [true, true, false, true, false, false, false, false];
+        JpcBandHelper.setBandArea(bands, rptTpl, pageStatus, true, false);
+        let unitFactor = JpcCommonHelper.getUnitFactor(rptTpl);
+        rst = rst.concat(me.outputPreviewContent(rptTpl, bands, unitFactor, controls, pageStatus));
+        rst = rst.concat(JpcDiscreteHelper.outputPreviewDiscreteInfo(rptTpl[JV.NODE_BILL_INFO][JV.NODE_DISCRETE_INFO], bands, unitFactor, pageStatus));
+        return rst;
+    };
+
+    outputAsSimpleJSONPage(rptTpl: any, dataObj: any, page: number, bands: any, controls: any, $CURRENT_RPT: any, customizeCfg: any) {
+        let me = this, rst: any[] = [], tabRstLst = [];
+        //1 calculate the band position
+        let pageStatus: boolean[] = [true, false, false, false, false, false, false, false];
+        JpcBandHelper.setBandArea(bands, rptTpl, pageStatus, false, false);
+        //2. start to output detail-part
+        let unitFactor = JpcCommonHelper.getUnitFactor(rptTpl);
+        //2.1 output content
+        tabRstLst.push(me.outputContent(rptTpl, dataObj, page, bands, unitFactor, controls, pageStatus, $CURRENT_RPT, customizeCfg));
+        //2.2 output discrete
+        tabRstLst.push(JpcDiscreteHelper.outputDiscreteInfo(rptTpl[JV.NODE_BILL_INFO][JV.NODE_DISCRETE_INFO], bands, dataObj, unitFactor, pageStatus, page - 1, 1, 0, $CURRENT_RPT, customizeCfg));
+        for (let i = 0; i < tabRstLst.length; i++) {
+            rst = rst.concat(tabRstLst[i]);
+            tabRstLst[i] = null;
+        }
+        return rst;
+    };
+
+    outputPreviewContent(rptTpl: any, bands: any, unitFactor: number, controls: any, pageStatus: boolean[]) {
+        let rst: any[] = [];
+        let tab = rptTpl[JV.NODE_BILL_INFO][JV.NODE_BILL_CONTENT];
+        let band = bands[tab[JV.PROP_BAND_NAME]];
+        if (band) {
+            if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]]) {
+                let tab_fields = tab[JV.PROP_BILL_FIELDS];
+                for (let i = 0; i < tab_fields.length; i++) {
+                    let tab_field = tab_fields[i];
+                    if (!(tab_field[JV.PROP_HIDDEN])) {
+                        let cellItem = JpcCommonOutputHelper.createCommonOutput(tab_field, "", controls);
+                        cellItem[JV.PROP_AREA] = JpcAreaHelper.outputArea(tab_field[JV.PROP_AREA], band, unitFactor, 1, 0, 1, 0, 1, 0, false, false);
+                        rst.push(cellItem);
+                    }
+                }
+                if (tab[JV.PROP_TEXT]) {
+                    rst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXT], band, unitFactor, 1, 0, 1, 0, 1, 0));
+                }
+                if (tab[JV.PROP_TEXTS]) {
+                    for (let j = 0; j < tab[JV.PROP_TEXTS].length; j++) {
+                        rst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXTS][j], band, unitFactor, 1, 0, 1, 0, 1, 0));
+                    }
+                }
+                if (tab[JV.NODE_DISCRETE_INFO]) {
+                    rst = rst.concat(JpcDiscreteHelper.outputPreviewDiscreteInfo(tab[JV.NODE_DISCRETE_INFO], bands, unitFactor, pageStatus));
+                }
+            }
+        }
+        return rst;
+    };
+
+    outputContent(rptTpl: any, dataObj: any, page: number, bands: any, unitFactor: number, controls: any, pageStatus: boolean[], $CURRENT_RPT: any, customizeCfg: any) {
+        let me = this, rst: any[] = [];
+        let tab = rptTpl[JV.NODE_BILL_INFO][JV.NODE_BILL_CONTENT];
+        let band = bands[tab[JV.PROP_BAND_NAME]];
+        if (band) {
+            if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]]) {
+                let tab_fields = tab[JV.PROP_BILL_FIELDS];
+                let data_details = null;
+                if (rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS]) {
+                    data_details = dataObj[JV.DATA_DETAIL_DATA];
+                } else if (rptTpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS]) {
+                    data_details = dataObj[JV.DATA_MASTER_DATA];
+                }
+                let flexiblePrecisionRefObj: any = null, flexibleRefField = null, precision_ref_data = null;
+                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 && (typeof me.disp_fields_idx[i] !== 'object')) {
+                        data_field = data_details[me.disp_fields_idx[i]];
+                    } else {
+                        if (map_data_field) {
+                            data_field = map_data_field[JV.PROP_AD_HOC_DATA];
+                        }
+                    }
+                    if (map_data_field && map_data_field[JV.PROP_PRECISION] && map_data_field.flexiblePrecisionRefObj) {
+                        if (flexiblePrecisionRefObj === null) {
+                            flexiblePrecisionRefObj = {};
+                            flexibleRefField = JE.F(map_data_field[JV.PROP_PRECISION][JV.PROP_FLEXIBLE_REF_FILED_ID], $CURRENT_RPT);
+                            precision_ref_data = dataObj[map_data_field.DataNodeName][flexibleRefField.DataSeq];
+                            for (let decimalObj of map_data_field.flexiblePrecisionRefObj) {
+                                flexiblePrecisionRefObj["refUnit_" + decimalObj.unit] = decimalObj.decimal;
+                            }
+                        }
+                        JpcFieldHelper.resetFlexibleFormat(tab_field, precision_ref_data, flexiblePrecisionRefObj, page - 1, customizeCfg);
+                    } else {
+                        if (page === 1) JpcFieldHelper.resetFormat(tab_field, map_data_field, customizeCfg);
+                    }
+                    if (!(tab_field[JV.PROP_HIDDEN])) {
+                        let val = JpcFieldHelper.getValue(data_field, page - 1);
+                        let cellItem = JpcCommonOutputHelper.createCommonOutput(tab_field, val, controls);
+                        cellItem[JV.PROP_AREA] = JpcAreaHelper.outputArea(tab_field[JV.PROP_AREA], band, unitFactor, 1, 0, 1, 0, 1, 0, false, false);
+                        rst.push(cellItem);
+                    }
+                }
+                if (tab[JV.PROP_TEXT]) {
+                    rst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXT], band, unitFactor, 1, 0, 1, 0, 1, 0));
+                }
+                if (tab[JV.PROP_TEXTS]) {
+                    for (let j = 0; j < tab[JV.PROP_TEXTS].length; j++) {
+                        rst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXTS][j], band, unitFactor, 1, 0, 1, 0, 1, 0));
+                    }
+                }
+                if (tab[JV.NODE_DISCRETE_INFO]) {
+                    rst = rst.concat(JpcDiscreteHelper.outputDiscreteInfo(tab[JV.NODE_DISCRETE_INFO], bands, dataObj, unitFactor, pageStatus, page - 1, 1, 0, $CURRENT_RPT, customizeCfg));
+                }
+            }
+        }
+        return rst;
+    }
+}
+
+export default JpcBillTabSrvClass;

+ 832 - 0
report/src/core/jpc_cross_tab.ts

@@ -0,0 +1,832 @@
+'use strict';
+
+import JV from './jpc_value_define';
+import JE from './jpc_rte';
+import JpcFieldHelper from './helper/jpc_helper_field';
+import JpcBandHelper from './helper/jpc_helper_band';
+import JpcBand from './jpc_band';
+import JpcCrossTabHelper from './helper/jpc_helper_cross_tab';
+import JpcCommonHelper from './helper/jpc_helper_common';
+import JpcDiscreteHelper from './helper/jpc_helper_discrete';
+import JpcTextHelper from './helper/jpc_helper_text';
+import JpcCommonOutputHelper from './helper/jpc_helper_common_output';
+import JpcAreaHelper from './helper/jpc_helper_area';
+
+class JpcCrossTabClass {
+    dispValueIdxLst_Row: any[];
+    dispValueIdxLst_Col: any[];
+    dispValueIdxLst_Content: any[];
+    dispSerialIdxLst_Row: any[];
+    col_sum_fields_idx: any[];
+    col_sum_fields_value_total: any[];
+    dispSumValueLst_Col: any[];
+    page_seg_map: any[];
+    row_fields_idx: any[];
+    row_fields_adhoc_idx: any[];
+    col_fields_idx: any[];
+    content_fields_idx: any[];
+    row_extension_fields_idx: any[];
+    row_sum_extension_fields_idx: any[];
+    sortedRowSequence: any[];
+    sortedColSequence: any[];
+    sortedContentSequence: any[];
+    crsOrient: number;
+    pageStatusLst: any[];
+    paging_option: string;
+    
+    constructor() {
+        this.dispValueIdxLst_Row = [];
+        this.dispValueIdxLst_Col = [];
+        this.dispValueIdxLst_Content = [];
+        this.dispSerialIdxLst_Row = [];
+        this.col_sum_fields_idx = [];
+        this.col_sum_fields_value_total = [];
+        this.dispSumValueLst_Col = [];
+        this.page_seg_map = [];
+        this.row_fields_idx = [];
+        this.row_fields_adhoc_idx = [];
+        this.col_fields_idx = [];
+        this.content_fields_idx = [];
+        this.row_extension_fields_idx = [];
+        this.row_sum_extension_fields_idx = [];
+        this.sortedRowSequence = [];
+        this.sortedColSequence = [];
+        this.sortedContentSequence = [];
+        this.crsOrient = JV.PAGE_ORIENTATION_V_FIRST;
+        this.pageStatusLst = [];
+        this.paging_option = JV.PAGING_OPTION_NORMAL;
+    };
+    sorting(rptTpl: any, dataObj: any, dataSeq: any[], $CURRENT_RPT: any) {
+        let me = this;
+        //IMPORTANT: the data should be sorted in SQL/NoSQL level!
+        me.sortedRowSequence = _SortAndOptimize(rptTpl, dataObj, dataSeq, JV.NODE_CROSS_ROW, me.row_fields_idx, $CURRENT_RPT);
+        _SortAndOptimize(rptTpl, dataObj, dataSeq, JV.NODE_CROSS_ROW_AD_HOC, me.row_fields_adhoc_idx, $CURRENT_RPT);
+        me.sortedColSequence = _SortAndOptimize(rptTpl, dataObj, dataSeq, JV.NODE_CROSS_COL, me.col_fields_idx, $CURRENT_RPT);
+        me.sortedContentSequence = _SortForDisplayContent(rptTpl, me.sortedRowSequence, me.sortedColSequence, me.content_fields_idx);
+        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_COL_SUM][JV.PROP_CROSS_FIELDS], null, me.col_sum_fields_idx, false);
+        //pre-sum the data(for col sum display)
+        let data_details = dataObj[JV.DATA_DETAIL_DATA],
+            data_fields = [];
+        for (let i = 0; i < me.col_sum_fields_idx.length; i++) {
+            let data_field = null;
+            if (typeof me.col_sum_fields_idx[i] === 'object') {
+                let exField = JE.F(me.col_sum_fields_idx[i][JV.PROP_ID], $CURRENT_RPT);
+                if (exField) {
+                    data_field = exField[JV.PROP_AD_HOC_DATA];
+                }
+            } else {
+                data_field = data_details[me.col_sum_fields_idx[i]];
+            }
+            data_fields.push(data_field);
+        }
+        for (let i = 0; i < me.sortedRowSequence.length; i++) { //seg level
+            if (me.sortedRowSequence[i].length > 0) {
+                me.col_sum_fields_value_total.push([]);
+                for (let j = 0; j < me.sortedRowSequence[i].length; j++) {
+                    let rowGrandTotal = [];
+                    for (let di = 0; di < data_fields.length; di++) {
+                        rowGrandTotal.push(0.0);
+                        for (let k = 0; k < me.sortedRowSequence[i][j].length; k++) {
+                            // 3. start to sum
+                            let vTtl = parseFloat(JpcFieldHelper.getValue(data_fields[di], me.sortedRowSequence[i][j][k]));
+                            if (isNaN(vTtl)) {
+                                vTtl = 0;
+                            }
+                            rowGrandTotal[di] = rowGrandTotal[di] + vTtl;
+                        }
+                    }
+                    me.col_sum_fields_value_total[i].push(rowGrandTotal);
+                }
+            }
+
+        }
+    };
+    preSetupPages(rptTpl: any, defProperties: any, option: any) {
+        let me = this, rst = 0;
+        me.paging_option = option||JV.PAGING_OPTION_NORMAL;
+        //1. original initialize
+        let maxRowRec = 1, maxColRec = 1, counterRowRec = 0, counterColRec = 0, pageIdx = 0, segCnt = me.sortedContentSequence.length;
+        let pageStatus: boolean[] = [true, true, false, true, false, false, false, false];
+        //2. calculate the page info one by one
+        let bands = JpcBand.createNew(rptTpl, defProperties);
+        function private_resetBandArea() {
+            JpcBandHelper.setBandArea(bands, rptTpl, pageStatus, false, false);
+            maxRowRec = JpcCrossTabHelper.getMaxRowsPerPage(bands, rptTpl);
+            maxColRec = JpcCrossTabHelper.getMaxColsPerPage(bands, rptTpl);
+        }
+        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_EXT][JV.PROP_CROSS_FIELDS], null, me.row_extension_fields_idx, false);
+        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_SUM_EXT][JV.PROP_CROSS_FIELDS], null, me.row_sum_extension_fields_idx, false);
+        if (me.paging_option === JV.PAGING_OPTION_INFINITY) {
+            // 交叉表暂时不支持无限拓展
+        } else {
+            for (let segIdx = 0; segIdx < segCnt; segIdx++) {
+                //2.1. seg level initialize
+                private_resetBandArea();
+                let orgMaxRowRec = maxRowRec, orgMaxColRec = maxColRec;
+                let rowSplitCnt = Math.ceil(1.0 * me.sortedRowSequence[segIdx].length / maxRowRec);
+                let colSplitCnt = Math.ceil(1.0 * me.sortedColSequence[segIdx].length / maxColRec);
+                pageStatus[JV.STATUS_CROSS_ROW_END] = true;
+                private_resetBandArea();
+                let hasAdHocRow = !JpcCrossTabHelper.chkTabEnd(JV.NODE_CROSS_ROW_SUM, rptTpl, bands, me.sortedRowSequence, segIdx, (rowSplitCnt - 1) * orgMaxRowRec, maxRowRec);
+                if (hasAdHocRow) {
+                    hasAdHocRow = !JpcCrossTabHelper.chkTabEnd(JV.NODE_CROSS_ROW_EXT, rptTpl, bands, me.sortedRowSequence, segIdx, (rowSplitCnt - 1) * orgMaxRowRec, maxRowRec);
+                }
+                pageStatus[JV.STATUS_CROSS_ROW_END] = false;
+                pageStatus[JV.STATUS_CROSS_COL_END] = true;
+                private_resetBandArea();
+                let hasAdHocCol = !JpcCrossTabHelper.chkTabEnd(JV.NODE_CROSS_COL_SUM, rptTpl, bands, me.sortedColSequence, segIdx, (colSplitCnt - 1) * orgMaxColRec, maxColRec);
+                pageStatus[JV.STATUS_CROSS_COL_END] = false;
+                private_resetBandArea();
+                if (hasAdHocRow) rowSplitCnt++;
+                if (hasAdHocCol) colSplitCnt++;
+                //2.2
+                if (rptTpl[JV.NODE_MAIN_INFO][JV.NODE_PAGE_INFO][JV.PROP_CROSS_DISPLAY_ORDER] === JV.PAGE_ORIENTATION_H_FIRST) {
+                    for (let rowIdx = 0; rowIdx < rowSplitCnt; rowIdx++) {
+                        pageStatus[JV.STATUS_CROSS_ROW_END] = (rowIdx === (rowSplitCnt - 1));
+                        private_resetBandArea();
+                        counterRowRec = orgMaxRowRec * rowIdx;
+                        let currentSortedRowSequence: any = me.sortedRowSequence;
+                        let currentSortedContentSequence: any = me.sortedContentSequence;
+                        if (hasAdHocRow && rowIdx === (rowSplitCnt - 1)) {
+                            currentSortedRowSequence = null;
+                            currentSortedContentSequence = null;
+                            counterRowRec = 0;
+                        }
+                        for (let colIdx = 0; colIdx < colSplitCnt; colIdx++) {
+                            pageStatus[JV.STATUS_CROSS_COL_END] = (colIdx === (colSplitCnt - 1));
+                            private_resetBandArea();
+                            counterColRec = orgMaxColRec * colIdx;
+                            let currentSortedColSequence: any = me.sortedColSequence;
+                            if (hasAdHocCol && colIdx === (colSplitCnt - 1)) {
+                                currentSortedColSequence = null;
+                                currentSortedContentSequence = null;
+                                counterColRec = 0;
+                            }
+                            me.pageStatusLst.push(pageStatus.slice(0));
+                            pageIdx++;
+                            _addTabValue(me.dispValueIdxLst_Row, currentSortedRowSequence, segIdx, counterRowRec, maxRowRec, me.dispSerialIdxLst_Row, me.col_sum_fields_value_total, me.dispSumValueLst_Col);
+                            _addTabValue(me.dispValueIdxLst_Col, currentSortedColSequence, segIdx, counterColRec, maxColRec, null, null, null);
+                            _addContentValue(me.dispValueIdxLst_Content, currentSortedContentSequence, segIdx, counterRowRec, maxRowRec, counterColRec, maxColRec, me.page_seg_map, pageIdx);
+                        }
+                    }
+                } else {
+                    for (let colIdx = 0; colIdx < colSplitCnt; colIdx++) {
+                        pageStatus[JV.STATUS_CROSS_COL_END] = (colIdx === (colSplitCnt - 1));
+                        private_resetBandArea();
+                        counterColRec = orgMaxColRec * colIdx;
+                        let currentSortedContentSequence: any = me.sortedContentSequence;
+                        let currentSortedColSequence: any = me.sortedColSequence;
+                        if (hasAdHocCol && colIdx === (colSplitCnt - 1)) {
+                            currentSortedColSequence = null;
+                            currentSortedContentSequence = null;
+                            counterColRec = 0;
+                        }
+                        for (let rowIdx = 0; rowIdx < rowSplitCnt; rowIdx++) {
+                            pageStatus[JV.STATUS_CROSS_ROW_END] = (rowIdx === (rowSplitCnt - 1));
+                            private_resetBandArea();
+                            me.pageStatusLst.push(pageStatus.slice(0));
+                            pageIdx++;
+                            counterRowRec = orgMaxRowRec * rowIdx;
+                            let currentSortedRowSequence: any = me.sortedRowSequence;
+                            if (hasAdHocRow && rowIdx === (rowSplitCnt - 1)) {
+                                currentSortedRowSequence = null;
+                                currentSortedContentSequence = null;
+                                counterRowRec = 0;
+                            }
+                            _addTabValue(me.dispValueIdxLst_Row, currentSortedRowSequence, segIdx, counterRowRec, maxRowRec, me.dispSerialIdxLst_Row, me.col_sum_fields_value_total, me.dispSumValueLst_Col);
+                            _addTabValue(me.dispValueIdxLst_Col, currentSortedColSequence, segIdx, counterColRec, maxColRec, null, null, null);
+                            _addContentValue(me.dispValueIdxLst_Content, currentSortedContentSequence, segIdx, counterRowRec, maxRowRec, counterColRec, maxColRec, me.page_seg_map, pageIdx);
+                        }
+                    }
+                }
+                JpcCrossTabHelper.initialPageStatus(pageStatus);
+            }
+            //3. set pageSeq and return the result
+            rst = pageIdx;
+        }
+        // bands = null;
+        return rst;
+    };
+    outputAsPreviewPage(rptTpl: any, bands: any, controls: any, $CURRENT_RPT: any, customizeCfg: any) {
+        let me = this, rst: any[] = [];
+        let pageStatus: boolean[] = [true, true, true, true, true, true, true, true];
+        me.pageStatusLst.push(pageStatus);
+        // JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_FLOW_INFO][JV.NODE_FLOW_CONTENT][JV.PROP_FLOW_FIELDS], null, me.disp_fields_idx, false);
+        JpcBandHelper.setBandArea(bands, rptTpl, pageStatus, true, false);
+        let maxRowRec = JpcCrossTabHelper.getMaxRowsPerPage(bands, rptTpl);
+        let maxColRec = JpcCrossTabHelper.getMaxColsPerPage(bands, rptTpl);
+        let unitFactor = JpcCommonHelper.getUnitFactor(rptTpl);
+        //1. 交叉行
+        rst = rst.concat(me.outputPreviewRowTab(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, unitFactor));
+        //2. 交叉列
+        rst = rst.concat(me.outputPreviewColTab(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxColRec, unitFactor));
+        //3. 交叉数据
+        rst = rst.concat(me.outputPreviewContent(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, maxColRec, unitFactor));
+        //4. 交叉行拓展
+        rst = rst.concat(me.outputPreviewTabExt(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxColRec, unitFactor));
+        //5. 交叉行拓展合计
+        rst = rst.concat(me.outputPreviewSumTabExt(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, unitFactor));
+        //6. 交叉列合计
+        rst = rst.concat(me.outputPreviewTabSum(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, JV.NODE_CROSS_COL_SUM, unitFactor));
+        //7. 离散
+        rst = rst.concat(JpcDiscreteHelper.outputPreviewDiscreteInfo(rptTpl[JV.NODE_CROSS_INFO][JV.NODE_DISCRETE_INFO], bands, unitFactor, pageStatus));
+        return rst;
+    };
+    outputPreviewContent(rptTpl: any, bands: any, controls: any, $CURRENT_RPT: any, customizeCfg: any, maxRowRec: number, maxColRec: number, unitFactor: number) {
+        return this.private_OutputPreviewCommon(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, maxColRec, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_CONTENT], unitFactor);
+    };
+    outputPreviewRowTab(rptTpl: any, bands: any, controls: any, $CURRENT_RPT: any, customizeCfg: any, maxRowRec: number, unitFactor: number) {
+        let rst = this.private_OutputPreviewCommon(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, 1, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW], unitFactor);
+        rst = rst.concat(this.private_OutputPreviewCommon(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, 1, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_AD_HOC], unitFactor));
+        return rst;
+    };
+    outputPreviewColTab(rptTpl: any, bands: any, controls: any, $CURRENT_RPT: any, customizeCfg: any, maxColRec: number, unitFactor: number) {
+        return this.private_OutputPreviewCommon(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, 1, maxColRec, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_COL], unitFactor);
+    };
+    outputPreviewTabExt(rptTpl: any, bands: any, controls: any, $CURRENT_RPT: any, customizeCfg: any, maxColRec: number, unitFactor: number) {
+        //交叉行拓展
+        return this.private_OutputPreviewCommon(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, 1, maxColRec, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_EXT], unitFactor);
+    };
+    outputPreviewSumTabExt(rptTpl: any, bands: any, controls: any, $CURRENT_RPT: any, customizeCfg: any, unitFactor: number) {
+        //交叉行拓展合计
+        return this.private_OutputPreviewCommon(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, 1, 1, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_SUM_EXT], unitFactor);
+    };
+    outputPreviewTabSum(rptTpl: any, bands: any, controls: any, $CURRENT_RPT: any, customizeCfg: any, maxRowRec: number, tabNodeName: string, unitFactor: number) {
+        return this.private_OutputPreviewCommon(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, 1, rptTpl[JV.NODE_CROSS_INFO][tabNodeName], unitFactor);
+    };
+    private_OutputPreviewCommon(rptTpl: any, bands: any, controls: any, $CURRENT_RPT: any, customizeCfg: any, maxRowRec: number, maxColRec: number, tab: any, unitFactor: number) {
+        let me = this, rst = [];
+        let band = (tab)?bands[tab[JV.PROP_BAND_NAME]]:null;
+        if (band) {
+            let tab_fields = tab[JV.PROP_CROSS_FIELDS];
+            for (let rowIdx = 0; rowIdx < maxRowRec; rowIdx++) {
+                for (let i = 0; i < tab_fields.length; i++) {
+                    let tab_field = tab_fields[i];
+                    if (!(tab_field[JV.PROP_HIDDEN])) {
+                        for (let colIdx = 0; colIdx < maxColRec; colIdx++) {
+                            if (tab_field[JV.PROP_IS_SERIAL]) {
+                                rst.push(me.outputTabField(band, tab_field, [rowIdx + 1], 0, -1, maxRowRec, rowIdx, maxColRec, colIdx, unitFactor, false, controls));
+                            } else {
+                                rst.push(me.outputTabField(band, tab_field, null, -1, -1, maxRowRec, rowIdx, maxColRec, colIdx, unitFactor, false, controls));
+                            }
+                        }
+                    }
+                }
+                if (tab[JV.PROP_TEXTS]) {
+                    for (let j = 0; j < tab[JV.PROP_TEXTS].length; j++) {
+                        for (let colIdx = 0; colIdx < maxColRec; colIdx++) {
+                            rst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXTS][j], band, unitFactor, maxRowRec, rowIdx, maxColRec, colIdx, 1, 0));
+                        }
+                    }
+                }
+            }
+        }
+        return rst;
+    };
+    outputAsSimpleJSONPage(rptTpl: any, dataObj: any, page: number, bands: any, controls: any, $CURRENT_RPT: any, customizeCfg: any) {
+        let me = this, rst: any = [], tabRstLst = [];
+        let unitFactor = JpcCommonHelper.getUnitFactor(rptTpl);
+        if (me.paging_option === JV.PAGING_OPTION_INFINITY) {
+            // 交叉式表暂时不支持无限扩展功能
+        } else {
+            let segIdx = JpcCommonHelper.getSegIdxByPageIdx(page, me.page_seg_map);
+            //1 calculate the band position
+            JpcBandHelper.setBandArea(bands, rptTpl, me.pageStatusLst[page - 1], false, false);
+            //2. start to output detail-part
+            //2.1 Row-Tab
+            //tabRstLst.push(me.outputRowTab(rptTpl, dataObj, page, bands, unitFactor, controls, $CURRENT_RPT, customizeCfg));
+            tabRstLst.push(me.outputRowTabCommon(rptTpl, dataObj, page, bands, JV.NODE_CROSS_ROW, me.row_fields_idx, unitFactor, controls, $CURRENT_RPT, customizeCfg));
+            tabRstLst.push(me.outputRowTabCommon(rptTpl, dataObj, page, bands, JV.NODE_CROSS_ROW_AD_HOC, me.row_fields_adhoc_idx, unitFactor, controls, $CURRENT_RPT, customizeCfg));
+            //2.2 Col-Tab
+            tabRstLst.push(me.outputColTab(rptTpl, dataObj, page, bands, unitFactor, controls, $CURRENT_RPT, customizeCfg));
+            //2.3 Content-Tab
+            tabRstLst.push(me.outputContent(rptTpl, dataObj, page, bands, unitFactor, controls, $CURRENT_RPT, customizeCfg));
+            //2.4 Sum-Tab Row
+            //2.4 Sum-tab Col
+            tabRstLst.push(me.outputTabSum(rptTpl, dataObj, page, bands, unitFactor, JV.NODE_CROSS_COL_SUM, controls, $CURRENT_RPT, customizeCfg));
+            //2.x row tab ext
+            tabRstLst.push(me.outputTabExt(rptTpl, dataObj, page, bands, unitFactor, controls, $CURRENT_RPT, customizeCfg));
+            tabRstLst.push(me.outputSumTabExt(rptTpl, dataObj, page, bands, unitFactor, segIdx, controls, $CURRENT_RPT, customizeCfg));
+            //2.5 Discrete
+            tabRstLst.push(JpcDiscreteHelper.outputDiscreteInfo(rptTpl[JV.NODE_CROSS_INFO][JV.NODE_DISCRETE_INFO], bands, dataObj, unitFactor, me.pageStatusLst[page - 1], segIdx, 1, 0, $CURRENT_RPT, customizeCfg));
+        }
+        for (let i = 0; i < tabRstLst.length; i++) {
+            rst = rst.concat(tabRstLst[i]);
+            tabRstLst[i] = null;
+        }
+        return rst;
+    };
+    outputTabSum(rptTpl: any, dataObj: any, page: number, bands: any, unitFactor: number, tabNodeName: string, controls: any, $CURRENT_RPT: any, customizeCfg: any) {
+        let me = this, rst: any = [],
+            tab = rptTpl[JV.NODE_CROSS_INFO][tabNodeName],
+            band = bands[tab[JV.PROP_BAND_NAME]];
+        if (band) {
+            let pageStatus = me.pageStatusLst[page - 1];
+            if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true) {
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS];
+                for (let i = 0; i < me.dispSumValueLst_Col[page - 1].length; i++) {
+                    if (i === 0) {
+                        for (let tfIdx = 0; tfIdx < tab_fields.length; tfIdx++) {
+                            let map_data_field = JE.F(tab_fields[tfIdx][JV.PROP_FIELD_ID], $CURRENT_RPT);
+                            JpcFieldHelper.resetFormat(tab_fields[tfIdx], map_data_field, customizeCfg);
+                        }
+                    }
+                    if (me.dispSumValueLst_Col[page - 1][i] !== null) {
+                        for (let j = 0; j < me.dispSumValueLst_Col[page - 1][i].length; j++) {
+                            let tab_field = tab_fields[j];
+                            let val = me.dispSumValueLst_Col[page - 1][i][j];
+                            let cellItem = JpcCommonOutputHelper.createCommonOutput(tab_field, val, controls);
+                            cellItem[JV.PROP_AREA] = JpcAreaHelper.outputArea(tab_field[JV.PROP_AREA], band, unitFactor, me.dispSumValueLst_Col[page - 1].length, i, 1, 0, 1, 0, true, false);
+                            rst.push(cellItem);
+                        }
+                    } else {
+                        let sumL = 1;
+                        for (let si = 0; si < me.dispSumValueLst_Col.length; si++) {
+                            if (me.dispSumValueLst_Col[si][0] !== null) {
+                                sumL = me.dispSumValueLst_Col[si][0].length;
+                                break;
+                            }
+                        }
+                        for (let j = 0; j < sumL; j++) {
+                            let tab_field = tab_fields[j];
+                            let val = null;
+                            let cellItem = JpcCommonOutputHelper.createCommonOutput(tab_field, val, controls);
+                            cellItem[JV.PROP_AREA] = JpcAreaHelper.outputArea(tab_field[JV.PROP_AREA], band, unitFactor, me.dispSumValueLst_Col[page - 1].length, i, 1, 0, 1, 0, true, false);
+                            rst.push(cellItem);
+                        }
+                    }
+                }
+            }
+        }
+        return rst;
+    };
+    outputTabExt(rptTpl: any, dataObj: any, page: number, bands: any, unitFactor: number, controls: any, $CURRENT_RPT: any, customizeCfg: any) {
+        let me = this, rst: any = [], firstTextOutput = true,
+            tab = rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_EXT];
+        let band = bands[tab[JV.PROP_BAND_NAME]];
+        if (band) {
+            let pageStatus = me.pageStatusLst[page - 1];
+            if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true) {
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS],
+                    data_details = dataObj[JV.DATA_DETAIL_DATA],
+                    valuesIdx = me.dispValueIdxLst_Col[page - 1];
+                for (let i = 0; i < me.row_extension_fields_idx.length; i++) {
+                    let tab_field = tab_fields[i];
+                    let data_field = null;
+                    let map_data_field = JE.F(tab_field[JV.PROP_FIELD_ID], $CURRENT_RPT);
+                    if (typeof me.row_extension_fields_idx[i] !== 'object') {
+                        data_field = data_details[me.row_extension_fields_idx[i]];
+                    } else {
+                        if (map_data_field) {
+                            data_field = map_data_field[JV.PROP_AD_HOC_DATA];
+                        }
+                    }
+
+                    if (!(tab_field[JV.PROP_HIDDEN])) {
+                        let cols = valuesIdx.length;
+                        for (let colIdx = 0; colIdx < cols; colIdx++) {
+                            if (colIdx === 0) JpcFieldHelper.resetFormat(tab_field, map_data_field, customizeCfg);
+                            rst.push(me.outputTabField(band, tab_field, data_field, valuesIdx[colIdx], -1, 1, 0, cols, colIdx, unitFactor, false, controls));
+                            //2. output texts if has
+                            if (firstTextOutput) {
+                                if (tab[JV.PROP_TEXT]) {
+                                    rst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXT], band, unitFactor, 1, 0, cols, colIdx, 1, 0));
+                                }
+                                if (tab[JV.PROP_TEXTS]) {
+                                    for (let j = 0; j < tab[JV.PROP_TEXTS].length; j++) {
+                                        rst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXTS][j], band, unitFactor, 1, 0, cols, colIdx, 1, 0));
+                                    }
+                                }
+                            }
+                        }
+                        firstTextOutput = false;
+                    }
+                }
+            }
+        }
+        return rst;
+    };
+    outputSumTabExt(rptTpl: any, dataObj: any, page: number, bands: any, unitFactor: number, segIdx: number, controls: any, $CURRENT_RPT: any, customizeCfg: any) {
+        let me = this, rst = [],
+            tab = rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_SUM_EXT];
+        let band = bands[tab[JV.PROP_BAND_NAME]];
+        if (band) {
+            let pageStatus = me.pageStatusLst[page - 1];
+            if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true && pageStatus[JV.STATUS_CROSS_ROW_END] === true) {
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS],
+                    data_details = dataObj[JV.DATA_DETAIL_DATA],
+                    data_fields = [];
+                for (let i = 0; i < me.row_sum_extension_fields_idx.length; i++) {
+                    // let data_field = data_details[me.row_sum_extension_fields_idx[i]];
+                    let tab_field = tab_fields[i];
+                    let data_field = null;
+                    let map_data_field = JE.F(tab_field[JV.PROP_FIELD_ID], $CURRENT_RPT);
+                    if (typeof me.row_sum_extension_fields_idx[i] !== 'object') {
+                        data_field = data_details[me.row_sum_extension_fields_idx[i]];
+                    } else {
+                        if (map_data_field) {
+                            data_field = map_data_field[JV.PROP_AD_HOC_DATA];
+                        }
+                    }
+                    JpcFieldHelper.resetFormat(tab_field, map_data_field, customizeCfg);
+                    data_fields.push(data_field);
+                }
+                //2. initialize grand total value
+                let rowGrandTotal = [];
+                for (let di = 0; di < data_fields.length; di++) {
+                    rowGrandTotal[di] = 0.0;
+                    //3. start to sum
+                    for (let i = 0; i < me.sortedColSequence[segIdx].length; i++) {
+                        //me.sortedColSequence[segIdx][i][0] //this is the data field value index!
+                        rowGrandTotal[di] = rowGrandTotal[di] + 1.0 * parseFloat(JpcFieldHelper.getValue(data_fields[di], me.sortedColSequence[segIdx][i][0]));
+                    }
+                }
+                //4. output
+                for (let di = 0; di < tab_fields.length; di++) {
+                    let tab_field = tab_fields[di];
+                    if (!tab_field[JV.PROP_HIDDEN]) {
+                        let val = rowGrandTotal[di];
+                        let cellItem = JpcCommonOutputHelper.createCommonOutput(tab_field, val, controls);
+                        cellItem[JV.PROP_AREA] = JpcAreaHelper.outputArea(tab_field[JV.PROP_AREA], band, unitFactor, 1, 0, 1, 0, 1, 0, false, false);
+                        rst.push(cellItem);
+                    }
+                }
+                //output texts if has
+                if (tab[JV.PROP_TEXT]) {
+                    rst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXT], band, unitFactor, 1, 0, 1, 0, 1, 0));
+                }
+                if (tab[JV.PROP_TEXTS]) {
+                    for (let j = 0; j < tab[JV.PROP_TEXTS].length; j++) {
+                        rst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXTS][j], band, unitFactor, 1, 0, 1, 0, 1, 0));
+                    }
+                }
+            }
+        }
+        return rst;
+    };
+    outputContent(rptTpl: any, dataObj: any, page: number, bands: any, unitFactor: number, controls: any, $CURRENT_RPT: any, customizeCfg: any) {
+        let me = this, rst: any = [];
+        let tab = rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_CONTENT];
+        let band = bands[tab[JV.PROP_BAND_NAME]];
+        if (band) {
+            let pageStatus = me.pageStatusLst[page - 1];
+            if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true) {
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS];
+                let data_details = dataObj[JV.DATA_DETAIL_DATA];
+                let contentValuesIdx = me.dispValueIdxLst_Content[page - 1];
+                let flexiblePrecisionRefObj: any = null, flexibleRefField = null, precision_ref_data = null;
+                for (let i = 0; i < tab_fields.length; i++) {
+                    let tab_field = tab_fields[i];
+                    let data_field = null;
+                    let map_data_field = JE.F(tab_field[JV.PROP_FIELD_ID], $CURRENT_RPT);
+                    if (typeof me.content_fields_idx[i] !== 'object') {
+                        data_field = data_details[me.content_fields_idx[i]];
+                    } else {
+                        if (map_data_field) {
+                            data_field = map_data_field[JV.PROP_AD_HOC_DATA];
+                        }
+                    }
+                    if (!(tab_field[JV.PROP_HIDDEN])) {
+                        let rows = contentValuesIdx.length;
+                        for (let rowIdx = 0; rowIdx < rows; rowIdx++) {
+                            let cols = contentValuesIdx[rowIdx].length;
+                            for (let colIdx = 0; colIdx < cols; colIdx++) {
+                                if (map_data_field && map_data_field[JV.PROP_PRECISION] && map_data_field.flexiblePrecisionRefObj) {
+                                    if (flexiblePrecisionRefObj === null) {
+                                        flexiblePrecisionRefObj = {};
+                                        flexibleRefField = JE.F(map_data_field[JV.PROP_PRECISION][JV.PROP_FLEXIBLE_REF_FILED_ID], $CURRENT_RPT);
+                                        precision_ref_data = dataObj[map_data_field.DataNodeName][flexibleRefField.DataSeq];
+                                        for (let decimalObj of map_data_field.flexiblePrecisionRefObj) {
+                                            flexiblePrecisionRefObj["refUnit_" + decimalObj.unit] = decimalObj.decimal;
+                                        }
+                                    }
+                                    JpcFieldHelper.resetFlexibleFormat(tab_field, precision_ref_data, flexiblePrecisionRefObj, contentValuesIdx[rowIdx][colIdx], customizeCfg);
+                                } else {
+                                    if (colIdx === 0) JpcFieldHelper.resetFormat(tab_field, map_data_field, customizeCfg);
+                                }
+                                rst.push(me.outputTabField(band, tab_field, data_field, contentValuesIdx[rowIdx][colIdx], -1, rows, rowIdx, cols, colIdx, unitFactor, true, controls));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return rst;
+    };
+    outputColTab(rptTpl: any, dataObj: any, page: number, bands: any, unitFactor: number, controls: any, $CURRENT_RPT: any, customizeCfg: any) {
+        let me = this, rst: any = [], firstTextOutput = true;
+        let tab = rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_COL];
+        let band = bands[tab[JV.PROP_BAND_NAME]];
+        if (band) {
+            let pageStatus = me.pageStatusLst[page - 1];
+            if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true) {
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS];
+                let data_details = dataObj[JV.DATA_DETAIL_DATA];
+                let valuesIdx = me.dispValueIdxLst_Col[page - 1];
+                let flexiblePrecisionRefObj: any = null, flexibleRefField = null, precision_ref_data = null;
+                for (let i = 0; i < me.col_fields_idx.length; i++) {
+                    let tab_field = tab_fields[i];
+                    if (!(tab_field[JV.PROP_HIDDEN])) {
+                        let mergedRst = [];
+                        let data_field = null;
+                        let map_data_field = JE.F(tab_field[JV.PROP_FIELD_ID], $CURRENT_RPT);
+                        if (typeof me.col_fields_idx[i] !== 'object') {
+                            data_field = data_details[me.col_fields_idx[i]];
+                        } else {
+                            if (map_data_field) {
+                                data_field = map_data_field[JV.PROP_AD_HOC_DATA];
+                            }
+                        }
+                        let cols = valuesIdx.length;
+                        for (let colIdx = 0; colIdx < cols; colIdx++) {
+                            if (map_data_field && map_data_field[JV.PROP_PRECISION] && map_data_field.flexiblePrecisionRefObj) {
+                                if (flexiblePrecisionRefObj === null) {
+                                    flexiblePrecisionRefObj = {};
+                                    flexibleRefField = JE.F(map_data_field[JV.PROP_PRECISION][JV.PROP_FLEXIBLE_REF_FILED_ID], $CURRENT_RPT);
+                                    precision_ref_data = dataObj[map_data_field.DataNodeName][flexibleRefField.DataSeq];
+                                    for (let decimalObj of map_data_field.flexiblePrecisionRefObj) {
+                                        flexiblePrecisionRefObj["refUnit_" + decimalObj.unit] = decimalObj.decimal;
+                                    }
+                                }
+                                JpcFieldHelper.resetFlexibleFormat(tab_field, precision_ref_data, flexiblePrecisionRefObj, valuesIdx[colIdx], customizeCfg);
+                            } else {
+                                if (colIdx === 0) JpcFieldHelper.resetFormat(tab_field, map_data_field, customizeCfg);
+                            }
+                            mergedRst.push(me.outputTabField(band, tab_field, data_field, valuesIdx[colIdx], -1, 1, 0, cols, colIdx, unitFactor, false, controls));
+                            //rst.push(me.outputTabField(band, tab_field, data_field, valuesIdx[colIdx], -1, 1, 0, cols, colIdx, unitFactor, false, controls));
+                            //2. output texts
+                            if (firstTextOutput) {
+                                if (tab[JV.PROP_TEXT]) {
+                                    // mergedRst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXT], band, unitFactor, 1, 0, cols, colIdx, 1, 0));
+                                    rst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXT], band, unitFactor, 1, 0, cols, colIdx, 1, 0));
+                                }
+                                if (tab[JV.PROP_TEXTS]) {
+                                    for (let j = 0; j < tab[JV.PROP_TEXTS].length; j++) {
+                                        // mergedRst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXTS][j], band, unitFactor, 1, 0, cols, colIdx, 1, 0));
+                                        rst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXTS][j], band, unitFactor, 1, 0, cols, colIdx, 1, 0));
+                                    }
+                                }
+                            }
+                        }
+                        firstTextOutput = false;
+                        //判断是否需要合并
+                        if (tab_field[JV.PROP_IS_MERGE] && mergedRst.length > 1) {
+                            let lastCell = mergedRst[mergedRst.length - 1];
+                            for (let mergeIdx = mergedRst.length - 2; mergeIdx >= 0; mergeIdx--) {
+                                if (lastCell[JV.PROP_VALUE] === mergedRst[mergeIdx][JV.PROP_VALUE]) {
+                                    mergedRst[mergeIdx][JV.PROP_AREA][JV.PROP_RIGHT] = lastCell[JV.PROP_AREA][JV.PROP_RIGHT];
+                                    mergedRst.splice(mergeIdx + 1, 1);
+                                }
+                                lastCell = mergedRst[mergeIdx];
+                            }
+                        }
+                        rst = rst.concat(mergedRst);
+                    }
+                }
+            }
+        }
+        return rst;
+    };
+    outputRowTabCommon(rptTpl: any, dataObj: any, page: number, bands: any, tabStr: string, rowFieldsIdxArr: any[], unitFactor: number, controls: any, $CURRENT_RPT: any, customizeCfg: any) {
+        let me = this, rst = [];
+        let tab = rptTpl[JV.NODE_CROSS_INFO][tabStr];
+        let band = (tab)?bands[tab[JV.PROP_BAND_NAME]]:null;
+        if (band) {
+            let pageStatus = me.pageStatusLst[page - 1];
+            if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true) {
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS];
+                let data_details = dataObj[JV.DATA_DETAIL_DATA];
+                let valuesIdx = me.dispValueIdxLst_Row[page - 1];
+                let serialsIdx = me.dispSerialIdxLst_Row[page - 1];
+                let flexiblePrecisionRefObj: any = null, flexibleRefField = null, precision_ref_data = null;
+                for (let i = 0; i < rowFieldsIdxArr.length; i++) {
+                    let tab_field = tab_fields[i];
+                    if (!(tab_field[JV.PROP_HIDDEN])) {
+                        let data_field = null;
+                        let map_data_field = JE.F(tab_field[JV.PROP_FIELD_ID], $CURRENT_RPT);
+                        if (typeof rowFieldsIdxArr[i] !== 'object') {
+                            data_field = data_details[rowFieldsIdxArr[i]];
+                        } else {
+                            if (map_data_field) {
+                                data_field = map_data_field[JV.PROP_AD_HOC_DATA];
+                            }
+                        }
+                        let rows = valuesIdx.length;
+                        for (let rowIdx = 0; rowIdx < rows; rowIdx++) {
+                            if (map_data_field && map_data_field[JV.PROP_PRECISION] && map_data_field.flexiblePrecisionRefObj) {
+                                if (flexiblePrecisionRefObj === null) {
+                                    flexiblePrecisionRefObj = {};
+                                    flexibleRefField = JE.F(map_data_field[JV.PROP_PRECISION][JV.PROP_FLEXIBLE_REF_FILED_ID], $CURRENT_RPT);
+                                    precision_ref_data = dataObj[map_data_field.DataNodeName][flexibleRefField.DataSeq];
+                                    for (let decimalObj of map_data_field.flexiblePrecisionRefObj) {
+                                        flexiblePrecisionRefObj["refUnit_" + decimalObj.unit] = decimalObj.decimal;
+                                    }
+                                }
+                                JpcFieldHelper.resetFlexibleFormat(tab_field, precision_ref_data, flexiblePrecisionRefObj, valuesIdx[rowIdx], customizeCfg);
+                            } else {
+                                if (rowIdx === 0) JpcFieldHelper.resetFormat(tab_field, map_data_field, customizeCfg);
+                            }
+                            rst.push(me.outputTabField(band, tab_field, data_field, valuesIdx[rowIdx], serialsIdx[rowIdx], rows, rowIdx, 1, 0, unitFactor, true, controls));
+                        }
+                    }
+                }
+            }
+        }
+        return rst;
+    };
+    outputTabField(band: any, tab_field: any, data_field: any, valueIdx: number, serialIdx: number, rows: number, rowIdx: number, cols: number, colIdx: number, unitFactor: number, isRow: boolean, controls: any) {
+        let rst = null;
+        if (isRow === true && tab_field[JV.PROP_IS_SERIAL] && tab_field[JV.PROP_IS_SERIAL] === true) {
+            if (serialIdx >= 0) {
+                rst = JpcCommonOutputHelper.createCommonOutput(tab_field, serialIdx + 1, null);
+            } else rst = JpcCommonOutputHelper.createCommonOutput(tab_field, "", controls);
+        } else {
+            rst = JpcCommonOutputHelper.createCommonOutput(tab_field, JpcFieldHelper.getValue(data_field, valueIdx), controls);
+        }
+        //position
+        if (isRow === true) {
+            rst[JV.PROP_AREA] = JpcAreaHelper.outputArea(tab_field[JV.PROP_AREA], band, unitFactor, rows, rowIdx, cols, colIdx, 1, 0, true, false);
+        } else {
+            rst[JV.PROP_AREA] = JpcAreaHelper.outputArea(tab_field[JV.PROP_AREA], band, unitFactor, rows, rowIdx, cols, colIdx, 1, 0, false, false);
+        }
+        return rst;
+    };
+};
+function _addTabValue(tabValuedIdxLst: any[], sortedSequence: any[], segIdx: number, preRec: number, nextRec: number, dispSerialIdxLst: any, sorted_sum_value_Lst: any, rst_sum_value_Lst: any) {
+    if (tabValuedIdxLst) {
+        let serial1stTier = null;
+        if (dispSerialIdxLst) serial1stTier = [];
+        let pgseg1stTier = [];
+        let sumVal = [];
+        let sumValL = 1;
+        if (sortedSequence) {
+            let arrDupVals = sortedSequence[segIdx];
+            let arrDupSumVals = null;
+            if (sorted_sum_value_Lst !== null) {
+                arrDupSumVals = sorted_sum_value_Lst[segIdx];
+                sumValL = arrDupSumVals[0].length;
+            }
+
+            for (let i = 0; i < nextRec; i++) {
+                if (arrDupVals.length <= preRec + i) {
+                    pgseg1stTier[i] = JV.BLANK_VALUE_INDEX;
+                    sumVal[i] = [];
+                    for (let ei = 0; ei < sumValL; ei++) {
+                        sumVal[i][ei] = null;
+                    }
+                    if (serial1stTier !== null) {
+                        serial1stTier[i] = JV.BLANK_VALUE_INDEX;
+                    }
+                    continue;
+                }
+                let duplicateValueArr = arrDupVals[preRec + i];
+                pgseg1stTier[i] = duplicateValueArr[0];
+                if (arrDupSumVals !== null) sumVal[i] = arrDupSumVals[preRec + i];
+
+                if (serial1stTier !== null) {
+                    serial1stTier[i] = preRec + i;
+                }
+            }
+            tabValuedIdxLst.push(pgseg1stTier);
+            if (dispSerialIdxLst !== null) {
+                dispSerialIdxLst.push(serial1stTier);
+            }
+            if (sorted_sum_value_Lst !== null && rst_sum_value_Lst !== null) {
+                rst_sum_value_Lst.push(sumVal);
+            }
+        } else {
+            //should push blank value index rather than null
+            for (let i = 0; i < nextRec; i++) {
+                pgseg1stTier[i] = JV.BLANK_VALUE_INDEX;
+                sumVal[i] = null;
+                if (serial1stTier !== null) {
+                    serial1stTier[i] = JV.BLANK_VALUE_INDEX;
+                }
+            }
+            tabValuedIdxLst.push(pgseg1stTier);
+            if (dispSerialIdxLst !== null) {
+                dispSerialIdxLst.push(serial1stTier);
+            }
+            if (sorted_sum_value_Lst !== null && rst_sum_value_Lst !== null) {
+                rst_sum_value_Lst.push(sumVal);
+            }
+        }
+    }
+};
+
+function _addContentValue(dispValueIdxLst_Content: any[], sortedContentSequence: any, segIdx: number, counterRowRec: number, maxRowRec: number, counterColRec: number, maxColRec: number, page_seg_map: any[], pageIdx: number) {
+    if (dispValueIdxLst_Content !== null) {
+        page_seg_map.push([pageIdx,segIdx]);
+        let arrContents: any[] = [];
+        if (sortedContentSequence !== null) {
+            let arrAllContent = sortedContentSequence[segIdx];
+            for (let i = 0; i < maxRowRec; i++) {
+                arrContents.push([]);
+                for (let j = 0; j < maxColRec; j++) {
+                    if (arrAllContent.length <= counterRowRec + i || arrAllContent[counterRowRec + i].length <= counterColRec + j) {
+                        arrContents[i][j] = JV.BLANK_VALUE_INDEX;
+                    } else {
+                        arrContents[i][j] = arrAllContent[counterRowRec + i][counterColRec + j];
+                    }
+                }
+            }
+            dispValueIdxLst_Content.push(arrContents);
+        } else {
+            //should push blank value index rather than null
+            for (let i = 0; i < maxRowRec; i++) {
+                arrContents.push([]);
+                for (let j = 0; j < maxColRec; j++) {
+                    arrContents[i][j] = JV.BLANK_VALUE_INDEX;
+                }
+            }
+            dispValueIdxLst_Content.push(arrContents);
+        }
+    }
+};
+
+function _SortAndOptimize(rptTpl: any, dataObj: any, dataSeq: any[], sortTab: string, rstFieldsIdx: any[], $CURRENT_RPT: any) {
+    let result = [];
+    let tab = rptTpl[JV.NODE_CROSS_INFO][sortTab];
+    if (tab) {
+        let sIDX = 0;
+        //1. prepare and sort by tab-field
+        let fields: any[] = [];
+        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, tab[JV.PROP_CROSS_FIELDS], fields, rstFieldsIdx, false);
+        let data_details = dataObj[JV.DATA_DETAIL_DATA];
+        JpcCrossTabHelper.sortTabFields(fields, rstFieldsIdx, data_details, dataSeq, $CURRENT_RPT);
+        //2. distinguish sort tab fields value
+        let b1 = false;
+        for (let i = 0; i < dataSeq.length; i++) {
+            sIDX = 0;
+            let segArr: any[] = [];
+            if (dataSeq[i].length === 1) {
+                JpcCrossTabHelper.pushToSeg(segArr, dataSeq, i, 0, 1);
+            } else {
+                for (let j = 1; j < dataSeq[i].length; j++) {
+                    b1 = false;
+                    for (let k = 0; k < rstFieldsIdx.length; k++) {
+                        if (fields[k].hasOwnProperty(JV.TAB_FIELD_PROP_SORT)) {
+                            //只有被选择了作为排序字段的才进入排序及优化
+                            if (typeof(rstFieldsIdx[k]) === 'object') {
+                                let map_data_field = JE.F(rstFieldsIdx[k][JV.PROP_ID], $CURRENT_RPT);
+                                if (map_data_field[JV.PROP_AD_HOC_DATA][dataSeq[i][j - 1]] !== map_data_field[JV.PROP_AD_HOC_DATA][dataSeq[i][j]]) {
+                                    b1 = true;
+                                    break;
+                                }
+                            } else {
+                                if (data_details[rstFieldsIdx[k]][dataSeq[i][j - 1]] !== data_details[rstFieldsIdx[k]][dataSeq[i][j]]) {
+                                    b1 = true;
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                    if (b1) {
+                        JpcCrossTabHelper.pushToSeg(segArr, dataSeq, i, sIDX, j);
+                        sIDX = j;
+                        if (j === dataSeq[i].length - 1) {
+                            JpcCrossTabHelper.pushToSeg(segArr, dataSeq, i, j, dataSeq[i].length);
+                        }
+                    } else if (j === dataSeq[i].length - 1) {
+                        JpcCrossTabHelper.pushToSeg(segArr, dataSeq, i, sIDX, dataSeq[i].length);
+                    }
+                }
+            }
+            if (segArr.length > 0) result.push(segArr);
+        }
+    }
+    return result;
+}
+
+function _SortForDisplayContent(rptTpl: any, rowSeq: any[], colSeq: any[], rstFieldsIdx: any[]){
+    let result = [];
+    let tab = rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_CONTENT];
+    if (tab) {
+        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, tab[JV.PROP_CROSS_FIELDS], [], rstFieldsIdx, false);
+    }
+    for (let i = 0; i < rowSeq.length; i++) {
+        let rl = rowSeq[i], cl = colSeq[i];
+        let ds: any[] = [];
+        //1. initialize to blank value index
+        for (let j = 0; j < rl.length; j++) {
+            ds.push([]);
+            for (let k = 0; k < cl.length; k++) {
+                ds[j].push(JV.BLANK_VALUE_INDEX);
+            }
+        }
+        //2. then fill up the right index
+        for (let j = 0; j < rl.length; j++) {
+            let ra = rl[j];
+            for (let k = 0; k < ra.length; k++) {
+                let colIdx = JpcCrossTabHelper.getColIDX(cl, ra[k]);
+                if (colIdx >= 0) {
+                    ds[j][colIdx] = ra[k];
+                }
+            }
+        }
+        result.push(ds);
+    }
+    return result;
+};
+
+export default JpcCrossTabClass;

+ 116 - 0
report/src/core/jpc_data.ts

@@ -0,0 +1,116 @@
+'use strict';
+
+import JV from './jpc_value_define';
+import jpc_common_helper from './helper/jpc_helper_common';
+
+const JpcData = {
+    createNew() {
+        let JpcDataRst = {
+            dataSeq: [],
+            exDataSeq: [],
+            analyzeData(rptTpl: any, dataObj: any) {
+                let me = this;
+                let _analyse = function(MASTER_FIELD_STR: string, DETAIL_FIELD_STR: string, MASTER_DATA_STR: string, DETAIL_DATA_STR: string, dataSeqArr: any) {
+                    //1. get ID fields
+                    let masterIDs = [];
+                    if (rptTpl[JV.NODE_FIELD_MAP][MASTER_FIELD_STR]) {
+                        for (let i = 0; i < rptTpl[JV.NODE_FIELD_MAP][MASTER_FIELD_STR].length; i++) {
+                            let mstFieldObj = rptTpl[JV.NODE_FIELD_MAP][MASTER_FIELD_STR][i];
+                            if (jpc_common_helper.getBoolean(mstFieldObj[JV.PROP_IS_ID])) {
+                                masterIDs.push({"idx": i, "seq": mstFieldObj[JV.PROP_ID_SEQ]});
+                            }
+                        }
+                    }
+                    let detailIDs = [];
+                    if (rptTpl[JV.NODE_FIELD_MAP][DETAIL_FIELD_STR]) {
+                        for (let i = 0; i < rptTpl[JV.NODE_FIELD_MAP][DETAIL_FIELD_STR].length; i++) {
+                            let dtlFieldObj = rptTpl[JV.NODE_FIELD_MAP][DETAIL_FIELD_STR][i];
+                            if (jpc_common_helper.getBoolean(dtlFieldObj[JV.PROP_IS_ID])) {
+                                detailIDs.push({"idx": i, "seq": dtlFieldObj[JV.PROP_ID_SEQ]});
+                            }
+                        }
+                    }
+                    //2. sort the ID fields
+                    if (masterIDs.length > 1) {
+                        masterIDs.sort(function(a, b) {
+                            return 1*a["seq"] - 1*b["seq"];
+                        })
+                    }
+                    if (detailIDs.length > 1) {
+                        detailIDs.sort(function(a, b) {
+                            return 1*a["seq"] - 1*b["seq"];
+                        })
+                    }
+                    //3. prepare data sequence
+                    if (masterIDs.length > 0) {
+                        let mst_dt_len = 0, dtl_dt_len = 0, mst_fields = [];
+                        for (let i = 0; i < masterIDs.length; i++) {
+                            mst_fields.push(dataObj[MASTER_DATA_STR][masterIDs[i]["idx"]]);
+                            mst_dt_len = dataObj[MASTER_DATA_STR][masterIDs[i]["idx"]].length;
+                        }
+                        let dtl_fields = [];
+                        for (let i = 0; i < detailIDs.length; i++) {
+                            dtl_fields.push(dataObj[DETAIL_DATA_STR][detailIDs[i]["idx"]]);
+                            dtl_dt_len = dataObj[DETAIL_DATA_STR][detailIDs[i]["idx"]].length;
+                        }
+                        let sIdx = 0;
+                        let isEqual = true;
+                        for (let i = 0; i < mst_dt_len; i++) {
+                            dataSeqArr.push([]);
+                            //then compare the master/detail ID-field value
+                            for (let j = sIdx; j < dtl_dt_len; j++) {
+                                isEqual = true;
+                                for (let k = 0; k < mst_fields.length; k++) {
+                                    if (!(mst_fields[k][i] === dtl_fields[k][j])) {
+                                        isEqual = false;
+                                        break;
+                                    }
+                                }
+                                if (isEqual) {
+                                    dataSeqArr[i].push(j);
+                                } else {
+                                    sIdx = j;
+                                    //below logic is for the data robustness purpose, to avoid those strange record(detail level) which could not match even one of the master record!
+                                    if (i < mst_dt_len - 1 && j < dtl_dt_len - 1) {
+                                        for (let j1 = j; j1 < dtl_dt_len; j1++) {
+                                            isEqual = true;
+                                            for (let k = 0; k < mst_fields.length; k++) {
+                                                if (!(mst_fields[k][i + 1] === dtl_fields[k][j1])) {
+                                                    isEqual = false;
+                                                    break;
+                                                }
+                                            }
+                                            if (isEqual) {
+                                                sIdx = j1;
+                                                break;
+                                            }
+                                        }
+                                    }
+                                    break;
+                                }
+                            }
+                        }
+                    } else { //if no master data
+                        if (dataObj && dataObj[DETAIL_DATA_STR] && dataObj[DETAIL_DATA_STR].length > 0) {
+                            //may be bill type report which may only have discrete fields!
+                            let field = dataObj[DETAIL_DATA_STR][0];
+                            dataSeqArr.push([]);
+                            for (let i = 0; i < field.length; i++) {
+                                dataSeqArr[0].push(i);
+                            }
+                        }
+                    }
+                };
+                if ((rptTpl) && (dataObj)) {
+                    _analyse(JV.NODE_MASTER_FIELDS, JV.NODE_DETAIL_FIELDS, JV.DATA_MASTER_DATA, JV.DATA_DETAIL_DATA, me.dataSeq);
+                    if (rptTpl[JV.NODE_FLOW_INFO_EX]) {
+                        _analyse(JV.NODE_MASTER_FIELDS_EX, JV.NODE_DETAIL_FIELDS_EX, JV.DATA_MASTER_DATA_EX, JV.DATA_DETAIL_DATA_EX, me.exDataSeq);
+                    }
+                }
+            }
+        };
+        return JpcDataRst;
+    }
+};
+
+export default JpcData;

+ 38 - 0
report/src/core/jpc_event.ts

@@ -0,0 +1,38 @@
+/**
+ * Created by Tony on 2019/5/17.
+ */
+'use strict';
+
+import JV from './jpc_value_define';
+
+const JpcEvent = {
+    createNew(rptTpl: any) {
+        let rst = {
+            [JV.EVENT_TYPE[JV.EVENT_IDX_GRP_ON_CREATE]]: null,
+            [JV.EVENT_TYPE[JV.EVENT_IDX_FLOW_CONTENT_ON_CREATE]]: null
+        };
+        if (rptTpl[JV.NODE_EVENTS]) {
+            for (let i = 0; i < rptTpl[JV.NODE_EVENTS].length; i++) {
+                let item = {...rptTpl[JV.NODE_EVENTS][i]};
+                // let propArr = Object.getOwnPropertyNames(rptTpl[JV.NODE_EVENTS][i]);
+                // for (let key of propArr) {
+                //     item[key] = rptTpl[JV.NODE_EVENTS][i][key];
+                // }
+                switch (JV.EVENT_TYPE.indexOf(item.type)) {
+                    case JV.EVENT_IDX_GRP_ON_CREATE:
+                        rst[JV.EVENT_TYPE[JV.EVENT_IDX_GRP_ON_CREATE]] = item;
+                        break;
+                    case JV.EVENT_IDX_FLOW_CONTENT_ON_CREATE:
+                        rst[JV.EVENT_TYPE[JV.EVENT_IDX_FLOW_CONTENT_ON_CREATE]] = item;
+                        break;
+                    default :
+                        break;
+                }
+                //rst.push(item);
+            }
+        }
+        return rst;
+    }
+};
+
+export default JpcEvent;

+ 399 - 0
report/src/core/jpc_ex.ts

@@ -0,0 +1,399 @@
+'use strict';
+
+import JV from './jpc_value_define';
+import JpcBand from './jpc_band';
+import JpcFlowTab from './jpc_flow_tab';
+import JpcBillTab from './jpc_bill_tab';
+import JpcCrossTab from './jpc_cross_tab';
+import JpcField from './jpc_field';
+
+import JpcParam from './jpc_param';
+import JpcFunc from './jpc_function';
+import JpcEvent from './jpc_event';
+import JpcData from './jpc_data';
+import JpcCommonHelper from './helper/jpc_helper_common';
+import $JE from './jpc_rte'; //Important: for self-define function execution purpose
+
+class JpcExClass{
+    flowTab: any;
+    flowTabEx: any;
+    billTab: any;
+    crossTab: any;
+    isFollowMode: boolean = false;
+    totalPages: number = 0;
+    exTotalPages: number = 0;
+    runTimePageData: any;
+    fields: any;
+    params: any;
+    formulas: any;
+    events: any;
+
+    constructor(rptTpl: any) {
+        if (rptTpl[JV.NODE_FLOW_INFO]) {
+            this.flowTab = new JpcFlowTab(false);
+            this.isFollowMode = false;
+        }
+        if (rptTpl[JV.NODE_FLOW_INFO_EX]) {
+            this.flowTabEx = new JpcFlowTab(true);
+            if (rptTpl[JV.NODE_FLOW_INFO_EX][JV.PROP_FLOW_EX_DISPLAY_MODE] === JV.DISPLAY_MODE_FOLLOW) {
+                this.isFollowMode = true;
+            }
+        }
+        if (rptTpl[JV.NODE_BILL_INFO]) {
+            this.billTab = new JpcBillTab();
+        }
+        //let dt1 = new Date();
+        if (rptTpl[JV.NODE_CROSS_INFO]) {
+            this.crossTab = new JpcCrossTab();
+        }
+        this.runTimePageData = {};
+        this.fields = JpcField.createNew(rptTpl);
+        this.params = JpcParam.createNew(rptTpl);
+        this.formulas = JpcFunc.createNew(rptTpl);
+        this.events = JpcEvent.createNew(rptTpl);
+    };
+
+    analyzeData(rptTpl: any, dataObj: any, defProperties: any, option: string, outputType: any) {
+        let me = this, dftPagingOption = option||JV.PAGING_OPTION_NORMAL;
+        //1. data object
+        let dataHelper: any = JpcData.createNew();
+        me.executeFormulas(JV.RUN_TYPE_BEFORE_ANALYZING, rptTpl, dataObj, me); //在分析前运行,主要是增加灵活性,比如:重新编排数据的主从关系
+        if (me.crossTab) {
+            me.executeFormulas(JV.RUN_TYPE_BEFORE_PAGING, rptTpl, dataObj, me);
+            dataHelper.analyzeData(rptTpl, dataObj);
+            me.crossTab.sorting(rptTpl, dataObj, dataHelper.dataSeq.slice(0), me);
+        } else {
+            dataHelper.analyzeData(rptTpl, dataObj);
+            //2. tab object
+            //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);
+                if (me.flowTabEx) {
+                    me.flowTabEx.sorting(rptTpl, dataObj, dataHelper.exDataSeq.slice(0), me);
+                }
+            }
+            if (me.billTab) {
+                me.billTab.sorting(rptTpl, dataObj, dataHelper.dataSeq.slice(0), me);
+            }
+            //let dt2 = new Date();
+            //alert(dt2 - dt1);
+            //3. formulas
+            me.executeFormulas(JV.RUN_TYPE_BEFORE_PAGING, rptTpl, dataObj, me);
+        }
+        //4. paging
+        me.paging(rptTpl, dataObj, defProperties, dftPagingOption);
+        //alert('analyzeData was completed!');
+        //for garbage collection:
+        // dataHelper = null;
+    };
+
+    paging(rptTpl: any, dataObj: any, defProperties: any, option: string) {
+        let me = this, dftPagingOption = option||JV.PAGING_OPTION_NORMAL;
+        if (me.flowTab) {
+            if (me.isFollowMode) {
+                me.totalPages = me.flowTab.preSetupPages(rptTpl, dataObj, defProperties, dftPagingOption, me, me.flowTabEx);
+            } else {
+                me.totalPages = me.flowTab.preSetupPages(rptTpl, dataObj, defProperties, dftPagingOption, me, null);
+                if (me.flowTabEx) {
+                    me.exTotalPages = me.flowTabEx.preSetupPages(rptTpl, dataObj, defProperties, dftPagingOption, me, null);
+                    //console.log('ad-hoc flow pages: ' + me.exTotalPages);
+                }
+                me.totalPages += me.exTotalPages;
+            }
+        } else if (me.crossTab) {
+            //me.totalPages = me.crossTab.preSetupPages(rptTpl, defProperties, dftPagingOption);
+            me.totalPages = me.crossTab.preSetupPages(rptTpl, defProperties, JV.PAGING_OPTION_NORMAL); //infinity对交叉表来说无意义
+        } else if (me.billTab) {
+            me.totalPages = me.billTab.paging(rptTpl, dataObj);
+        }
+    };
+
+    executeFormulas(runType: string, $CURRENT_TEMPLATE: any, $CURRENT_DATA: any, $CURRENT_RPT: any) {
+        let execFmlMe = this;
+        for (let execFmlIdx = 0; execFmlIdx < execFmlMe.formulas.length; execFmlIdx++) {
+            //remark: 搞这么复杂的变量名是为了防止与表达式起冲突(如循环变量i,j,k,容易造成变量冲突且不容易看出问题)
+            if (execFmlMe.formulas[execFmlIdx][JV.PROP_RUN_TYPE] === runType) {
+                let expression = execFmlMe.formulas[execFmlIdx][JV.PROP_EXPRESSION];
+                if (expression) {
+                    let $ME = execFmlMe.formulas[execFmlIdx];
+                    // console.log("current expression idx: " + execFmlIdx);
+                    // console.log(expression);
+                    try {
+                        eval(expression);
+                    } catch (ex) {
+                        console.log(ex);
+                    }
+                }
+            }
+        }
+    };
+
+    outputAsPreviewPage(rptTpl: any, defProperties: any) {
+        let me = this, rst: any = {};
+        rst[JV.NODE_CONTROL_COLLECTION] = private_buildDftControls(rptTpl, (defProperties === null)?null:defProperties.ctrls);
+        rst[JV.NODE_STYLE_COLLECTION] = private_buildDftStyles(rptTpl, (defProperties === null)?null:defProperties.styles);
+        rst[JV.NODE_FONT_COLLECTION] = private_buildDftFonts(rptTpl, (defProperties === null)?null:defProperties.fonts);
+        rst[JV.NODE_PAGE_INFO] = {};
+        rst[JV.NODE_PAGE_INFO][JV.NODE_MAIN_INFO_RPT_NAME] = rptTpl[JV.NODE_MAIN_INFO][JV.NODE_MAIN_INFO_RPT_NAME];
+        rst[JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE] = JpcCommonHelper.getPageSize(rptTpl);
+        rst[JV.NODE_PAGE_INFO][JV.NODE_MARGINS] = rptTpl[JV.NODE_MAIN_INFO][JV.NODE_MARGINS];
+        rst.items = [];
+        let bands = JpcBand.createNew(rptTpl, defProperties);
+        try {
+            function getPageMergeBorder() {
+                let mergeRst: any = null;
+                if (bands[JV.BAND_PROP_MERGE_BAND]) {
+                    let mergedBand = bands[JV.BAND_PROP_MERGE_BAND];
+                    mergeRst = {};
+                    mergeRst[JV.PROP_LEFT] = parseInt(mergedBand[JV.PROP_LEFT].toFixed(0));
+                    mergeRst[JV.PROP_RIGHT] = parseInt(mergedBand[JV.PROP_RIGHT].toFixed(0));
+                    mergeRst[JV.PROP_TOP] = parseInt(mergedBand[JV.PROP_TOP].toFixed(0));
+                    mergeRst[JV.PROP_BOTTOM] = parseInt(mergedBand[JV.PROP_BOTTOM].toFixed(0));
+                }
+                return mergeRst;
+            }
+            //1.
+            let rstPage: any = {};
+            rstPage[JV.PROP_PAGE_SEQ] = 1;
+            if (me.flowTab) {
+                rstPage[JV.PROP_CELLS] = me.flowTab.outputAsPreviewPage(rptTpl, bands, rst[JV.NODE_CONTROL_COLLECTION], me);
+            } else if (me.crossTab) {
+                rstPage[JV.PROP_CELLS] = me.crossTab.outputAsPreviewPage(rptTpl, bands, rst[JV.NODE_CONTROL_COLLECTION], me);
+            } else if (me.billTab) {
+                rstPage[JV.PROP_CELLS] = me.billTab.outputAsPreviewPage(rptTpl, bands, rst[JV.NODE_CONTROL_COLLECTION], me);
+            }
+            let pageMergeBorder = getPageMergeBorder();
+            if (pageMergeBorder) {
+                rstPage[JV.PROP_PAGE_MERGE_BORDER] = pageMergeBorder;
+            }
+            rst.items.push(rstPage);
+            //2.
+            if (bands[JV.BAND_PROP_MERGE_BAND]) {
+                let mergedBand: any = {}, band = bands[JV.BAND_PROP_MERGE_BAND];
+                mergedBand[JV.PROP_LEFT] = parseInt(band[JV.PROP_LEFT].toFixed(0));
+                mergedBand[JV.PROP_RIGHT] = parseInt(band[JV.PROP_RIGHT].toFixed(0));
+                mergedBand[JV.PROP_TOP] = parseInt(band[JV.PROP_TOP].toFixed(0));
+                mergedBand[JV.PROP_BOTTOM] = parseInt(band[JV.PROP_BOTTOM].toFixed(0));
+                mergedBand[JV.BAND_PROP_STYLE] = band[JV.BAND_PROP_STYLE];
+                rst[JV.BAND_PROP_MERGE_BAND] = mergedBand;
+            }
+        } catch(exception) {
+            console.log(exception);
+        } finally {
+            bands = null;
+        }
+        return rst;
+    };
+
+    outputAsSimpleJSONPageArray(rptTpl: any, dataObj: any, startPage: number, endPage: number, defProperties: any, customizeCfg: any) {
+        let me = this, rst: any = {};
+        if ((startPage > 0) && (startPage <= endPage) && (endPage <= me.totalPages)) {
+            rst[JV.NODE_CONTROL_COLLECTION] = private_buildDftControls(rptTpl, (defProperties === null)?null:defProperties.ctrls);
+            rst[JV.NODE_STYLE_COLLECTION] = private_buildDftStyles(rptTpl, (defProperties === null)?null:defProperties.styles);
+            rst[JV.NODE_FONT_COLLECTION] = private_buildDftFonts(rptTpl, (defProperties === null)?null:defProperties.fonts);
+            rst[JV.NODE_PAGE_INFO] = {};
+            rst[JV.NODE_PAGE_INFO][JV.NODE_MAIN_INFO_RPT_NAME] = rptTpl[JV.NODE_MAIN_INFO][JV.NODE_MAIN_INFO_RPT_NAME];
+            rst[JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE] = JpcCommonHelper.getPageSize(rptTpl);
+            rst[JV.NODE_PAGE_INFO][JV.NODE_MARGINS] = rptTpl[JV.NODE_MAIN_INFO][JV.NODE_MARGINS];
+            rst.items = [];
+            let bands = JpcBand.createNew(rptTpl, defProperties);
+            try {
+                for (let page = startPage; page <= endPage; page++) {
+                    me.runTimePageData.currentPage = page;
+                    me.executeFormulas(JV.RUN_TYPE_BEFORE_OUTPUT, rptTpl, dataObj, me);
+                    rst.items.push(me.outputAsSimpleJSONPage(rptTpl, dataObj, bands, page, rst[JV.NODE_CONTROL_COLLECTION], customizeCfg));
+                }
+                if (bands[JV.BAND_PROP_MERGE_BAND]) {
+                    let mergedBand: any = {}, band = bands[JV.BAND_PROP_MERGE_BAND];
+                    mergedBand[JV.PROP_LEFT] = parseInt(band[JV.PROP_LEFT].toFixed(0));
+                    mergedBand[JV.PROP_RIGHT] = parseInt(band[JV.PROP_RIGHT].toFixed(0));
+                    mergedBand[JV.PROP_TOP] = parseInt(band[JV.PROP_TOP].toFixed(0));
+                    mergedBand[JV.PROP_BOTTOM] = parseInt(band[JV.PROP_BOTTOM].toFixed(0));
+                    mergedBand[JV.BAND_PROP_STYLE] = band[JV.BAND_PROP_STYLE];
+                    rst[JV.BAND_PROP_MERGE_BAND] = mergedBand;
+                }
+            } catch(exception) {
+                console.log(exception);
+            } finally {
+                bands = null;
+            }
+        }
+        return rst;
+    };
+
+    outputAsSimpleJSONPage(rptTpl: any, dataObj: any, bands: any, page: number, controls: any, customizeCfg: any) {
+        let me = this, rst: any = null;
+        function getPageMergeBorder() {
+            let mergeRst: any = null;
+            if (bands[JV.BAND_PROP_MERGE_BAND]) {
+                let mergedBand = bands[JV.BAND_PROP_MERGE_BAND];
+                mergeRst = {};
+                mergeRst[JV.PROP_LEFT] = parseInt(mergedBand[JV.PROP_LEFT].toFixed(0));
+                mergeRst[JV.PROP_RIGHT] = parseInt(mergedBand[JV.PROP_RIGHT].toFixed(0));
+                mergeRst[JV.PROP_TOP] = parseInt(mergedBand[JV.PROP_TOP].toFixed(0));
+                mergeRst[JV.PROP_BOTTOM] = parseInt(mergedBand[JV.PROP_BOTTOM].toFixed(0));
+            }
+            return mergeRst;
+        }
+        if (me.totalPages >= page) {
+            rst = {};
+            rst[JV.PROP_PAGE_SEQ] = page;
+            //rst.cells = [];
+            let adHocMergePos: any = null;
+            if (me.flowTab) {
+                if (me.totalPages - me.exTotalPages >= page) {
+                    if (me.flowTab.paging_option === JV.PAGING_OPTION_INFINITY) {
+                        adHocMergePos = {};
+                    }
+                    rst[JV.PROP_CELLS] = me.flowTab.outputAsSimpleJSONPage(rptTpl, dataObj, page, bands, controls, adHocMergePos, me, customizeCfg);
+                    if (adHocMergePos) {
+                        adHocMergePos[JV.NODE_PAGE_SIZE] = JpcCommonHelper.getPageSize(rptTpl);
+                        rst[JV.PAGE_SPECIAL_MERGE_POS] = adHocMergePos;
+                    }
+
+                } else {
+                    if (!me.isFollowMode) {
+                        rst[JV.PROP_CELLS] = me.flowTabEx.outputAsSimpleJSONPage(rptTpl, dataObj, page - (me.totalPages - me.exTotalPages), bands, controls, adHocMergePos, me, customizeCfg);
+                    }
+                }
+            } else if (me.crossTab) {
+                rst[JV.PROP_CELLS] = me.crossTab.outputAsSimpleJSONPage(rptTpl, dataObj, page, bands, controls, me, customizeCfg);
+            } else if (me.billTab) {
+                rst[JV.PROP_CELLS] = me.billTab.outputAsSimpleJSONPage(rptTpl, dataObj, page, bands, controls, me, customizeCfg);
+            }
+            if (!(me.flowTab && me.flowTab.paging_option === JV.PAGING_OPTION_INFINITY)) {
+                let pageMergeBorder = getPageMergeBorder();
+                if (pageMergeBorder) {
+                    rst[JV.PROP_PAGE_MERGE_BORDER] = pageMergeBorder;
+                }
+            }
+        }
+        return rst;
+    };
+}
+
+function private_buildDftItems(rptTpl: any, dftCollection: any, propArray: any, nodeName: any) {
+    const rst: any = {};
+    if (dftCollection) {
+        for (let i = 0; i < dftCollection.length; i++) {
+            const item: any = {};
+            for (let j = 0; j < propArray.length; j++) {
+                item[propArray[j]] = dftCollection[i][propArray[j]];
+            }
+            rst[dftCollection[i][JV.PROP_ID]] = item;
+        }
+        if (rptTpl && rptTpl[nodeName] && rptTpl[nodeName].length > 0) {
+            for (let i = 0; i < rptTpl[nodeName].length; i++) {
+                const rptDftItem = rptTpl[nodeName][i];
+                if (rst[rptDftItem[JV.PROP_ID]] === undefined) {
+                    const item: any = {};
+                    for (let j = 0; j < propArray.length; j++) {
+                        item[propArray[j]] = rptDftItem[propArray[j]];
+                    }
+                    rst[rptDftItem[JV.PROP_ID]] = item;
+                }
+            }
+        }
+    }
+    return rst;
+}
+
+function private_buildDftControls(rptTpl: any, dftControlCollection: any) {
+    return private_buildDftItems(rptTpl, dftControlCollection, JV.CONTROL_PROPS, JV.NODE_CONTROL_COLLECTION);
+}
+
+function private_buildDftFonts(rptTpl: any, dftFontCollection: any) {
+    return private_buildDftItems(rptTpl, dftFontCollection, JV.FONT_PROPS, JV.NODE_FONT_COLLECTION);
+}
+
+function private_buildDftStyles(rptTpl: any, dftStyleCollection: any) {
+    const rst: any = {};
+    function private_CopyBorder(destItem: any, srcItem: any) {
+        destItem[JV.PROP_LINE_WEIGHT] = srcItem[JV.PROP_LINE_WEIGHT];
+        destItem[JV.PROP_DASH_STYLE] = srcItem[JV.PROP_DASH_STYLE];
+        destItem[JV.PROP_COLOR] = srcItem[JV.PROP_COLOR];
+    }
+    if (dftStyleCollection) {
+        for (let i = 0; i < dftStyleCollection.length; i++) {
+            const item: any = {};
+            if (dftStyleCollection[i][JV.PROP_BORDER_STYLE] && dftStyleCollection[i][JV.PROP_BORDER_STYLE].length > 0) {
+                for (let j = 0; j < dftStyleCollection[i][JV.PROP_BORDER_STYLE].length; j++) {
+                    const borderItem = {};
+                    private_CopyBorder(borderItem, dftStyleCollection[i][JV.PROP_BORDER_STYLE][j]);
+                    item[dftStyleCollection[i][JV.PROP_BORDER_STYLE][j][JV.PROP_POSITION]] = borderItem;
+                }
+            }
+            rst[dftStyleCollection[i][JV.PROP_ID]] = item;
+        }
+        if (rptTpl && rptTpl[JV.NODE_STYLE_COLLECTION] && rptTpl[JV.NODE_STYLE_COLLECTION].length > 0) {
+            for (let i = 0; i < rptTpl[JV.NODE_STYLE_COLLECTION].length; i++) {
+                const rptDftItem = rptTpl[JV.NODE_STYLE_COLLECTION][i];
+                if (rst[rptDftItem[JV.PROP_ID]] === undefined) {
+                    if (rptDftItem[JV.PROP_ID].indexOf('_AutoHeightMerge_Top') > 0) {
+                        const key = rptDftItem[JV.PROP_ID].substr(0, rptDftItem[JV.PROP_ID].indexOf('_AutoHeightMerge_Top'));
+                        if (rst[key]) {
+                            const item: any = {};
+                            if (rst[key][JV.PROP_LEFT]) {
+                                item[JV.PROP_LEFT] = {};
+                                private_CopyBorder(item[JV.PROP_LEFT], rst[key][JV.PROP_LEFT]);
+                            }
+                            if (rst[key][JV.PROP_RIGHT]) {
+                                item[JV.PROP_RIGHT] = {};
+                                private_CopyBorder(item[JV.PROP_RIGHT], rst[key][JV.PROP_RIGHT]);
+                            }
+                            if (rst[key][JV.PROP_TOP]) {
+                                item[JV.PROP_TOP] = {};
+                                private_CopyBorder(item[JV.PROP_TOP], rst[key][JV.PROP_TOP]);
+                            }
+                            rst[rptDftItem[JV.PROP_ID]] = item;
+                        }
+                    } else if (rptDftItem[JV.PROP_ID].indexOf('_AutoHeightMerge_Middle') > 0) {
+                        const key = rptDftItem[JV.PROP_ID].substr(0, rptDftItem[JV.PROP_ID].indexOf('_AutoHeightMerge_Middle'));
+                        if (rst[key]) {
+                            const item: any = {};
+                            if (rst[key][JV.PROP_LEFT]) {
+                                item[JV.PROP_LEFT] = {};
+                                private_CopyBorder(item[JV.PROP_LEFT], rst[key][JV.PROP_LEFT]);
+                            }
+                            if (rst[key][JV.PROP_RIGHT]) {
+                                item[JV.PROP_RIGHT] = {};
+                                private_CopyBorder(item[JV.PROP_RIGHT], rst[key][JV.PROP_RIGHT]);
+                            }
+                            rst[rptDftItem[JV.PROP_ID]] = item;
+                        }
+                    } else if (rptDftItem[JV.PROP_ID].indexOf('_AutoHeightMerge_Bottom') > 0) {
+                        const key = rptDftItem[JV.PROP_ID].substr(0, rptDftItem[JV.PROP_ID].indexOf('_AutoHeightMerge_Bottom'));
+                        if (rst[key]) {
+                            const item: any = {};
+                            if (rst[key][JV.PROP_LEFT]) {
+                                item[JV.PROP_LEFT] = {};
+                                private_CopyBorder(item[JV.PROP_LEFT], rst[key][JV.PROP_LEFT]);
+                            }
+                            if (rst[key][JV.PROP_RIGHT]) {
+                                item[JV.PROP_RIGHT] = {};
+                                private_CopyBorder(item[JV.PROP_RIGHT], rst[key][JV.PROP_RIGHT]);
+                            }
+                            if (rst[key][JV.PROP_BOTTOM]) {
+                                item[JV.PROP_BOTTOM] = {};
+                                private_CopyBorder(item[JV.PROP_BOTTOM], rst[key][JV.PROP_BOTTOM]);
+                            }
+                            rst[rptDftItem[JV.PROP_ID]] = item;
+                        }
+                    } else {
+                        const item: any = {};
+                        for (let j = 0; j < rptDftItem[JV.PROP_BORDER_STYLE].length; j++) {
+                            const borderItem = {};
+                            private_CopyBorder(borderItem, rptDftItem[JV.PROP_BORDER_STYLE][j]);
+                            item[rptDftItem[JV.PROP_BORDER_STYLE][j][JV.PROP_POSITION]] = borderItem;
+                        }
+                        rst[rptDftItem[JV.PROP_ID]] = item;
+                    }
+                }
+            }
+        }
+    }
+    return rst;
+}
+
+export default JpcExClass;

+ 74 - 0
report/src/core/jpc_field.ts

@@ -0,0 +1,74 @@
+'use strict';
+
+import JV from './jpc_value_define';
+
+const JpcField = {
+    createNew(rptTpl: any) {
+        let me = this;
+        let JpcFieldResult = {
+            [JV.NODE_DISCRETE_FIELDS] : {},
+            [JV.NODE_MASTER_FIELDS] : {},
+            [JV.NODE_DETAIL_FIELDS] : {},
+            [JV.NODE_NO_MAPPING_FIELDS] : {},
+            [JV.NODE_MASTER_FIELDS_EX] : {},
+            [JV.NODE_DETAIL_FIELDS_EX] : {}
+        };
+        if (rptTpl[JV.NODE_FIELD_MAP] && rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DISCRETE_FIELDS]) {
+            for (let i = 0; i < rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DISCRETE_FIELDS].length; i++) {
+                me.createSingle(rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DISCRETE_FIELDS][i], JpcFieldResult[JV.NODE_DISCRETE_FIELDS], JV.DATA_DISCRETE_DATA, i);
+            }
+        }
+        if (rptTpl[JV.NODE_FIELD_MAP] && rptTpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS]) {
+            for (let i = 0; i < rptTpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS].length; i++) {
+                me.createSingle(rptTpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS][i], JpcFieldResult[JV.NODE_MASTER_FIELDS], JV.DATA_MASTER_DATA, i);
+            }
+        }
+        if (rptTpl[JV.NODE_FIELD_MAP] && rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS]) {
+            for (let i = 0; i < rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS].length; i++) {
+                me.createSingle(rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS][i], JpcFieldResult[JV.NODE_DETAIL_FIELDS], JV.DATA_DETAIL_DATA, i);
+            }
+        }
+        if (rptTpl[JV.NODE_NO_MAPPING_FIELDS]) {
+            for (let i = 0; i < rptTpl[JV.NODE_NO_MAPPING_FIELDS].length; i++) {
+                me.createSingle(rptTpl[JV.NODE_NO_MAPPING_FIELDS][i], JpcFieldResult[JV.NODE_NO_MAPPING_FIELDS], "NA", JV.BLANK_FIELD_INDEX);
+            }
+        }
+        if (rptTpl[JV.NODE_FIELD_MAP] && rptTpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS_EX]) {
+            for (let i = 0; i < rptTpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS_EX].length; i++) {
+                me.createSingle(rptTpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS_EX][i], JpcFieldResult[JV.NODE_MASTER_FIELDS_EX], JV.DATA_MASTER_DATA_EX, i);
+            }
+        }
+        if (rptTpl[JV.NODE_FIELD_MAP] && rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS_EX]) {
+            for (let i = 0; i < rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS_EX].length; i++) {
+                me.createSingle(rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS_EX][i], JpcFieldResult[JV.NODE_DETAIL_FIELDS_EX], JV.DATA_DETAIL_DATA_EX, i);
+            }
+        }
+        //NODE_MASTER_FIELDS_EX
+        return JpcFieldResult;
+    },
+    createSingle(fieldNode: any, parentObj: any, dataNodeName: string, sequence: number) {
+        if (fieldNode && fieldNode[JV.PROP_ID]) {
+            let item = {
+                [JV.PROP_ID] : fieldNode[JV.PROP_ID],
+                [JV.PROP_NAME] : fieldNode[JV.PROP_NAME],
+                [JV.PROP_DATA_TYPE] : fieldNode[JV.PROP_DATA_TYPE],
+                [JV.PROP_PRECISION] : null
+            };
+            if (fieldNode[JV.PROP_PRECISION]) {
+                item[JV.PROP_PRECISION] = {};
+                item[JV.PROP_PRECISION].type = fieldNode[JV.PROP_PRECISION].type;
+                if (fieldNode[JV.PROP_PRECISION].type === 'fixed') {
+                    item[JV.PROP_FIXED_PRECISION_AMT] = fieldNode[JV.PROP_FIXED_PRECISION_AMT];
+                } else {
+                    item[JV.PROP_PRECISION][JV.PROP_FLEXIBLE_REF_FILED_ID] = fieldNode[JV.PROP_PRECISION][JV.PROP_FLEXIBLE_REF_FILED_ID];
+                    item.flexiblePrecisionRefObj = fieldNode.flexiblePrecisionRefObj;
+                }
+            }
+            item.DataNodeName = dataNodeName;
+            item.DataSeq = sequence;
+            parentObj[JV.PROP_ID + "_" + fieldNode[JV.PROP_ID]] = item;
+        }
+    }
+};
+
+export default JpcField;

Файловите разлики са ограничени, защото са твърде много
+ 1505 - 0
report/src/core/jpc_flow_tab.ts


+ 22 - 0
report/src/core/jpc_function.ts

@@ -0,0 +1,22 @@
+'use strict';
+
+import JV from './jpc_value_define';
+
+const JpcFunc = {
+    createNew(rptTpl: any) {
+        let rst = [];
+        if (rptTpl[JV.NODE_FORMULAS]) {
+            for (let i = 0; i < rptTpl[JV.NODE_FORMULAS].length; i++) {
+                let item = {...rptTpl[JV.NODE_FORMULAS][i]}; // 新的copy规范
+                // let propArr = Object.getOwnPropertyNames(rptTpl[JV.NODE_FORMULAS][i]);
+                // for (let key of propArr) {
+                //     item[key] = rptTpl[JV.NODE_FORMULAS][i][key];
+                // }
+                rst.push(item);
+            }
+        }
+        return rst;
+    }
+};
+
+export default JpcFunc;

+ 30 - 0
report/src/core/jpc_param.ts

@@ -0,0 +1,30 @@
+'use strict';
+
+import JV from './jpc_value_define';
+
+const JpcParam = {
+    createNew(rptTpl: any) {
+        let JpcParamResult = {};
+        let me = this;
+        if (rptTpl[JV.NODE_DISCRETE_PARAMS]) {
+            for (let i = 0; i < rptTpl[JV.NODE_DISCRETE_PARAMS].length; i++) {
+                me.createSingle(rptTpl[JV.NODE_DISCRETE_PARAMS][i], JpcParamResult, i);
+            }
+        }
+        return JpcParamResult;
+    },
+    createSingle(paramNode: any, parentObj: any, sequence: number) {
+        if (paramNode && paramNode[JV.PROP_ID]) {
+            const item = {
+                [JV.PROP_ID] : paramNode[JV.PROP_ID],
+                [JV.PROP_NAME] : paramNode[JV.PROP_NAME],
+                [JV.PROP_DATA_TYPE] : paramNode[JV.PROP_DATA_TYPE],
+                [JV.PROP_DFT_VALUE] : (paramNode[JV.PROP_DFT_VALUE]) ? paramNode[JV.PROP_DFT_VALUE] : null,
+                DataSeq : sequence
+            };
+            parentObj[JV.PROP_ID + '_' + paramNode[JV.PROP_ID]] = item;
+        }
+    }
+};
+
+export default JpcParam;

+ 182 - 0
report/src/core/jpc_rte.ts

@@ -0,0 +1,182 @@
+/**
+ * Created by Tony on 2016/12/28.
+ */
+'use strict';
+
+import strUtil from '../public/stringUtil';
+import scMathUtil from '../public/scMathUtil';
+import JV from './jpc_value_define';
+
+const JE = {
+    $STR_UTIL: strUtil,
+    $SC_MATH_UTIL: scMathUtil,
+    F: function(fID: number, $CURRENT_RPT: any) {
+        let rst = null;
+        if ($CURRENT_RPT && ($CURRENT_RPT.fields[JV.NODE_DETAIL_FIELDS][JV.PROP_ID + "_" + fID])) {
+            rst = $CURRENT_RPT.fields[JV.NODE_DETAIL_FIELDS][JV.PROP_ID + "_" + fID];
+        } else if ($CURRENT_RPT && ($CURRENT_RPT.fields[JV.NODE_MASTER_FIELDS][JV.PROP_ID + "_" + fID])) {
+            rst = $CURRENT_RPT.fields[JV.NODE_MASTER_FIELDS][JV.PROP_ID + "_" + fID];
+        } else if ($CURRENT_RPT && ($CURRENT_RPT.fields[JV.NODE_MASTER_FIELDS_EX][JV.PROP_ID + "_" + fID])) {
+            rst = $CURRENT_RPT.fields[JV.NODE_MASTER_FIELDS_EX][JV.PROP_ID + "_" + fID];
+        } else if ($CURRENT_RPT && ($CURRENT_RPT.fields[JV.NODE_DETAIL_FIELDS_EX][JV.PROP_ID + "_" + fID])) {
+            rst = $CURRENT_RPT.fields[JV.NODE_DETAIL_FIELDS_EX][JV.PROP_ID + "_" + fID];
+        } else if ($CURRENT_RPT && ($CURRENT_RPT.fields[JV.NODE_DISCRETE_FIELDS][JV.PROP_ID + "_" + fID])) {
+            rst = $CURRENT_RPT.fields[JV.NODE_DISCRETE_FIELDS][JV.PROP_ID + "_" + fID];
+        } else if ($CURRENT_RPT && ($CURRENT_RPT.fields[JV.NODE_NO_MAPPING_FIELDS][JV.PROP_ID + "_" + fID])) {
+            rst = $CURRENT_RPT.fields[JV.NODE_NO_MAPPING_FIELDS][JV.PROP_ID + "_" + fID];
+        } else {
+            rst = {msg: "the Field-ID is not valid, no result could be found!"};
+        }
+        return rst;
+    },
+    P: function(pID: number, $CURRENT_RPT: any) {
+        let rst = null;
+        if ($CURRENT_RPT && ($CURRENT_RPT.params[JV.PROP_ID + "_" + pID])) {
+            rst = $CURRENT_RPT.params[JV.PROP_ID + "_" + pID];
+        } else {
+            rst = {msg: "the Param-ID is not valid, no result was found!"};
+        }
+        return rst;
+    },
+    getCurrentPage: function ($CURRENT_RPT: any) {
+        let rst = 0;
+        if ($CURRENT_RPT) {
+            rst = $CURRENT_RPT.runTimePageData.currentPage;
+        }
+        return rst;
+    },
+    getTotalPage: function ($CURRENT_RPT: any) {
+        let rst = 0;
+        if ($CURRENT_RPT) {
+            rst = $CURRENT_RPT.totalPages;
+        }
+        return rst;
+    },
+    getFieldDataLen: function(field: any, dataObj: any) {
+        let rst = 0;
+        if (field.DataNodeName === 'NA') {
+            if (!field[JV.PROP_AD_HOC_DATA]) {
+                field[JV.PROP_AD_HOC_DATA] = [];
+            }
+            rst = field[JV.PROP_AD_HOC_DATA].length;
+        } else {
+            if (!field.DataNodeName) {
+                // that means this is a self-defined discrete field!
+                field.DataNodeName = JV.DATA_DISCRETE_DATA;
+                field.DataSeq = dataObj[JV.DATA_DISCRETE_DATA];
+                dataObj[JV.DATA_DISCRETE_DATA].push([]);
+            }
+            rst = dataObj[field.DataNodeName][field.DataSeq].length;
+        }
+        return rst;
+    },
+    setFieldValue: function (field: any, dataObj: any, valIdx: number, newValue: any) {
+        if (field.DataNodeName === "NA") {
+            if (!field[JV.PROP_AD_HOC_DATA]) {
+                field[JV.PROP_AD_HOC_DATA] = [];
+            }
+            field[JV.PROP_AD_HOC_DATA][valIdx] = newValue;
+        } else if (!field.DataNodeName) {
+            //that means this is a self-defined discrete field!
+            field.DataNodeName = JV.DATA_DISCRETE_DATA;
+            field.DataSeq = dataObj[JV.DATA_DISCRETE_DATA].length;
+            dataObj[JV.DATA_DISCRETE_DATA].push([]);
+            dataObj[field.DataNodeName][field.DataSeq][valIdx] = newValue;
+        } else {
+            dataObj[field.DataNodeName][field.DataSeq][valIdx] = newValue;
+        }
+    },
+    getFieldValue: function (field: any, dataObj: any, valIdx: number, newVal: any) {
+        let rst = null;
+        if (field.DataNodeName === "NA") {
+            if (!field[JV.PROP_AD_HOC_DATA]) {
+                field[JV.PROP_AD_HOC_DATA] = [];
+            }
+            if (field[JV.PROP_AD_HOC_DATA].length > valIdx) {
+                rst = field[JV.PROP_AD_HOC_DATA][valIdx];
+            } else {
+                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];
+                }
+            }
+        } else {
+            if (!field.DataNodeName) {
+                //that means this is a self-defined discrete field!
+                field.DataNodeName = JV.DATA_DISCRETE_DATA;
+                field.DataSeq = dataObj[JV.DATA_DISCRETE_DATA];
+                dataObj[JV.DATA_DISCRETE_DATA].push([]);
+            }
+            if (dataObj[field.DataNodeName][field.DataSeq].length > valIdx) {
+                rst = dataObj[field.DataNodeName][field.DataSeq][valIdx];
+            } else {
+                if (newVal === null && dataObj[field.DataNodeName][field.DataSeq].length > 0) {
+                    rst = dataObj[field.DataNodeName][field.DataSeq][dataObj[field.DataNodeName][field.DataSeq].length - 1];
+                }
+            }
+        }
+        if (rst === null || rst === undefined) rst = newVal;
+        return rst;
+    },
+    getFieldValueArray: function(field: any, dataObj: any) {
+        let rst = null;
+        if (field.DataNodeName === 'NA') {
+            if (!field[JV.PROP_AD_HOC_DATA]) {
+                field[JV.PROP_AD_HOC_DATA] = [];
+            }
+            rst = field[JV.PROP_AD_HOC_DATA];
+        } else {
+            if (!field.DataNodeName) {
+                // that means this is a self-defined discrete field!
+                field.DataNodeName = JV.DATA_DISCRETE_DATA;
+                field.DataSeq = dataObj[JV.DATA_DISCRETE_DATA];
+                dataObj[JV.DATA_DISCRETE_DATA].push([]);
+            }
+            rst = dataObj[field.DataNodeName][field.DataSeq];
+        }
+        if (rst === null || rst === undefined) rst = [];
+        return rst;
+    },
+    setFieldValueArray: function(field: any, dataObj: any, newArr: any[]) {
+        if (newArr instanceof Array) {
+            if (field.DataNodeName === 'NA') {
+                field[JV.PROP_AD_HOC_DATA] = newArr;
+            } else {
+                if (!field.DataNodeName) {
+                    // that means this is a self-defined discrete field!
+                    field.DataNodeName = JV.DATA_DISCRETE_DATA;
+                    field.DataSeq = dataObj[JV.DATA_DISCRETE_DATA].length;
+                    dataObj[JV.DATA_DISCRETE_DATA].push([]);
+                }
+                dataObj[field.DataNodeName][field.DataSeq] = newArr;
+            }
+        }
+    },
+    removeFieldValue: function (field: any, dataObj: any, valIdx: number) {
+        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: any, dataObj: any, valIdx: number, newValue: any) {
+        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 (field[JV.PROP_AD_HOC_DATA].length <= valIdx) {
+                field[JV.PROP_AD_HOC_DATA][field[JV.PROP_AD_HOC_DATA].length] = newValue;
+            }
+        } else {
+            if (dataObj[field.DataNodeName][field.DataSeq].length > valIdx && valIdx >= 0) {
+                dataObj[field.DataNodeName][field.DataSeq].splice(valIdx, 0, newValue);
+            } else if (dataObj[field.DataNodeName][field.DataSeq].length <= valIdx) {
+                dataObj[field.DataNodeName][field.DataSeq][dataObj[field.DataNodeName][field.DataSeq].length] = newValue;
+            }
+        }
+    }
+};
+
+export default JE;

+ 331 - 0
report/src/core/jpc_value_define.ts

@@ -0,0 +1,331 @@
+'use strict';
+
+const VAL_DEF = {
+    NODE_CROSS_INFO: '交叉表_信息',
+    NODE_CROSS_ROW: '交叉行',
+    NODE_CROSS_COL: '交叉列',
+    NODE_CROSS_CONTENT: '交叉数据',
+    NODE_CROSS_ROW_SUM: '交叉行合计',
+    NODE_CROSS_COL_SUM: '交叉列合计',
+    NODE_CROSS_ROW_EXT: '交叉行拓展',
+    NODE_CROSS_ROW_SUM_EXT: '交叉行拓展合计',
+    NODE_CROSS_ROW_AD_HOC: '交叉行AD_HOC',
+    NODE_FIELD_MAP: '指标_数据_映射',
+    NODE_DISCRETE_FIELDS: '离散指标_集合',
+    NODE_NO_MAPPING_FIELDS: '无映射离散指标_集合',
+    NODE_DISCRETE_PARAMS: '离散参数_集合',
+    NODE_MASTER_FIELDS: '主数据指标_集合',
+    NODE_MASTER_FIELDS_EX: '主数据指标_拓展集合',
+    NODE_DETAIL_FIELDS: '从数据指标_集合',
+    NODE_DETAIL_FIELDS_EX: '从数据指标_拓展集合',
+    NODE_BAND_COLLECTION: '布局框_集合',
+    NODE_FORMULAS: '计算式_集合',
+    NODE_EVENTS: '事件_集合',
+    NODE_DISCRETE_INFO: '离散信息',
+    NODE_BILL_INFO: '账单式表_信息',
+    NODE_BILL_CONTENT : '账单式表_数据',
+    NODE_FLOW_INFO: '流水式表_信息',
+    NODE_FLOW_INFO_EX: '流水式表_拓展信息',
+    NODE_FLOW_GROUP: '流水式表_分组信息',
+    NODE_FLOW_SEG_SUM: '流水式表_段统计信息',
+    NODE_FLOW_PAGE_SUM: '流水式表_页统计信息',
+    NODE_FLOW_COLUMN : '流水式表_列',
+    NODE_FLOW_CONTENT : '流水式表_数据',
+    PROP_MULTI_COLUMN: '多列显示数量',
+    PROP_FLOW_EX_DISPLAY_MODE: '流水拓展显示模式',
+    DISPLAY_MODE_INDEPENDENT: '单独模式',
+    DISPLAY_MODE_FOLLOW: '紧随模式',
+
+    NODE_MAIN_INFO: '主信息',
+    NODE_MAIN_INFO_RPT_NAME: '报表名称',
+    NODE_PAGE_INFO: '打印页面_信息',
+    NODE_PAGE_SIZE: '纸张宽高',
+    NODE_MARGINS: '页边距',
+    NODE_RPT_BIZ_TYPE: '报表业务类型',
+    NODE_BIZ_TYPE_AUDI: '审核类型',
+    NODE_BIZ_TYPE_SUM: '汇总类型',
+    NODE_BIZ_TYPE_DETAIL: '明细类型',
+
+    NODE_MAP_DATA_HANDLE_INFO: '映射数据预处理',
+    PROP_DATA_KEY: '映射数据对象',
+    PROP_PARENT_DATA_KEY: '父映射数据对象',
+    PROP_PARENT_CHILD_SORT_KEY: '父子排序键',
+    PROP_PARENT_SORT_KEYS: '父排序键值集',
+    PROP_CHILD_SORT_KEYS: '子排序键值集',
+    PROP_OTHER_SUB_SORT: '其他子排序',
+    PROP_OTHER_SUB_FILTER: '其他子过滤',
+    PROP_HANDLE_TYPE: '预处理类型',
+    PROP_FILTER_KEYS: '过滤键值集',
+    PROP_FILTER_TOP_BILLS_NODES: '清单顶节点集',
+    PROP_FILTER_OTHER_BILLS_NODES: '其他清单节点集',
+    PROP_FILTER_COMPARE_OBJ: 'compareObjKey',
+    PROP_FILTER_COMPARE_OBJ_KEY: 'compareObjIdKey',
+    PROP_FILTER_COMPARE_VAL: 'compareValue',
+    PROP_FILTER_CONDITION: '判断条件',
+    PROP_HANDLE_TYPE_FILTER: '过滤',
+    PROP_HANDLE_TYPE_SUM: '合计',
+    PROP_HANDLE_TYPE_SORT: '排序',
+    PROP_HANDLE_TYPE_ADD_DUMMY: '增加Dummy数据',
+    PROP_HANDLE_TYPE_ADJUST: '数据调整',
+    PROP_HANDLE_TYPE_BILLS_DATA_MOVE: '量材数据转移',
+    PROP_HANDLE_TYPE_COMPONENT_MOVE: '组成物数据转移',
+    PROP_HANDLE_TYPE_COMPONENT_REPLACEMENT: '组成物替换',
+    PROP_HANDLE_TYPE_PRECISION: '合计精度',
+
+    PROP_ADJUST_COLLECTION: '数据调整集',
+    PROP_ADJUST_ACTION: 'action',
+    PROP_ADJUST_ACTION_VAL: 'actionValue',
+    PROP_DUMMY_COLLECTION: 'Dummy数据集',
+    PROP_DUMMY_VAL: 'Dummy数据对象值',
+    PROP_FREQUENCY: '频率',
+    PROP_GRP_KEYS: 'GrpKeyIds',
+    PROP_SORT_TYPE: '排序方式',
+    PROP_SORT_TYPE_SELF_DEFINE_LOGIC: '自定义逻辑',
+    PROP_SORT_KEYS: '排序键值集',
+    PROP_SUM_GROUP_KEYS: '分组键值集',
+    PROP_SUM_SUM_KEYS: '统计键值集',
+    PROP_SUM_CALC_AHEAD: '统计前计算',
+    PROP_SUM_CACL_TYPE: '计算类型',
+    PROP_FIELD_EXP_MAP: 'mapExpression',
+    PROP_PRECISION: 'Precision',
+    PROP_FIXED_PRECISION_AMT: 'fixedPrecisionNum',
+    PROP_FIELD_EXP_FIXED_MAP: 'fixedMapExpression',
+    PROP_FIELD_EXP_FLEXIBLE_MAP: 'flexibleMapExpression',
+    PROP_FLEXIBLE_REF_FILED_ID: 'flexibleRefFieldID',
+
+    NODE_FONT_COLLECTION: 'font_collection',
+    NODE_STYLE_COLLECTION: 'style_collection',
+    NODE_CONTROL_COLLECTION: 'control_collection',
+
+    PROP_ID: 'ID',
+    PROP_AD_HOC_DATA: 'data_field',
+    PROP_CMN_HEIGHT: 'CommonHeight',
+    PROP_CMN_WIDTH: 'CommonWidth',
+    PROP_BAND_NAME: 'BandName',
+    PROP_BAND_NORMAL_ONLY: 'normalOnly',
+    PROP_BAND_EX_ONLY: 'exOnly',
+    PROP_BAND_EX_JOIN_AFTER: 'isJoinAfter',
+
+    PROP_UNITS: '单位',
+    PROP_PAGE_SIZE: '页规格',
+    PROP_ORIENTATION: '方向',
+    PROP_LEFT: 'Left',
+    PROP_RIGHT: 'Right',
+    PROP_TOP: 'Top',
+    PROP_BOTTOM: 'Bottom',
+    PROP_DATA_TYPE: 'DataType',
+    PROP_NAME: 'Name',
+    PROP_DFT_VALUE: 'Default_Value',
+    PROP_EXPRESSION: 'expression',
+    PROP_RUN_TYPE: 'run_type',
+    PROP_BORDER_STYLE: 'border_style',
+    PROP_POSITION: 'Position',
+    PROP_HIDDEN: 'Hidden',
+    PROP_IS_SERIAL: 'isSerial',
+    PROP_IS_MERGE: 'isMerge',
+    PROP_COMBINE_TYPE: 'combineType',
+    PROP_IS_AUTO_HEIGHT: 'isAutoHeight',
+    PROP_FONT: 'font',
+    PROP_CONTROL: 'control',
+    PROP_STYLE: 'style',
+    PROP_VALUE: 'Value',
+    PROP_LABEL: 'Label',
+    PROP_AREA: 'area',
+    PROP_DISCRETE_FIELDS: 'discrete_field_s',
+    PROP_FLOW_FIELDS: 'flow_field_s',
+    PROP_BILL_FIELDS: 'bill_field_s',
+    PROP_CROSS_FIELDS: 'cross_field_s',
+    PROP_CROSS_DISPLAY_ORDER: 'cross_display_order',
+    PROP_GROUP_FIELDS: 'group_field_s', //用来分组的指标(如按清单、定额etc...)
+    PROP_GROUP_LINES: 'group_lines',    //显示分组行,因分组的特殊性,分组的数据当成流水数据一样(行高相同),group_lines里的每一条数据占用流水的一整行,里面再细分(指标/text)
+    PROP_GROUP_SUM_KEYS: 'SumKey_S',
+    PROP_SUM_KEY: 'SumKey',
+    PROP_SUM_FIELDS: 'sum_field_s',
+    PROP_TEXTS: 'text_s',
+    PROP_TEXT: 'text',
+    PROP_PARAMS: 'param_s',
+    PROP_FIELD_ID: 'FieldID',
+    PROP_PARAM_ID: 'ParamID',
+    PROP_PREFIX: 'Prefix',
+    PROP_SUFFIX: 'Suffix',
+    PROP_FORMAT: 'Format',
+
+    PROP_SHOW_ZERO: 'ShowZero',
+    PROP_EXTENSION_TYPE: 'ExtType',
+
+    PROP_CALCULATION: 'CalculationType',
+    PROP_H_CALCULATION: 'H_CalculationType',
+    PROP_V_CALCULATION: 'V_CalculationType',
+    PROP_FIT_AREA: 'isFitArea',
+
+    IDX_LEFT: 0,
+    IDX_TOP: 1,
+    IDX_RIGHT: 2,
+    IDX_BOTTOM: 3,
+
+    BAND_PROP_NAME: 'Name',
+    BAND_PROP_MERGE_BAND: 'MergeBand',
+    BAND_PROP_STYLE: 'style',
+    BAND_PROP_CONTROL: 'control',
+    BAND_PROP_HEIGHT: 'Height',
+    BAND_PROP_WIDTH: 'Width',
+    BAND_PROP_DISPLAY_TYPE: 'DisplayType',
+    BAND_PROP_ALIGNMENT: 'Alignment',
+    BAND_PROP_MERGE_BORDER: 'MergeBorder',
+    BAND_PROP_SUB_BANDS: 'band_s',
+
+    MEASUREMENT: {
+        PIXEL:['像素点', '象素点', 'PIXEL'],
+        CM: ['厘米', 'CM'],
+        INCH: ['英寸','INCH']
+    },
+
+    PROP_IS_ID: 'isID',
+    PROP_ID_SEQ: 'IDSeq',
+
+    TAB_FIELD_PROP_SORT: 'Sort',
+    TAB_FIELD_PROP_SORT_VAL_NOSORT: 'no_sort',
+    TAB_FIELD_PROP_SORT_VAL_ASC: 'ascend',
+    TAB_FIELD_PROP_SORT_VAL_DESC: 'descend',
+
+    DATA_DISCRETE_DATA: 'discrete_data',
+    DATA_MASTER_DATA: 'master_data',
+    DATA_DETAIL_DATA: 'detail_data',
+    DATA_MASTER_DATA_EX: 'master_data_ex',
+    DATA_DETAIL_DATA_EX: 'detail_data_ex',
+
+    BLANK_FIELD_INDEX: -10,
+    BLANK_VALUE_INDEX: -100,
+    BLANK_PAGE_VALUE_INDEX: -200,
+
+    PROP_SEG_GRP_IDX: 'segGrpRecStartIdx',
+    PROP_PRE_ADD_GRP_REC_INFO: 'preAddPageGrpInfo',
+    PROP_INSERTED_GRP_REC: 'insertedGrpRecAmt',
+    PROP_GRP_LINES: 'group_lines_amt',
+
+    RUN_TYPE_BEFORE_ANALYZING: 'before_analyzing',
+    RUN_TYPE_BEFORE_PAGING: 'before_paging',
+    RUN_TYPE_BEFORE_OUTPUT: 'before_output',
+    RUN_TYPE_BEFORE_COMBINE: 'before_combine',
+    RUN_TYPE_AFTER_COMBINE: 'after_combine',
+
+    RUN_TYPE_BEFORE_GROUP_TEXT_OUT: 'before_group_text_output',
+
+    PAGE_STATUS: ['EveryPage','FirstPage', 'LastPage', 'SegmentStart', 'SegmentEnd', 'Group', 'CrossRowEnd', 'CrossColEnd'],
+
+    CONTROL_PROPS: ['Shrink', 'ShowZero', 'Horizon', 'Vertical', 'Wrap', 'VerticalForExcel', 'ShrinkFirst', 'CloseOutput'],
+    CONTROL_PROP_IDX_SHRINK: 0,
+    CONTROL_PROP_IDX_SHOW_ZERO: 1,
+    CONTROL_PROP_IDX_HORIZON: 2,
+    CONTROL_PROP_IDX_VERTICAL: 3,
+    CONTROL_PROP_IDX_WRAP: 4,
+    CONTROL_PROP_IDX_VERTICAL_EXCEL: 5,
+    CONTROL_PROP_IDX_SHRINK_FIRST: 6,
+    CONTROL_PROP_IDX_CLOSE_OUTPUT: 7,
+    BORDER_STYLE_PROPS: ['LineWeight', 'DashStyle', 'Color'],
+    PROP_LINE_WEIGHT: 'LineWeight',
+    PROP_DASH_STYLE: 'DashStyle',
+    PROP_COLOR: 'Color',
+    FONT_PROPS: ['Name', 'FontHeight', 'FontColor', 'FontBold', 'FontItalic', 'FontUnderline', 'FontStrikeOut', 'FontAngle'],
+    FONT_PROP_IDX_NAME: 0,
+    FONT_PROP_IDX_HEIGHT: 1,
+    FONT_PROP_IDX_COLOR: 2,
+    FONT_PROP_IDX_BOLD: 3,
+    FONT_PROP_IDX_ITALIC: 4,
+    FONT_PROP_IDX_UNDERLINE: 5,
+    FONT_PROP_IDX_STRIKEOUT: 6,
+    FONT_PROP_IDX_ANGLE: 7,
+
+    STATUS_NORMAL: 0,
+    STATUS_REPORT_START: 1,
+    STATUS_REPORT_END: 2,
+    STATUS_SEGMENT_START: 3,
+    STATUS_SEGMENT_END: 4,
+    STATUS_GROUP: 5,
+    STATUS_CROSS_ROW_END: 6,
+    STATUS_CROSS_COL_END: 7,
+
+    LAYOUT: ['Top','Bottom', 'Left', 'Right', 'Fulfill'],
+    LAYOUT_TOP: 0,
+    LAYOUT_BOTTOM: 1,
+    LAYOUT_LEFT: 2,
+    LAYOUT_RIGHT: 3,
+    LAYOUT_FULFILL: 4,
+
+    OUTPUT_ALIGN: {
+        H: ['left', 'center', 'right'],
+        V: ['top', 'center', 'bottom']
+    },
+    H_ALIGN_IDX_LEFT : 0,
+    H_ALIGN_IDX_CENTER : 1,
+    H_ALIGN_IDX_RIGHT : 2,
+    V_ALIGN_IDX_TOP : 0,
+    V_ALIGN_IDX_CENTER : 1,
+    V_ALIGN_IDX_BOTTOM : 2,
+
+    CAL_TYPE:['percentage','abstract'],
+    CAL_TYPE_PERCENTAGE: 0,
+    CAL_TYPE_ABSTRACT: 1,
+
+    EVENT_TYPE: ['GRP_ON_CREATE', 'FLOW_CONTENT_ON_CREATE'],
+    EVENT_IDX_GRP_ON_CREATE: 0,
+    EVENT_IDX_FLOW_CONTENT_ON_CREATE: 1,
+
+    PAGE_ORIENTATION_V_FIRST: 0,
+    PAGE_ORIENTATION_H_FIRST: 1,
+
+    ORIENTATION_PORTRAIT: 'PORTRAIT',
+    ORIENTATION_LANDSCAPE: 'LANDSCAPE',
+    ORIENTATION_PORTRAIT_CHN: '纵向',
+    ORIENTATION_LANDSCAPE_CHN: '横向',
+    SIZE_A3: [11.69, 16.54],
+    SIZE_A4: [8.27, 11.69],
+    SIZE_A5: [5.83, 8.27],
+    SIZE_B5: [6.93, 9.84],
+    SIZE_LETTER: [8.5, 11.0],
+    SIZE_LEGAL: [8.5, 14.0],
+    SIZE_16K: [7.75, 10.75],
+    SIZE_EXECUTIVE: [7.25, 10.5],
+
+    OUTPUT_OFFSET: [2,2,1,3],
+    OFFSET_IDX_LEFT: 0,
+    OFFSET_IDX_RIGHT: 1,
+    OFFSET_IDX_TOP: 2,
+    OFFSET_IDX_BOTTOM: 3,
+
+    PROP_PAGE_SEQ: 'page_seq',
+    PROP_PAGE_MERGE_BORDER: 'page_merge_border',
+    PROP_CELLS: 'cells',
+
+    PAGING_OPTION_NORMAL: 'normal',
+    PAGING_OPTION_INFINITY: 'infinity',
+
+    OUTPUT_TYPE_NORMAL: 'normal',
+    OUTPUT_TYPE_SVG: 'svg',
+    OUTPUT_TYPE_PDF: 'pdf',
+    OUTPUT_TYPE_EXCEL: 'excel',
+
+    DISPLAY_VAL_TYPE_NORMAL: 0,
+    DISPLAY_VAL_TYPE_GROUP: 1,
+    DISPLAY_VAL_TYPE_AUTO_HEIGHT: 2,
+
+    TYPE_FOLLOW_MODE: 1,
+
+    PAGE_SELF_DEFINE: '自定义',
+    PAGE_SPECIAL_MERGE_POS: 'page_merge_pos',
+
+    PAGES_SIZE_STR: ['A3', 'A4', 'A5', 'B5', 'LETTER', 'LEGAL', 'EXECUTIVE', '16K'],
+    PAGES_SIZE_IDX: [8, 9, 11, 13, 1, 5, 7, 93],
+    PAGES_SIZE: [[11.69, 16.54], [8.27, 11.69], [5.83, 8.27], [6.93, 9.84], [8.5, 11.0], [8.5, 14.0], [7.25, 10.5], [7.25, 10.5]],
+
+    HUNDRED_PERCENT : 100.0,
+
+    VERTICAL_ANGLE: '90',
+    ANTI_VERTICAL_ANGLE: '-90',
+    VERTICAL_ANGLE_INT: 90,
+    ANTI_VERTICAL_ANGLE_INT: -90,
+
+    LAST_DEF: ''
+};
+
+export default VAL_DEF;

+ 281 - 0
report/src/excel_util/excel_base_files/theme1.xml

@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office 主题">
+	<a:themeElements>
+		<a:clrScheme name="Office">
+			<a:dk1>
+				<a:sysClr val="windowText" lastClr="000000"/>
+			</a:dk1>
+			<a:lt1>
+				<a:sysClr val="window" lastClr="FFFFFF"/>
+			</a:lt1>
+			<a:dk2>
+				<a:srgbClr val="1F497D"/>
+			</a:dk2>
+			<a:lt2>
+				<a:srgbClr val="EEECE1"/>
+			</a:lt2>
+			<a:accent1>
+				<a:srgbClr val="4F81BD"/>
+			</a:accent1>
+			<a:accent2>
+				<a:srgbClr val="C0504D"/>
+			</a:accent2>
+			<a:accent3>
+				<a:srgbClr val="9BBB59"/>
+			</a:accent3>
+			<a:accent4>
+				<a:srgbClr val="8064A2"/>
+			</a:accent4>
+			<a:accent5>
+				<a:srgbClr val="4BACC6"/>
+			</a:accent5>
+			<a:accent6>
+				<a:srgbClr val="F79646"/>
+			</a:accent6>
+			<a:hlink>
+				<a:srgbClr val="0000FF"/>
+			</a:hlink>
+			<a:folHlink>
+				<a:srgbClr val="800080"/>
+			</a:folHlink>
+		</a:clrScheme>
+		<a:fontScheme name="Office">
+			<a:majorFont>
+				<a:latin typeface="Cambria"/>
+				<a:ea typeface=""/>
+				<a:cs typeface=""/>
+				<a:font script="Jpan" typeface="MS Pゴシック"/>
+				<a:font script="Hang" typeface="맑은 고딕"/>
+				<a:font script="Hans" typeface="宋体"/>
+				<a:font script="Hant" typeface="新細明體"/>
+				<a:font script="Arab" typeface="Times New Roman"/>
+				<a:font script="Hebr" typeface="Times New Roman"/>
+				<a:font script="Thai" typeface="Tahoma"/>
+				<a:font script="Ethi" typeface="Nyala"/>
+				<a:font script="Beng" typeface="Vrinda"/>
+				<a:font script="Gujr" typeface="Shruti"/>
+				<a:font script="Khmr" typeface="MoolBoran"/>
+				<a:font script="Knda" typeface="Tunga"/>
+				<a:font script="Guru" typeface="Raavi"/>
+				<a:font script="Cans" typeface="Euphemia"/>
+				<a:font script="Cher" typeface="Plantagenet Cherokee"/>
+				<a:font script="Yiii" typeface="Microsoft Yi Baiti"/>
+				<a:font script="Tibt" typeface="Microsoft Himalaya"/>
+				<a:font script="Thaa" typeface="MV Boli"/>
+				<a:font script="Deva" typeface="Mangal"/>
+				<a:font script="Telu" typeface="Gautami"/>
+				<a:font script="Taml" typeface="Latha"/>
+				<a:font script="Syrc" typeface="Estrangelo Edessa"/>
+				<a:font script="Orya" typeface="Kalinga"/>
+				<a:font script="Mlym" typeface="Kartika"/>
+				<a:font script="Laoo" typeface="DokChampa"/>
+				<a:font script="Sinh" typeface="Iskoola Pota"/>
+				<a:font script="Mong" typeface="Mongolian Baiti"/>
+				<a:font script="Viet" typeface="Times New Roman"/>
+				<a:font script="Uigh" typeface="Microsoft Uighur"/>
+			</a:majorFont>
+			<a:minorFont>
+				<a:latin typeface="Calibri"/>
+				<a:ea typeface=""/>
+				<a:cs typeface=""/>
+				<a:font script="Jpan" typeface="MS Pゴシック"/>
+				<a:font script="Hang" typeface="맑은 고딕"/>
+				<a:font script="Hans" typeface="宋体"/>
+				<a:font script="Hant" typeface="新細明體"/>
+				<a:font script="Arab" typeface="Arial"/>
+				<a:font script="Hebr" typeface="Arial"/>
+				<a:font script="Thai" typeface="Tahoma"/>
+				<a:font script="Ethi" typeface="Nyala"/>
+				<a:font script="Beng" typeface="Vrinda"/>
+				<a:font script="Gujr" typeface="Shruti"/>
+				<a:font script="Khmr" typeface="DaunPenh"/>
+				<a:font script="Knda" typeface="Tunga"/>
+				<a:font script="Guru" typeface="Raavi"/>
+				<a:font script="Cans" typeface="Euphemia"/>
+				<a:font script="Cher" typeface="Plantagenet Cherokee"/>
+				<a:font script="Yiii" typeface="Microsoft Yi Baiti"/>
+				<a:font script="Tibt" typeface="Microsoft Himalaya"/>
+				<a:font script="Thaa" typeface="MV Boli"/>
+				<a:font script="Deva" typeface="Mangal"/>
+				<a:font script="Telu" typeface="Gautami"/>
+				<a:font script="Taml" typeface="Latha"/>
+				<a:font script="Syrc" typeface="Estrangelo Edessa"/>
+				<a:font script="Orya" typeface="Kalinga"/>
+				<a:font script="Mlym" typeface="Kartika"/>
+				<a:font script="Laoo" typeface="DokChampa"/>
+				<a:font script="Sinh" typeface="Iskoola Pota"/>
+				<a:font script="Mong" typeface="Mongolian Baiti"/>
+				<a:font script="Viet" typeface="Arial"/>
+				<a:font script="Uigh" typeface="Microsoft Uighur"/>
+			</a:minorFont>
+		</a:fontScheme>
+		<a:fmtScheme name="Office">
+			<a:fillStyleLst>
+				<a:solidFill>
+					<a:schemeClr val="phClr"/>
+				</a:solidFill>
+				<a:gradFill rotWithShape="1">
+					<a:gsLst>
+						<a:gs pos="0">
+							<a:schemeClr val="phClr">
+								<a:tint val="50000"/>
+								<a:satMod val="300000"/>
+							</a:schemeClr>
+						</a:gs>
+						<a:gs pos="35000">
+							<a:schemeClr val="phClr">
+								<a:tint val="37000"/>
+								<a:satMod val="300000"/>
+							</a:schemeClr>
+						</a:gs>
+						<a:gs pos="100000">
+							<a:schemeClr val="phClr">
+								<a:tint val="15000"/>
+								<a:satMod val="350000"/>
+							</a:schemeClr>
+						</a:gs>
+					</a:gsLst>
+					<a:lin ang="16200000" scaled="1"/>
+				</a:gradFill>
+				<a:gradFill rotWithShape="1">
+					<a:gsLst>
+						<a:gs pos="0">
+							<a:schemeClr val="phClr">
+								<a:shade val="51000"/>
+								<a:satMod val="130000"/>
+							</a:schemeClr>
+						</a:gs>
+						<a:gs pos="80000">
+							<a:schemeClr val="phClr">
+								<a:shade val="93000"/>
+								<a:satMod val="130000"/>
+							</a:schemeClr>
+						</a:gs>
+						<a:gs pos="100000">
+							<a:schemeClr val="phClr">
+								<a:shade val="94000"/>
+								<a:satMod val="135000"/>
+							</a:schemeClr>
+						</a:gs>
+					</a:gsLst>
+					<a:lin ang="16200000" scaled="0"/>
+				</a:gradFill>
+			</a:fillStyleLst>
+			<a:lnStyleLst>
+				<a:ln w="9525" cap="flat" cmpd="sng" algn="ctr">
+					<a:solidFill>
+						<a:schemeClr val="phClr">
+							<a:shade val="95000"/>
+							<a:satMod val="105000"/>
+						</a:schemeClr>
+					</a:solidFill>
+					<a:prstDash val="solid"/>
+				</a:ln>
+				<a:ln w="25400" cap="flat" cmpd="sng" algn="ctr">
+					<a:solidFill>
+						<a:schemeClr val="phClr"/>
+					</a:solidFill>
+					<a:prstDash val="solid"/>
+				</a:ln>
+				<a:ln w="38100" cap="flat" cmpd="sng" algn="ctr">
+					<a:solidFill>
+						<a:schemeClr val="phClr"/>
+					</a:solidFill>
+					<a:prstDash val="solid"/>
+				</a:ln>
+			</a:lnStyleLst>
+			<a:effectStyleLst>
+				<a:effectStyle>
+					<a:effectLst>
+						<a:outerShdw blurRad="40000" dist="20000" dir="5400000" rotWithShape="0">
+							<a:srgbClr val="000000">
+								<a:alpha val="38000"/>
+							</a:srgbClr>
+						</a:outerShdw>
+					</a:effectLst>
+				</a:effectStyle>
+				<a:effectStyle>
+					<a:effectLst>
+						<a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0">
+							<a:srgbClr val="000000">
+								<a:alpha val="35000"/>
+							</a:srgbClr>
+						</a:outerShdw>
+					</a:effectLst>
+				</a:effectStyle>
+				<a:effectStyle>
+					<a:effectLst>
+						<a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0">
+							<a:srgbClr val="000000">
+								<a:alpha val="35000"/>
+							</a:srgbClr>
+						</a:outerShdw>
+					</a:effectLst>
+					<a:scene3d>
+						<a:camera prst="orthographicFront">
+							<a:rot lat="0" lon="0" rev="0"/>
+						</a:camera>
+						<a:lightRig rig="threePt" dir="t">
+							<a:rot lat="0" lon="0" rev="1200000"/>
+						</a:lightRig>
+					</a:scene3d>
+					<a:sp3d>
+						<a:bevelT w="63500" h="25400"/>
+					</a:sp3d>
+				</a:effectStyle>
+			</a:effectStyleLst>
+			<a:bgFillStyleLst>
+				<a:solidFill>
+					<a:schemeClr val="phClr"/>
+				</a:solidFill>
+				<a:gradFill rotWithShape="1">
+					<a:gsLst>
+						<a:gs pos="0">
+							<a:schemeClr val="phClr">
+								<a:tint val="40000"/>
+								<a:satMod val="350000"/>
+							</a:schemeClr>
+						</a:gs>
+						<a:gs pos="40000">
+							<a:schemeClr val="phClr">
+								<a:tint val="45000"/>
+								<a:shade val="99000"/>
+								<a:satMod val="350000"/>
+							</a:schemeClr>
+						</a:gs>
+						<a:gs pos="100000">
+							<a:schemeClr val="phClr">
+								<a:shade val="20000"/>
+								<a:satMod val="255000"/>
+							</a:schemeClr>
+						</a:gs>
+					</a:gsLst>
+					<a:path path="circle">
+						<a:fillToRect l="50000" t="-80000" r="50000" b="180000"/>
+					</a:path>
+				</a:gradFill>
+				<a:gradFill rotWithShape="1">
+					<a:gsLst>
+						<a:gs pos="0">
+							<a:schemeClr val="phClr">
+								<a:tint val="80000"/>
+								<a:satMod val="300000"/>
+							</a:schemeClr>
+						</a:gs>
+						<a:gs pos="100000">
+							<a:schemeClr val="phClr">
+								<a:shade val="30000"/>
+								<a:satMod val="200000"/>
+							</a:schemeClr>
+						</a:gs>
+					</a:gsLst>
+					<a:path path="circle">
+						<a:fillToRect l="50000" t="50000" r="50000" b="50000"/>
+					</a:path>
+				</a:gradFill>
+			</a:bgFillStyleLst>
+		</a:fmtScheme>
+	</a:themeElements>
+	<a:objectDefaults/>
+	<a:extraClrSchemeLst/>
+</a:theme>

Файловите разлики са ограничени, защото са твърде много
+ 1089 - 0
report/src/excel_util/rpt_excel_util.ts


+ 16 - 0
report/src/excel_util/test/demo.test.ts

@@ -0,0 +1,16 @@
+/**
+ * demo of unit test
+ */
+
+import * as chai from 'chai';
+import Demo from './demo';
+
+// Demo类的测试声明
+const expect = chai.expect;
+describe('Demo', function () {
+    describe('Demo.getSum', function() {
+        it('1+1 应该等于 2', function() {
+            expect(Demo.getSum([1, 1])).to.be.equal(2);
+        });
+    });
+});

+ 15 - 0
report/src/excel_util/test/demo.ts

@@ -0,0 +1,15 @@
+
+export default class Demo {
+    /**
+     * @name getSum
+     * @description 求和demo
+     * @param arg 
+     */
+    public static getSum (arg: Array<number>): number {
+        let sum: number = 0;
+        for (let i in arg) {
+            sum += arg[i];
+        }
+        return sum;
+    }
+}

+ 4 - 0
report/src/index.ts

@@ -0,0 +1,4 @@
+
+export { default as core } from './core/jpc_ex';
+
+export { default as excel_util } from './excel_util/rpt_excel_util';

+ 36 - 0
report/src/public/fsUtil.ts

@@ -0,0 +1,36 @@
+/**
+ * Created by Tony on 2017/4/10.
+ */
+'use strict';
+
+import * as fs from 'fs';
+
+export default {
+    writeArrayToFile: function(arr: any[], filePath: string) {
+        if (arr && filePath && Array.isArray(arr)) {
+            let chunks = [], len = 0;
+            for (let i = 0; i < arr.length; i++) {
+                let buffer = new Buffer(arr[i]);
+                chunks.push(buffer);
+                len += buffer.length;
+                //
+            }
+            let resultBuffer = new Buffer(len);
+            for(let i=0,size=chunks.length,pos=0;i<size;i++){
+                chunks[i].copy(resultBuffer,pos);
+                pos += chunks[i].length;
+            }
+            fs.writeFile(filePath, resultBuffer, function(err: any){
+                if(err) throw err;
+                //console.log('Write file: ' + filePath + ' ok!');
+            });
+        }
+    },
+    writeObjToFile: function(obj: any, filePath: string) {
+        if (obj) {
+            let arr = [];
+            arr.push(JSON.stringify(obj));
+            this.writeArrayToFile(arr, filePath);
+        }
+    }
+};

+ 215 - 0
report/src/public/scMathUtil.ts

@@ -0,0 +1,215 @@
+/**
+ * Created by Tony on 2017/4/14.
+ */
+'use strict';
+
+export default {
+    innerRoundTo: function(num: number, digit: number){
+        let lFactor = Math.pow(10, digit);
+        let fOffSet = 0.5;
+        let sign = '';
+        // 处理符号
+        if (num < 0){
+            sign = '-';
+            num = Math.abs(num);
+        }
+        // 计算
+        let result = Math.floor((num / lFactor) + fOffSet).toString();
+        let iLength = result.length;
+        // 因为数值被转为整数计算,当目标位数在小数点后,且数值小于0,需要补齐前面的位数
+        if (iLength < -digit){
+            result = this.zeroString(-digit) + result;
+        }
+        // 当目标位数在小数点前,需要补齐后面的位数
+        else if ((digit > 0) && (iLength < digit)){
+            result = result + this.zeroString(digit);
+        }
+        iLength = result.length;
+        // 获得小数点前的数字
+        let r1 = result.substring(0, iLength + digit);
+        // 获得小数点后的数字
+        let r2 = result.substring(iLength + digit, iLength);
+        // 拼出完整结果
+        return Number(sign + r1 + '.' + r2);
+    },
+    // 原来直接用num.toString(2),如果小数段最后位数是0,会被舍掉,导致进位计算bug
+    // 改为自己计算二进制,固定为53位。
+    // 经验证速度没有差别
+    // 另:经手工验证,用num.toString(2)将十进制浮点数转换为二进制浮点数时,最后一位有错误的情况出现,例子(10.0311)
+    floatToBin: function(num: number): string {
+        let sign = '';
+        let dNum = num;
+        // 符号位
+        if (num < 0) {
+            sign = '-';
+            dNum = -num;
+        };
+        // 解析整数段
+        let iNum = Math.floor(dNum);
+        let iFactor;
+        let sResult1 = '';
+        // 计算二进制整数段
+        while (iNum > 0){
+            iFactor = iNum % 2;
+            iNum = Math.floor(iNum / 2);
+            sResult1 = iFactor + sResult1;
+        }
+        // 判断是否有整数段
+        let bIntZero = sResult1 === '';
+        if (bIntZero){
+            sResult1 = '0';
+        }
+        // 解析小数段
+        let fNum = dNum - Math.floor(dNum);
+        let sResult2 = '';
+        if (fNum > 0){
+            // 双精度浮点数,尾数总长52位,因为第一位总是1,存储时已经被隐藏,所以有效位数为53位
+            // 由于js未对浮点数做优化,所以在有运算的情况下,误差会被放大,因此放弃一位有效位数来消除误差,二进制有效位数50位,十进制有效位数15位
+            const floatLength = 50;
+
+            let iLength;
+            // js的bug,浮点数直接取小数可能不能获得精确值,只有转成字符串,截取字符串中的小数段
+            let sNum = dNum.toString(10);
+            let iDot = sNum.indexOf('.');
+            sNum = '0' + sNum.substring(iDot, sNum.length);
+            fNum = Number(sNum);
+            // 有整数段,则小数位数为全部位数-整数位数
+            if (!bIntZero){
+                iLength = floatLength - sResult1.length;
+            }
+            else{
+                iLength = floatLength;
+            }
+            // 计算二进制小数段
+            while (iLength > 0){
+                fNum = fNum * 2;
+                iFactor = Math.floor(fNum);
+                fNum = fNum % 1;
+                sResult2 = sResult2 + iFactor;
+                if (iFactor > 0){
+                    bIntZero = false;
+                }
+                if (bIntZero && (iFactor === 0)){
+                    continue;
+                }
+                iLength--;
+            }
+        }
+        return sign + sResult1 + '.' + sResult2;
+    },
+    binToFloat: function(bin: string): string {
+        let result = 0;
+        let iLength = bin.length;
+        let sign = '';
+        if (iLength > 0 && bin[0]==='-'){
+            sign = '-';
+            bin = bin.substring(1, iLength);
+        }
+        iLength = bin.length;
+        let iDot = bin.indexOf('.');
+        if (iDot >= 0) {
+            for (let i = 0; i < iLength; i++) {
+                let num = Number(bin[i]);
+                let idx = iDot - i;
+                if (idx === 0) {
+                    continue
+                };
+                if (idx > 0) {
+                    idx -= 1
+                };
+                let r = Math.pow(2, idx);
+                result += num * r;
+            }
+        }
+        else {
+            result = parseInt(bin, 2);
+        };
+        return sign + result;
+    },
+    zeroString: function(length: number){
+        let result = '';
+        for (let i = 0; i < length; i++){
+            result = result + '0';
+        };
+        return result;
+    },
+    incMantissa: function(bin: string): string {
+        let result = bin;
+        const me = this;
+        let iDot = bin.indexOf('.');
+        if (iDot < 0) {
+            return result
+        };
+        let iLength = bin.length;
+        iLength = bin.length;
+        for (let i = iLength - 1; i > iDot; i--){
+            let num = Number(bin[i]);
+            if (num === 0){
+                num = 1;
+                let bin1 = bin.substring(0, i);
+                let bin2 = me.zeroString(iLength - (i + 1));//bin.substring(i + 1, iLength);
+                result = bin1 + num.toString() + bin2;
+                break;
+            }
+
+        };
+        return result;
+    },
+
+    roundTo: function(num: number, digit: number) {
+        let me = this;
+        // let str1 = me.floatToBin(num);
+        // let str2 = me.incMantissa(str1);
+        // return me.innerRoundTo( parseFloat(me.binToFloat(str2)), digit);
+        return me.innerRoundTo( parseFloat(me.binToFloat( me.incMantissa( me.floatToBin(num)))), digit);
+    },
+    isNumber : function (obj: any) {
+        return obj === +obj;
+    },
+    roundForObj:function(obj: any, decimal: number) {
+        let me = this;
+        let value;
+        if(me.isNumber(obj)){
+            value = me.roundTo(obj,-decimal)
+        }else {
+            value = me.roundTo(Number(obj),-decimal);
+        }
+        return value
+    },
+    roundToString:function(obj: any, decimal: number){
+        let me = this;
+        let value;
+        if(me.isNumber(obj)){
+            value = me.roundTo(obj,-decimal)
+        }else {
+            value = me.roundTo(Number(obj),-decimal);
+        }
+        return value.toFixed(decimal);
+    },
+    // isNumOrFormula:function (text: any) {
+    //     let value = Number(text);
+    //     if(value ==0 ) return value;
+    //     if (!value) {
+    //         try {
+    //             let exp = new Expression('');
+    //             exp.Expression(text);
+    //             value = Number(exp.Evaluate());
+    //         } catch (error) {
+    //             value = 0;
+    //         }
+    //     }
+    //     if(text == null || text == ''){
+    //         value = 0;
+    //     }
+    //     return value;
+    // },
+    isDef:function (v: any) {
+        return v !== undefined && v !== null;
+    },
+    //获取ID引用
+    getFIDArr: function (exp: any) {
+        let fidRex = /@[\d,a-z,A-Z,-]{36}/g;
+        let fidArr = exp.match(fidRex);
+        return this.isDef(fidArr) ? fidArr : [];
+    }
+};

+ 137 - 0
report/src/public/stringUtil.ts

@@ -0,0 +1,137 @@
+'use strict';
+
+export default {
+    isEmptyString: function(str: string) {
+        let rst = false;
+        if (str === null || str === undefined) {
+            rst = true;
+        } else if (typeof str) {
+            let reg = /^\s*$/;
+            rst = reg.test(str);
+        }
+        return rst;
+    },
+    trim: function(str: string) {
+        return str.replace(/(^\s*)|(\s*$)/g, "");
+    },
+    leftTrim: function(str: string) {
+        return str.replace(/(^\s*)/g,"");
+    },
+    rightTrim: function(str: string) {
+        return str.replace(/(\s*$)/g,"");
+    },
+    replaceAll: function (targetStr: string, FindText: string, RepText: string) {
+        let regExp = new RegExp(FindText, "gm");
+        return targetStr.replace(regExp, RepText);
+    },
+    comdify: function(numStr: string){
+        let re = /\d{1,3}(?=(\d{3})+$)/g;
+        return numStr.replace(/^(\d+)((\.\d+)?)$/,function(s,s1,s2){return s1.replace(re,"$&,")+s2;});
+    },
+    convertToCaptionNum: function(num: any, isCurrency: boolean, isTraditionalCap: boolean) {
+        let me = this, rst = "";
+        if (/^\d*(\.\d*)?$/.test(num)) {
+            let capChars, unitChars;
+            if (isTraditionalCap) {
+                capChars = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
+                unitChars = ["" , "拾", "佰", "仟", "萬", "拾", "佰", "仟", "亿", "拾", "佰", "仟", "万"];
+            } else {
+                capChars = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
+                unitChars = ["" , "十", "百", "千", "万", "十", "百", "千", "亿", "十", "百", "千", "万"];
+            }
+
+            let numSplitArr = ("" + num).replace(/(^0*)/g, "").split(".");
+            if (numSplitArr[0] === "") numSplitArr[0] = "0";
+            let len = numSplitArr[0].length;
+            let intPartArr = [];
+            if (len <= 13) {
+                for (let idx = 0; idx < len; idx++) {
+                    intPartArr.push(capChars[ parseInt(numSplitArr[0].charAt(idx) )] + unitChars[len - idx - 1]);
+                }
+                rst = intPartArr.join('');
+                rst = me.replaceAll(rst, capChars[0] + unitChars[3], capChars[0]); //零千 -> 零
+                rst = me.replaceAll(rst, capChars[0] + unitChars[2], capChars[0]); //零百 -> 零
+                rst = me.replaceAll(rst, capChars[0] + unitChars[1], capChars[0]); //零十 -> 零
+                //
+                rst = me.replaceAll(me.replaceAll(rst, "零零", "零"), "零零", "零");
+                rst = me.replaceAll(rst, capChars[0] + unitChars[8], unitChars[8]); //零亿 -> 亿
+                rst = me.replaceAll(rst, capChars[0] + unitChars[4], unitChars[4]); //零万 -> 万
+                //
+                rst = me.replaceAll(rst, unitChars[8] + unitChars[4], unitChars[8] + capChars[0]); //亿万 -> 亿零
+                if (num === 0) {
+                    rst = "零";
+                } else if (rst.length > 1 && rst.charAt(rst.length - 1) === '零') {
+                    rst = rst.slice(0, rst.length - 1);
+                }
+                //小数部分处理
+                if (numSplitArr.length > 1) {
+                    len = numSplitArr[1].length;
+                    if (parseInt(numSplitArr[1]) === 0) {
+                        rst = rst + (isCurrency?"元整":"");
+                    } else {
+                        if (isCurrency && len > 2) len = 2;
+                        let fractionStr = [];
+                        for (let idx = 0; idx < len; idx++) {
+                            fractionStr.push(capChars[ parseInt(numSplitArr[1].charAt(idx))]+ (isCurrency?((idx === 0)?"角":"分"):""));
+                        }
+                        rst = rst + (isCurrency?"元":"点") + fractionStr.join("");
+                    }
+                } else {
+                    rst = rst + (isCurrency?"元整":"");
+                }
+            } else {
+                rst = "Number is too big!";
+            }
+        } else {
+            rst = "Number is wrong!";
+        }
+        return rst;
+    },
+    convertStrToBoolean: function(str: string) {
+        let rst = false, me = this;
+        if (!me.isEmptyString(str)) {
+            let upperStr = str.toUpperCase();
+            if (upperStr === 'T' || upperStr === 'Y' || upperStr === 'YES' || upperStr === 'TRUE') {
+                rst = true;
+            }
+        }
+        return rst;
+    },
+    formatNumber: function(formatStr: string, val: any) {
+        let rst = val;
+        if (formatStr) {
+            if (!(isNaN(parseFloat(val)))) {
+                let dotIdx = formatStr.indexOf(".");
+                if (dotIdx >= 0) {
+                    let tmpStr = parseFloat(val).toFixed(formatStr.length - dotIdx - 1);
+                    let digStr = formatStr.substr(dotIdx + 1, formatStr.length - dotIdx);
+                    for (let sIdx = digStr.length - 1; sIdx >= 0; sIdx--) {
+                        if (digStr[sIdx] === '#') {
+                            if (tmpStr.length > 0 && tmpStr[tmpStr.length - 1] === '0') {
+                                tmpStr = tmpStr.substr(0, tmpStr.length - 1);
+                            } else {
+                                break;
+                            }
+                        } else {
+                            break;
+                        }
+                    }
+                    if (tmpStr[tmpStr.length - 1] === '.') tmpStr = tmpStr.substr(0, tmpStr.length - 1);
+                    rst = tmpStr;
+                } else {
+                    rst = parseFloat(val).toFixed(0);
+                }
+                let commaIdx = formatStr.indexOf(",");
+                if (commaIdx >= 0) {
+                    rst = comdify(val.toString());
+                }
+            }
+        }
+        return rst;
+    },
+}
+
+function comdify(numStr: string){
+    let re = /\d{1,3}(?=(\d{3})+$)/g;
+    return numStr.replace(/^(\d+)((\.\d+)?)$/, function(s,s1,s2){return s1.replace(re,"$&,")+s2;});
+}

+ 166 - 0
report/src/public/treeUtil.ts

@@ -0,0 +1,166 @@
+'use strict';
+
+/**
+ * Created by Tony on 2017/3/14.
+ */
+
+const NODE_ID = 'ledger_id';
+const P_ID = 'ledger_pid';
+const NEXT_ID = 'NextSiblingID';
+const ADHOC_PRE_ID = 'Previous_ID';
+const ORDER_ID = 'order';
+const CHILDREN_NODE = 'items';
+const SUB_ID = 'sub_ids';
+const EMPTY_ID_VAL = -1;
+const TREE_LEVEL = 'treeLevel';
+const TOP_BILL_ID = 'topBillID';
+const TREE_FLAT_SERIAL_ORDER = 'treeFlatSerialOrder';
+
+function sortOder(item1: any, item2: any) {
+    return parseInt(item1[ORDER_ID]) - parseInt(item2[ORDER_ID]);
+}
+
+const tree_Data_Helper = {
+    buildTreeNodeDirectly: function(data: any, addLevel: number): any[] {
+        let topArr = [], tmpNodes: { [k: string]: any } = {}, prefix = "id_";
+        const rst: any[] = []; 
+        let private_getStartNode = function (idArr: any[]) {
+            let tmpNodeRst = null;
+            for (let i = 0; i < idArr.length; i++) {
+                if (parseInt(tmpNodes[prefix + idArr[i]][ADHOC_PRE_ID]) === EMPTY_ID_VAL) {
+                    tmpNodeRst = tmpNodes[prefix + idArr[i]];
+                    break;
+                }
+            }
+            return tmpNodeRst;
+        };
+        let private_buildNodeData = function(parentItem: any, idArr: any[], treeLevel: number, tbID: number) {
+            let iter = [], nextNode = private_getStartNode(idArr), pushedIds = [];
+            while (nextNode !== null && nextNode !== undefined ) {
+                if (parentItem) {
+                    parentItem[CHILDREN_NODE].push(nextNode);
+                } else {
+                    rst.push(nextNode);
+                }
+                iter.push(nextNode);
+                pushedIds.push(nextNode[NODE_ID]);
+                nextNode[TOP_BILL_ID] = tbID;
+                if (parentItem === null) {
+                    nextNode[TOP_BILL_ID] = nextNode[NODE_ID];
+                    if (nextNode.flags && nextNode.flags.length > 0) {
+                        for (let flag of nextNode.flags) {
+                            if (flag.fieldName === "fixed") {
+                                nextNode[TOP_BILL_ID] = flag.flag;
+                                break;
+                            }
+                        }
+                    }
+                }
+                if (addLevel) nextNode[TREE_LEVEL] = treeLevel;
+                nextNode = tmpNodes[prefix + nextNode[NEXT_ID]];
+                if (nextNode === null || nextNode === undefined) {
+                    //备注: 考虑到实际数据的健壮性,有些节点会掉链子,需要用 parentItem[SUB_ID] 比对已经加上的节点,如发现加上的节点数量不够,那就得在这里补充上去
+                    if (parentItem) {
+                        if (parentItem[SUB_ID].length > iter.length) {
+                            for (let subId of parentItem[SUB_ID]) {
+                                if (pushedIds.indexOf(subId) < 0) {
+                                    let restNode = tmpNodes[prefix + subId];
+                                    if (addLevel) restNode[TREE_LEVEL] = treeLevel;
+                                    restNode[TOP_BILL_ID] = tbID;
+                                    parentItem[CHILDREN_NODE].push(restNode);
+                                    iter.push(restNode);
+                                }
+                            }
+                        }
+                    } else {
+                        if (idArr.length > iter.length) {
+                            for (let topId of idArr) {
+                                if (pushedIds.indexOf(topId) < 0) {
+                                    let restNode = tmpNodes[prefix + topId];
+                                    if (addLevel) restNode[TREE_LEVEL] = treeLevel;
+                                    restNode[TOP_BILL_ID] = restNode[NODE_ID];
+                                    if (restNode.flags && restNode.flags.length > 0) {
+                                        for (let flag of restNode.flags) {
+                                            if (flag.fieldName === "fixed") {
+                                                restNode[TOP_BILL_ID] = flag.flag;
+                                                break;
+                                            }
+                                        }
+                                    }
+                                    rst.push(restNode);
+                                    iter.push(restNode);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            pushedIds = [];
+            for (let i = 0; i < iter.length; i++) {
+                let rtbID = tbID;
+                if (parentItem === null) {
+                    rtbID = iter[i][TOP_BILL_ID];
+                }
+                private_buildNodeData(iter[i], iter[i][SUB_ID], (treeLevel + 1), rtbID);
+            }
+        };
+        //1. 给每个节点设置key, 顺便找Top Node
+        for (let i = 0; i < data.length; i++) {
+            let propK = prefix + String(data[i][NODE_ID]);
+            tmpNodes[propK] = data[i];
+            data[i][ADHOC_PRE_ID] = EMPTY_ID_VAL;
+            data[i][SUB_ID] = [];
+            data[i][CHILDREN_NODE] = [];
+            if (parseInt(data[i][P_ID]) === EMPTY_ID_VAL) {
+                topArr.push(data[i][NODE_ID]);
+            }
+        }
+        //2. 通过key,设置好兄弟/父子关系
+        for (let i = 0; i < data.length; i++) {
+            if (parseInt(data[i][NEXT_ID]) !== EMPTY_ID_VAL) {
+                if (tmpNodes[prefix + data[i][NEXT_ID]] !== undefined){
+                    if (tmpNodes[prefix + data[i][NEXT_ID]][P_ID] === data[i][P_ID]) {
+                        tmpNodes[prefix + data[i][NEXT_ID]][ADHOC_PRE_ID] = data[i][NODE_ID];
+                    } else {
+                        tmpNodes[prefix + data[i][NEXT_ID]][ADHOC_PRE_ID] = EMPTY_ID_VAL;
+                        data[i][NEXT_ID] = EMPTY_ID_VAL;
+                    }
+                }
+            }
+            if (parseInt(data[i][P_ID]) !== EMPTY_ID_VAL) {
+                tmpNodes[prefix + data[i][P_ID]][SUB_ID].push(data[i][NODE_ID]);
+            }
+        }
+        //3. 开build
+        private_buildNodeData(null, topArr, 0, -1);
+        //try to release and return
+        // tmpNodes = null;
+        topArr.length = 0;
+        return rst;
+    },
+
+    getFlatArray: function(srcArr: any[], destArr: any[], addSerialOrder: boolean) {
+        let serialStartOrder = 0;
+        const private_put = function(parentItem: any) {
+            if (addSerialOrder) {
+                parentItem[TREE_FLAT_SERIAL_ORDER] = serialStartOrder;
+                serialStartOrder++;
+                // 说明:当清单通过树排序后,为了后续的排序方便,有必要加这个序号,并作为指标提供
+            }
+            destArr.push(parentItem);
+            if (parentItem[CHILDREN_NODE]) {
+                for (const subItem of parentItem[CHILDREN_NODE]) {
+                    private_put(subItem);
+                }
+            }
+        }
+        for (const node of srcArr) {
+            private_put(node);
+        }
+        for (const item of destArr) {
+            delete item[CHILDREN_NODE];
+        }
+    },
+};
+
+module.exports = tree_Data_Helper;

+ 16 - 0
report/tsconfig.json

@@ -0,0 +1,16 @@
+{
+  "compilerOptions": {
+    "target": "ESNext",
+    "module": "ESNext",
+    "declaration": true,
+    "outDir": "./",
+    "strict": true
+  },
+  "include": [
+    "src/**/*.ts",
+    "src/excel_util/test/*.ts",
+  ],
+  "exclude": [
+    "node_modules"
+  ]
+}