UNPKG

chart-render

Version:

<div style="display:flex;align-items:center;margin-bottom:24px"> <img src="https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png" alt="logo" width="48px"/> <h4 style="font-size:30px;font-weight:600;display:inline-block;margin-left:12px">C

748 lines (650 loc) 25.2 kB
import React, { useState } from 'react'; import { Bar, Column, Area, DualAxes, Line } from '@ant-design/charts'; import { buildDrillTree, convertDrillTreeToCrossTree, buildRecordMatrix, CrossTable } from 'ali-react-table/pivot'; import { createAggregateFunction } from 'dvt-aggregation'; function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread2(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } /** * 把元数据信息按照 isDim 字段拆分成维度元数据和指标元数据 * @param meta 完整元数据信息 * @todo 可以在这里面加字段的排序逻辑,如有必要 */ function splitMeta(meta) { var metaDim = []; var metaInd = []; meta.forEach(function (item) { if (item.isDim) { metaDim.push(item); } else { metaInd.push(item); } }); return { metaDim: metaDim, metaInd: metaInd }; } /** * 解决浮点数丢失精度的问题,来源:https://github.com/camsong/blog/issues/9 */ function strip(num) { var precision = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 12; return +parseFloat(num === null || num === void 0 ? void 0 : num.toPrecision(precision)); } var ErrorTemplate = (function () { return /*#__PURE__*/React.createElement("div", { style: { color: 'rgba(0,0,0,.85)', backgroundColor: '#fff2f0', fontSize: 14, border: '1px solid #ffccc7', padding: '8px 15px' } }, "\u2757\uFE0F\u4F20\u5165\u7684\u6307\u6807\u6570\u76EE\u6709\u8BEF\uFF01"); }); function generateConfig(meta, data) { var _splitMeta = splitMeta(meta), metaDim = _splitMeta.metaDim, metaInd = _splitMeta.metaInd; if (metaInd.length >= 1 && metaDim.length === 0) { // case 1: N指标、0维度 => 指标名作为 x 轴,指标值作为 y 轴 var xField = 'type'; var yField = 'value'; return { xField: xField, yField: yField, data: data.map(function (item) { return metaInd.map(function (_ref) { var _ref2; var id = _ref.id, name = _ref.name; return _ref2 = {}, _defineProperty(_ref2, xField, id), _defineProperty(_ref2, yField, item[id]), _ref2; }); }).flat(), meta: _defineProperty({}, xField, { formatter: function formatter(label) { var _meta$find; return ((_meta$find = meta.find(function (_ref3) { var id = _ref3.id; return label === id; })) === null || _meta$find === void 0 ? void 0 : _meta$find.name) || label; } }), tooltip: { formatter: function formatter(_ref4) { var _meta$find2; var type = _ref4[xField], value = _ref4[yField]; return { name: (_meta$find2 = meta.find(function (_ref5) { var id = _ref5.id; return type === id; })) === null || _meta$find2 === void 0 ? void 0 : _meta$find2.name, value: value }; } } }; } else if (metaInd.length === 1 && metaDim.length === 1) { var _metaDim$shift, _metaInd$shift, _meta$find3; // case 2: 单指标,单维度 => 维度作为 x 轴,指标作为 y 轴 var _xField = (_metaDim$shift = metaDim.shift()) === null || _metaDim$shift === void 0 ? void 0 : _metaDim$shift.id; var _yField = (_metaInd$shift = metaInd.shift()) === null || _metaInd$shift === void 0 ? void 0 : _metaInd$shift.id; return { data: data, xField: _xField, yField: _yField, meta: _defineProperty({}, _yField, { alias: (_meta$find3 = meta.find(function (_ref6) { var id = _ref6.id; return id === _yField; })) === null || _meta$find3 === void 0 ? void 0 : _meta$find3.name }) }; } else if (metaInd.length > 1 && metaDim.length === 1) { var _metaDim$shift2; // case 3: 多指标,单维度 => 维度作为 x 轴,指标名作为系列,指标值作为 y 轴 var _xField2 = (_metaDim$shift2 = metaDim.shift()) === null || _metaDim$shift2 === void 0 ? void 0 : _metaDim$shift2.id; var _yField2 = 'value'; var seriesField = 'type'; return { data: data.map(function (item) { return metaInd.map(function (_ref7) { var _ref8; var id = _ref7.id, name = _ref7.name; return _ref8 = {}, _defineProperty(_ref8, _xField2, item[_xField2]), _defineProperty(_ref8, _yField2, item[id]), _defineProperty(_ref8, seriesField, name), _ref8; }); }).flat(), xField: _xField2, yField: _yField2, seriesField: seriesField, isGroup: true }; } else if (metaInd.length === 1 && metaDim.length === 2) { var _metaDim$shift3, _metaInd$shift2, _metaDim$shift4; // case 3: 单指标,双维度 return { data: data, xField: (_metaDim$shift3 = metaDim.shift()) === null || _metaDim$shift3 === void 0 ? void 0 : _metaDim$shift3.id, yField: (_metaInd$shift2 = metaInd.shift()) === null || _metaInd$shift2 === void 0 ? void 0 : _metaInd$shift2.id, seriesField: (_metaDim$shift4 = metaDim.shift()) === null || _metaDim$shift4 === void 0 ? void 0 : _metaDim$shift4.id, isGroup: true }; } return { data: data, xField: '', yField: '' }; } var CRColumn = function CRColumn(_ref9) { var className = _ref9.className, style = _ref9.style, _ref9$meta = _ref9.meta, meta = _ref9$meta === void 0 ? [] : _ref9$meta, _ref9$data = _ref9.data, data = _ref9$data === void 0 ? [] : _ref9$data, inverted = _ref9.inverted, props = _objectWithoutProperties(_ref9, ["className", "style", "meta", "data", "inverted"]); if (inverted) { var _generateConfig = generateConfig(meta, data), xField = _generateConfig.xField, yField = _generateConfig.yField, otherConfig = _objectWithoutProperties(_generateConfig, ["xField", "yField"]); // 条形图 x、y 互换 return /*#__PURE__*/React.createElement(Bar, Object.assign({ xField: yField, yField: xField }, otherConfig, { renderer: "svg", errorTemplate: function errorTemplate() { return /*#__PURE__*/React.createElement(ErrorTemplate, null); } }, props)); } else { return /*#__PURE__*/React.createElement(Column, Object.assign({}, generateConfig(meta, data), { renderer: "svg", errorTemplate: function errorTemplate() { return /*#__PURE__*/React.createElement(ErrorTemplate, null); } }, props)); } }; function generateConfig$1(meta, data) { var _splitMeta = splitMeta(meta), metaDim = _splitMeta.metaDim, metaInd = _splitMeta.metaInd; if (metaInd.length === 1 && metaDim.length === 1) { var _meta$find; // case 1: 单指标、单维度 => 维度作为 x 轴,指标作为 y 轴 var xFieldMeta = metaDim.shift(); var yFieldMeta = metaInd.shift(); var xField = xFieldMeta.id; var yField = yFieldMeta.id; return { data: data, xField: xField, yField: yField, yAxis: { label: { formatter: function formatter(v) { return yFieldMeta.isRate ? "".concat(strip(100 * Number(v)), "%") : v; } } }, tooltip: { formatter: function formatter(_ref) { var type = _ref[xField], value = _ref[yField]; return { name: yFieldMeta.name, value: yFieldMeta.isRate ? "".concat(strip(100 * Number(value)), "%") : value }; } }, meta: _defineProperty({}, yField, { alias: (_meta$find = meta.find(function (_ref2) { var id = _ref2.id; return id === yField; })) === null || _meta$find === void 0 ? void 0 : _meta$find.name }) }; } else if (metaInd.length === 1 && metaDim.length === 2) { // case 2: 单指标、双维度 => 第一维度作为 x 轴,指标作为 y 轴,第二维度作为 系列 var _xFieldMeta = metaDim.shift(); var _yFieldMeta = metaInd.shift(); var seriesFieldMeta = metaDim.shift(); var _xField = _xFieldMeta.id; var _yField = _yFieldMeta.id; var seriesField = seriesFieldMeta.id; return { data: data, xField: _xField, yField: _yField, seriesField: seriesField, yAxis: { label: { formatter: function formatter(v) { return _yFieldMeta.isRate ? "".concat(strip(100 * Number(v)), "%") : v; } } }, tooltip: { formatter: function formatter(_ref3) { var type = _ref3[seriesField], value = _ref3[_yField]; return { name: type, value: _yFieldMeta.isRate ? "".concat(strip(100 * Number(value)), "%") : value }; } } }; } else if (metaInd.length === 2 && metaDim.length === 2) { var _metaDim$shift, _metaInd$shift, _metaInd$shift2, _metaDim$shift2; // case 3: 双指标、双维度 => 第一维度作为 x 轴,第二维度作为 系列,第一指标作为左 y 轴,第二指标作为右 y 轴 var data1 = data.map(function (_ref4) { var _metaInd$0$id = metaInd[0].id, _metaDim$1$id = metaDim[1].id, _ = _ref4[_metaInd$0$id], metaValue = _ref4[_metaDim$1$id], item = _objectWithoutProperties(_ref4, [_metaInd$0$id, _metaDim$1$id].map(_toPropertyKey)); return _objectSpread2(_defineProperty({}, metaDim[1].id, "".concat(metaValue, "-").concat(metaInd[0].name)), item); }); var data2 = data.map(function (_ref5) { var _metaInd$1$id = metaInd[1].id, _metaDim$1$id2 = metaDim[1].id, _ = _ref5[_metaInd$1$id], metaValue = _ref5[_metaDim$1$id2], item = _objectWithoutProperties(_ref5, [_metaInd$1$id, _metaDim$1$id2].map(_toPropertyKey)); return _objectSpread2(_defineProperty({}, metaDim[1].id, "".concat(metaValue, "-").concat(metaInd[1].name)), item); }); return { data: [data2, data1], geometryOptions: [{ geometry: 'line', seriesField: metaDim[1].id }, { geometry: 'line', seriesField: metaDim[1].id, lineStyle: { lineDash: [5, 5] } }], xField: (_metaDim$shift = metaDim.shift()) === null || _metaDim$shift === void 0 ? void 0 : _metaDim$shift.id, yField: [(_metaInd$shift = metaInd.shift()) === null || _metaInd$shift === void 0 ? void 0 : _metaInd$shift.id, (_metaInd$shift2 = metaInd.shift()) === null || _metaInd$shift2 === void 0 ? void 0 : _metaInd$shift2.id], seriesField: (_metaDim$shift2 = metaDim.shift()) === null || _metaDim$shift2 === void 0 ? void 0 : _metaDim$shift2.id }; } else if (metaInd.length > 1 && metaDim.length === 1) { var _metaDim$shift3; // case 4: 多指标、单维度 => 维度作为 x 轴,指标名作为系列,指标值作为 y 轴 // 需要把 data 做一下转化,例:从 { ds, uv, pv }[] 转为 [{ ds, type: uvName, value: xxx }, { ds, type: pvName, value: xxx }] var _xField2 = (_metaDim$shift3 = metaDim.shift()) === null || _metaDim$shift3 === void 0 ? void 0 : _metaDim$shift3.id; var _yField2 = 'value'; var _seriesField = 'type'; return { data: data.map(function (item) { return metaInd.map(function (_ref6) { var _ref7; var id = _ref6.id, name = _ref6.name; return _ref7 = {}, _defineProperty(_ref7, _xField2, item[_xField2]), _defineProperty(_ref7, _yField2, item[id]), _defineProperty(_ref7, _seriesField, name), _ref7; }); }).flat(), xField: _xField2, yField: _yField2, seriesField: _seriesField }; } return { data: data }; } var CRLine = function CRLine(_ref8) { var className = _ref8.className, style = _ref8.style, _ref8$meta = _ref8.meta, meta = _ref8$meta === void 0 ? [] : _ref8$meta, _ref8$data = _ref8.data, data = _ref8$data === void 0 ? [] : _ref8$data, withArea = _ref8.withArea, props = _objectWithoutProperties(_ref8, ["className", "style", "meta", "data", "withArea"]); var config = generateConfig$1(meta, data); // 面积图展示 if (withArea) { return /*#__PURE__*/React.createElement(Area, Object.assign({}, config, { renderer: "svg", errorTemplate: function errorTemplate() { return /*#__PURE__*/React.createElement(ErrorTemplate, null); } }, props)); } // 双轴图展示 if (Array.isArray(config.yField)) { return /*#__PURE__*/React.createElement(DualAxes, Object.assign({}, config, { renderer: "svg", errorTemplate: function errorTemplate() { return /*#__PURE__*/React.createElement(ErrorTemplate, null); } }, props)); } // 普通折线图 return /*#__PURE__*/React.createElement(Line, Object.assign({}, config, { renderer: "svg", errorTemplate: function errorTemplate() { return /*#__PURE__*/React.createElement(ErrorTemplate, null); } }, props)); }; function styleInject(css, ref) { if ( ref === void 0 ) ref = {}; var insertAt = ref.insertAt; if (!css || typeof document === 'undefined') { return; } var head = document.head || document.getElementsByTagName('head')[0]; var style = document.createElement('style'); style.type = 'text/css'; if (insertAt === 'top') { if (head.firstChild) { head.insertBefore(style, head.firstChild); } else { head.appendChild(style); } } else { head.appendChild(style); } if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } } var css_248z = ".CR-PivotTable.CR-PivotTable-small .art-table {\n --row-height: fit-content;\n}\n.CR-PivotTable.CR-PivotTable-middle .art-table {\n --row-height: 46px;\n}\n.CR-PivotTable.CR-PivotTable-large .art-table {\n --row-height: 54px;\n}\n"; styleInject(css_248z); var CRPivotTable = function CRPivotTable(_ref) { var className = _ref.className, style = _ref.style, _ref$showSubtotal = _ref.showSubtotal, showSubtotal = _ref$showSubtotal === void 0 ? true : _ref$showSubtotal, _ref$subtotalText = _ref.subtotalText, subtotalText = _ref$subtotalText === void 0 ? ['总计', '小计'] : _ref$subtotalText, _ref$meta = _ref.meta, meta = _ref$meta === void 0 ? [] : _ref$meta, _ref$data = _ref.data, data = _ref$data === void 0 ? [] : _ref$data, _ref$size = _ref.size, size = _ref$size === void 0 ? 'middle' : _ref$size, _ref$indicatorSide = _ref.indicatorSide, indicatorSide = _ref$indicatorSide === void 0 ? 'top' : _ref$indicatorSide, _ref$leftDimensionLen = _ref.leftDimensionLength, leftDimensionLength = _ref$leftDimensionLen === void 0 ? meta.length : _ref$leftDimensionLen, _ref$leftExpandable = _ref.leftExpandable, leftExpandable = _ref$leftExpandable === void 0 ? false : _ref$leftExpandable, _ref$topExpandable = _ref.topExpandable, topExpandable = _ref$topExpandable === void 0 ? false : _ref$topExpandable, _ref$defaultExpandAll = _ref.defaultExpandAll, cellRender = _ref.cellRender, props = _objectWithoutProperties(_ref, ["className", "style", "showSubtotal", "subtotalText", "meta", "data", "size", "indicatorSide", "leftDimensionLength", "leftExpandable", "topExpandable", "defaultExpandAll", "cellRender"]); var _useState = useState([]), _useState2 = _slicedToArray(_useState, 2), topExpandKeys = _useState2[0], setTopExpandKeys = _useState2[1]; var _useState3 = useState([]), _useState4 = _slicedToArray(_useState3, 2), leftExpandKeys = _useState4[0], setLeftExpandKeys = _useState4[1]; var _splitMeta = splitMeta(meta), metaDim = _splitMeta.metaDim, metaInd = _splitMeta.metaInd; var indicators = metaInd.map(function (_ref2) { var id = _ref2.id, name = _ref2.name, isRate = _ref2.isRate; return { name: name, code: id, align: 'right', expression: isRate ? "AVG(".concat(id, ")") : "SUM(".concat(id, ")") }; }); // 左维树 var leftMetaDim = metaDim.filter(function (_, index) { return index < leftDimensionLength; }); var leftCodes = leftMetaDim.map(function (_ref3) { var id = _ref3.id; return id; }); var leftDrillTree = buildDrillTree(data, leftCodes, { includeTopWrapper: true, isExpand: leftExpandable ? function (key) { return leftExpandKeys.includes(key); } : undefined }); var _convertDrillTreeToCr = convertDrillTreeToCrossTree(leftDrillTree, { // @ts-expect-error => 可以传入 align 字段 indicators: indicatorSide === 'left' ? indicators : undefined, supportsExpand: leftExpandable, expandKeys: leftExpandKeys, onChangeExpandKeys: setLeftExpandKeys, generateSubtotalNode: showSubtotal ? function (drillNode) { return { position: 'start', value: drillNode.path.length === 0 ? subtotalText[0] || '总计' : subtotalText[1] || '小计' }; } : undefined }), _convertDrillTreeToCr2 = _slicedToArray(_convertDrillTreeToCr, 1), leftTreeRoot = _convertDrillTreeToCr2[0]; // 顶维树 var topMetaDim = metaDim.filter(function (_, index) { return index >= leftDimensionLength; }); var topCodes = topMetaDim.map(function (_ref4) { var id = _ref4.id; return id; }); var topDrillTree = buildDrillTree(data, topCodes, { includeTopWrapper: true, isExpand: topExpandable ? function (key) { return topExpandKeys.includes(key); } : undefined }); var _convertDrillTreeToCr3 = convertDrillTreeToCrossTree(topDrillTree, { // @ts-expect-error => 可以传入 align 字段 indicators: indicatorSide === 'top' ? indicators : undefined, supportsExpand: topExpandable, expandKeys: topExpandKeys, onChangeExpandKeys: setTopExpandKeys, generateSubtotalNode: showSubtotal ? function (drillNode) { return { position: 'start', value: drillNode.path.length === 0 ? subtotalText[0] || '总计' : subtotalText[1] || '小计' }; } : undefined }), _convertDrillTreeToCr4 = _slicedToArray(_convertDrillTreeToCr3, 1), topTreeRoot = _convertDrillTreeToCr4[0]; // 数据集 var matrix = buildRecordMatrix({ data: data, leftCodes: leftCodes, topCodes: topCodes, aggregate: createAggregateFunction(indicators) }); return /*#__PURE__*/React.createElement("div", { style: style, className: "CR-PivotTable CR-PivotTable-".concat(size, " ").concat(className || '') }, /*#__PURE__*/React.createElement(CrossTable, Object.assign({ defaultColumnWidth: 100, leftMetaColumns: metaDim.filter(function (_ref5) { var id = _ref5.id; return leftCodes.includes(id); }).map(function (_ref6) { var id = _ref6.id, name = _ref6.name; return { code: id, name: name }; }), // @ts-expect-error leftTree: leftTreeRoot.children, leftTotalNode: leftTreeRoot, // @ts-expect-error topTree: topTreeRoot.children, topTotalNode: topTreeRoot, getValue: function getValue(leftNode, topNode) { var _matrix$get; var record = (_matrix$get = matrix.get(leftNode.data.dataKey)) === null || _matrix$get === void 0 ? void 0 : _matrix$get.get(topNode.data.dataKey); return record === null || record === void 0 ? void 0 : record[topNode.code]; }, render: function render(value, leftNode, topNode) { var _metaInd$find; // 自定义渲染 if (cellRender) { var _leftNode$data$dataPa = leftNode.data.dataPath, leftDataPath = _leftNode$data$dataPa === void 0 ? [] : _leftNode$data$dataPa; var _topNode$data$dataPat = topNode.data.dataPath, topDataPath = _topNode$data$dataPat === void 0 ? [] : _topNode$data$dataPat; var dimRecord = {}; leftDataPath.forEach(function (dimValue, index) { var _leftMetaDim$index; dimRecord[(_leftMetaDim$index = leftMetaDim[index]) === null || _leftMetaDim$index === void 0 ? void 0 : _leftMetaDim$index.id] = dimValue; }); topDataPath.forEach(function (dimValue, index) { var _topMetaDim$index; dimRecord[(_topMetaDim$index = topMetaDim[index]) === null || _topMetaDim$index === void 0 ? void 0 : _topMetaDim$index.id] = dimValue; }); return cellRender(value, dimRecord, topNode.code); } // 正常渲染 if ((_metaInd$find = metaInd.find(function (_ref7) { var id = _ref7.id; return id === topNode.code; })) === null || _metaInd$find === void 0 ? void 0 : _metaInd$find.isRate) { return "".concat(strip(value * 100).toFixed(2), "%"); } else { return value; } } }, props))); }; export { CRColumn as Column, CRLine as Line, CRPivotTable as PivotTable };