TableBody.tsx 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. import React from 'react'
  2. import classnames from 'classnames'
  3. import { IDrawingData, IMetricAxisConfig, ILegend } from './Pivot'
  4. import { IWidgetMetric, DimetionType, RenderType, IChartStyles } from '../Widget'
  5. import Cell from './Cell'
  6. import Chart, { IChartUnit, IChartLine, IChartBlock } from './Chart'
  7. import { PIVOT_CANVAS_SIZE_LIMIT, PIVOT_CANVAS_POLAR_SIZE_LIMIT } from 'app/globalConstants'
  8. import {
  9. getPivotContentTextWidth,
  10. getPivotCellWidth,
  11. getPivotCellHeight,
  12. getChartPieces,
  13. decodeMetricName
  14. } from '../util'
  15. import { uuid } from 'utils/util'
  16. import { IDataParamProperty } from '../Workbench/OperatingPanel'
  17. const styles = require('./Pivot.less')
  18. export interface ITableBodyProps {
  19. cols: string[]
  20. rows: string[]
  21. rowKeys: string[][]
  22. colKeys: string[][]
  23. rowWidths: number[]
  24. selectedChart: number
  25. selectedItems?: number[]
  26. rowTree: object
  27. colTree: object
  28. tree: object
  29. interacting?: boolean
  30. metrics: IWidgetMetric[]
  31. metricAxisConfig: IMetricAxisConfig
  32. chartStyles: IChartStyles
  33. drawingData: IDrawingData
  34. dimetionAxis: DimetionType
  35. color?: IDataParamProperty
  36. label?: IDataParamProperty
  37. size?: IDataParamProperty
  38. xAxis?: IDataParamProperty
  39. tip?: IDataParamProperty
  40. renderType: RenderType
  41. legend: ILegend
  42. onCheckTableInteract?: () => boolean
  43. onDoInteract?: (triggerData: object) => void
  44. getDataDrillDetail?: (position: string) => void
  45. isDrilling?: boolean
  46. ifSelectedTdToDrill: (obj: any) => any
  47. whichDataDrillBrushed?: boolean | object []
  48. // onHideDrillPanel?: (swtich: boolean) => void
  49. onSelectChartsItems?: (selectedItems: number[]) => void
  50. }
  51. interface ITableBodyState {
  52. selectedPivotTds: any[]
  53. }
  54. export class TableBody extends React.Component<ITableBodyProps, ITableBodyState> {
  55. constructor (props) {
  56. super(props)
  57. this.state = {
  58. selectedPivotTds: []
  59. }
  60. }
  61. private gridCutting = (width, height, chartGrid) => {
  62. // console.log(chartGrid)
  63. const chunks = this.horizontalCutting(height, chartGrid)
  64. // console.log(chunks)
  65. chunks.forEach((chunk) => {
  66. chunk.data = this.verticalCutting(width, chunk.data)
  67. })
  68. return chunks
  69. }
  70. private horizontalCutting = (height, chartGrid) => {
  71. const { metrics, dimetionAxis, drawingData: { multiCoordinate } } = this.props
  72. const limit = multiCoordinate ? PIVOT_CANVAS_POLAR_SIZE_LIMIT : PIVOT_CANVAS_SIZE_LIMIT
  73. if (height > limit) {
  74. const result = []
  75. let chunk = {
  76. key: '',
  77. height: 0,
  78. data: []
  79. }
  80. chartGrid.forEach((cg, index) => {
  81. const lineHeight = dimetionAxis === 'col'
  82. ? cg.height * metrics.length
  83. : cg.height
  84. if (chunk.height + lineHeight > limit) {
  85. chunk.key = `${index}${chunk.data.map((d) => d.key).join(',')}`
  86. result.push(chunk)
  87. chunk = {
  88. key: '',
  89. height: 0,
  90. data: []
  91. }
  92. }
  93. chunk.height += lineHeight
  94. chunk.data.push(cg)
  95. if (index === chartGrid.length - 1) {
  96. chunk.key = `${index}${chunk.data.map((d) => d.key).join(',')}`
  97. result.push(chunk)
  98. }
  99. })
  100. return result
  101. } else {
  102. return [{
  103. key: 'chunk',
  104. height,
  105. data: chartGrid
  106. }]
  107. }
  108. }
  109. private verticalCutting = (width, chartLines: IChartLine[]) => {
  110. const { metrics, dimetionAxis, drawingData: { multiCoordinate } } = this.props
  111. const limit = multiCoordinate ? PIVOT_CANVAS_POLAR_SIZE_LIMIT : PIVOT_CANVAS_SIZE_LIMIT
  112. if (width > limit) {
  113. const result = {}
  114. chartLines.forEach((line: IChartLine) => {
  115. let blockLine: IChartLine = this.initBlockLine(line)
  116. let block: IChartBlock = this.initBlock(blockLine)
  117. line.data.forEach((cu: IChartUnit, index) => {
  118. const unitWidth = dimetionAxis === 'row'
  119. ? cu.width * metrics.length
  120. : cu.width
  121. if (block.width + unitWidth > limit) {
  122. if (result[index - 1]) {
  123. const currentBlock = result[index - 1]
  124. // currentBlock.width += block.width
  125. currentBlock.data = currentBlock.data.concat(block.data)
  126. } else {
  127. result[index - 1] = {
  128. ...block,
  129. key: `${index - 1}${block.data.map((d) => d.key).join(',')}`
  130. }
  131. }
  132. blockLine = this.initBlockLine(line)
  133. block = this.initBlock(blockLine)
  134. }
  135. block.width += unitWidth
  136. blockLine.data.push(cu)
  137. if (index === line.data.length - 1) {
  138. if (result[index]) {
  139. const currentBlock = result[index]
  140. // currentBlock.width += block.width
  141. currentBlock.data = currentBlock.data.concat(block.data)
  142. } else {
  143. result[index] = {
  144. ...block,
  145. key: `${index}${block.data.map((d) => d.key).join(',')}`
  146. }
  147. }
  148. }
  149. })
  150. })
  151. return Object.values(result).map((block: IChartBlock) => ({
  152. ...block,
  153. pieces: getChartPieces(
  154. block.data.reduce((lsum, line: IChartLine) =>
  155. lsum + line.data.reduce((usum, unit: IChartUnit) =>
  156. usum + (dimetionAxis === 'col' ? unit.records.length * metrics.length : unit.records.length)
  157. , 0)
  158. , 0),
  159. block.data.length
  160. )
  161. }))
  162. } else {
  163. return [{
  164. key: 'block',
  165. width,
  166. data: chartLines,
  167. pieces: getChartPieces(
  168. chartLines.reduce((lsum, line: IChartLine) =>
  169. lsum + line.data.reduce((usum, unit: IChartUnit) =>
  170. usum + (dimetionAxis === 'col' ? unit.records.length * metrics.length : unit.records.length)
  171. , 0)
  172. , 0),
  173. chartLines.length
  174. )
  175. }]
  176. }
  177. }
  178. private initBlock = (blockLine) => ({
  179. key: '',
  180. width: 0,
  181. data: [blockLine],
  182. pieces: 0
  183. })
  184. private initBlockLine = (line) => ({
  185. ...line,
  186. key: `${uuid(8, 16)}${line.key}`,
  187. data: []
  188. })
  189. public render () {
  190. const {
  191. rows,
  192. cols,
  193. rowKeys,
  194. colKeys,
  195. rowTree,
  196. rowWidths,
  197. colTree,
  198. tree,
  199. metrics,
  200. metricAxisConfig,
  201. chartStyles,
  202. drawingData,
  203. dimetionAxis,
  204. color,
  205. label,
  206. size,
  207. xAxis,
  208. tip,
  209. renderType,
  210. legend,
  211. onCheckTableInteract,
  212. onDoInteract,
  213. getDataDrillDetail,
  214. isDrilling,
  215. ifSelectedTdToDrill
  216. // onHideDrillPanel
  217. } = this.props
  218. const { elementSize, unitMetricWidth, unitMetricHeight, tableBodyCollapsed } = drawingData
  219. let tableBody = null
  220. const chartGrid: IChartLine[] = []
  221. const cells = []
  222. let tableWidth = 0
  223. if (dimetionAxis) {
  224. let metricAxisCount = 0
  225. if (colKeys.length && rowKeys.length) {
  226. let chartRowLineRecorder: IChartUnit[][] = []
  227. rowKeys.forEach((rk, i) => {
  228. const flatRowKey = rk.join(String.fromCharCode(0))
  229. let chartLine: IChartUnit[] = []
  230. // tableWidth = 0
  231. colKeys.forEach((ck, j) => {
  232. const flatColKey = ck.join(String.fromCharCode(0))
  233. const records = tree[flatRowKey][flatColKey]
  234. if (dimetionAxis === 'col') {
  235. const nextCk = colKeys[j + 1] || []
  236. let lastUnit = chartLine[chartLine.length - 1]
  237. if (!lastUnit || lastUnit.ended) {
  238. lastUnit = {
  239. key: `${flatRowKey}${flatColKey}`,
  240. width: 0,
  241. records: [],
  242. ended: false
  243. }
  244. chartLine.push(lastUnit)
  245. }
  246. lastUnit.records.push({
  247. key: ck[ck.length - 1],
  248. value: records
  249. })
  250. if (ck.length === 1 && j === colKeys.length - 1 ||
  251. ck[ck.length - 2] !== nextCk[nextCk.length - 2]) {
  252. const unitWidth = lastUnit.records.length * elementSize
  253. // tableWidth += unitWidth
  254. lastUnit.width = unitWidth
  255. lastUnit.ended = true
  256. if (!nextCk.length) {
  257. chartGrid.push({
  258. key: flatRowKey,
  259. height: unitMetricHeight,
  260. data: chartLine.slice()
  261. })
  262. // tableHeight += unitMetricHeight * (extraMetricCount + 1)
  263. metricAxisCount += 1
  264. chartLine = []
  265. }
  266. }
  267. } else {
  268. const nextRk = rowKeys[i + 1] || []
  269. if (!chartRowLineRecorder[j]) {
  270. chartRowLineRecorder[j] = []
  271. }
  272. const rowLine: IChartUnit[] = chartRowLineRecorder[j]
  273. let lastUnit = rowLine[rowLine.length - 1]
  274. if (!lastUnit || lastUnit.ended) {
  275. lastUnit = {
  276. key: `${flatColKey}${flatRowKey}`,
  277. width: 0,
  278. records: [],
  279. ended: false
  280. }
  281. rowLine.push(lastUnit)
  282. }
  283. lastUnit.records.push({
  284. key: rk[rk.length - 1],
  285. value: records
  286. })
  287. if (rk.length === 1 && i === rowKeys.length - 1 ||
  288. rk[rk.length - 2] !== nextRk[nextRk.length - 2]) {
  289. // tableWidth += unitMetricWidth * (extraMetricCount + 1)
  290. lastUnit.width = unitMetricWidth
  291. lastUnit.ended = true
  292. if (j === colKeys.length - 1) {
  293. const height = lastUnit.records.length * elementSize
  294. chartGrid.push({
  295. key: flatRowKey,
  296. height,
  297. data: chartRowLineRecorder.reduce((arr, r) => arr.concat(r), [])
  298. })
  299. // tableHeight += height
  300. chartRowLineRecorder = []
  301. }
  302. if (i === rowKeys.length - 1) {
  303. metricAxisCount += 1
  304. }
  305. }
  306. }
  307. })
  308. })
  309. } else if (colKeys.length) {
  310. const chartLine: IChartUnit[] = []
  311. // tableWidth = 0
  312. colKeys.forEach((ck, j) => {
  313. const flatColKey = ck.join(String.fromCharCode(0))
  314. const { records } = colTree[flatColKey]
  315. if (dimetionAxis === 'col') {
  316. const nextCk = colKeys[j + 1] || []
  317. let lastUnit = chartLine[chartLine.length - 1]
  318. if (!lastUnit || lastUnit.ended) {
  319. lastUnit = {
  320. key: flatColKey,
  321. width: 0,
  322. records: [],
  323. ended: false
  324. }
  325. chartLine.push(lastUnit)
  326. }
  327. lastUnit.records.push({
  328. key: ck[ck.length - 1],
  329. value: records
  330. })
  331. if (ck.length === 1 && j === colKeys.length - 1 ||
  332. ck[ck.length - 2] !== nextCk[nextCk.length - 2]) {
  333. const unitWidth = lastUnit.records.length * elementSize
  334. // tableWidth += unitWidth
  335. lastUnit.width = unitWidth
  336. lastUnit.ended = true
  337. if (!nextCk.length) {
  338. chartGrid.push({
  339. key: flatColKey,
  340. height: unitMetricHeight,
  341. data: chartLine.slice()
  342. })
  343. metricAxisCount += 1
  344. // tableHeight += unitMetricHeight * (extraMetricCount + 1)
  345. }
  346. }
  347. } else {
  348. // tableWidth += unitMetricWidth * (extraMetricCount + 1)
  349. chartLine.push({
  350. key: flatColKey,
  351. width: unitMetricWidth,
  352. records: [{
  353. key: ck[ck.length - 1],
  354. value: records
  355. }],
  356. ended: true
  357. })
  358. metricAxisCount += 1
  359. if (j === colKeys.length - 1) {
  360. chartGrid.push({
  361. key: flatColKey,
  362. height: elementSize,
  363. data: chartLine.slice()
  364. })
  365. // tableHeight = elementSize
  366. }
  367. }
  368. })
  369. } else if (rowKeys.length) {
  370. let chartLine: IChartUnit[] = []
  371. rowKeys.forEach((rk, i) => {
  372. const flatRowKey = rk.join(String.fromCharCode(0))
  373. const { records } = rowTree[flatRowKey]
  374. // tableWidth = 0
  375. if (dimetionAxis === 'row') {
  376. const nextRk = rowKeys[i + 1] || []
  377. let lastUnit = chartLine[chartLine.length - 1]
  378. if (!lastUnit || lastUnit.ended) {
  379. lastUnit = {
  380. key: flatRowKey,
  381. width: 0,
  382. records: [],
  383. ended: false
  384. }
  385. chartLine.push(lastUnit)
  386. }
  387. lastUnit.records.push({
  388. key: rk[rk.length - 1],
  389. value: records
  390. })
  391. if (rk.length === 1 && i === rowKeys.length - 1 ||
  392. rk[rk.length - 2] !== nextRk[nextRk.length - 2]) {
  393. // tableWidth += unitMetricWidth * (extraMetricCount + 1)
  394. lastUnit.width = unitMetricWidth
  395. lastUnit.ended = true
  396. const height = lastUnit.records.length * elementSize
  397. chartGrid.push({
  398. key: flatRowKey,
  399. height,
  400. data: chartLine.slice()
  401. })
  402. // tableHeight += height
  403. chartLine = []
  404. if (i === rowKeys.length - 1) {
  405. metricAxisCount += 1
  406. }
  407. }
  408. } else {
  409. // tableWidth += elementSize
  410. chartGrid.push({
  411. key: flatRowKey,
  412. height: unitMetricHeight,
  413. data: [{
  414. key: flatRowKey,
  415. width: elementSize,
  416. records: [{
  417. key: rk[rk.length - 1],
  418. value: records
  419. }],
  420. ended: false
  421. }]
  422. })
  423. metricAxisCount += 1
  424. // tableHeight += unitMetricHeight * (extraMetricCount + 1)
  425. }
  426. })
  427. } else {
  428. const records = tree[0]
  429. const width = dimetionAxis === 'col' ? elementSize : unitMetricWidth
  430. const height = dimetionAxis === 'row' ? elementSize : unitMetricHeight
  431. // tableWidth = width * (dimetionAxis === 'row' ? extraMetricCount + 1 : 1)
  432. // tableHeight = height * (dimetionAxis === 'col' ? extraMetricCount + 1 : 1)
  433. const chartUnit = {
  434. width,
  435. // records: records.map((r) => ({
  436. // key: '',
  437. // value: [r]
  438. // })),
  439. records: [{
  440. key: 'data',
  441. value: records
  442. }],
  443. ended: true
  444. }
  445. chartGrid.push({ height, data: [chartUnit] })
  446. }
  447. const colKeyLength = colKeys.length || 1
  448. const rowKeyLength = rowKeys.length || 1
  449. metricAxisCount = metricAxisCount || 1
  450. let tableHeight = 0
  451. if (dimetionAxis === 'col') {
  452. tableWidth = colKeyLength * elementSize
  453. tableHeight = metricAxisCount * unitMetricHeight * metrics.length
  454. } else {
  455. tableWidth = metricAxisCount * unitMetricWidth * metrics.length
  456. tableHeight = rowKeyLength * elementSize
  457. }
  458. tableBody = (
  459. <Chart
  460. width={tableWidth}
  461. height={tableHeight}
  462. cols={cols}
  463. rows={rows}
  464. dimetionAxisCount={dimetionAxis === 'col' ? colKeyLength : rowKeyLength}
  465. metricAxisCount={metricAxisCount}
  466. metrics={metrics}
  467. data={this.gridCutting(tableWidth, tableHeight, chartGrid)}
  468. chartStyles={chartStyles}
  469. drawingData={drawingData}
  470. dimetionAxis={dimetionAxis}
  471. metricAxisConfig={metricAxisConfig}
  472. color={color}
  473. label={label}
  474. size={size}
  475. xAxis={xAxis}
  476. tip={tip}
  477. renderType={renderType}
  478. legend={legend}
  479. onCheckTableInteract={onCheckTableInteract}
  480. onDoInteract={onDoInteract}
  481. getDataDrillDetail={getDataDrillDetail}
  482. isDrilling={isDrilling}
  483. selectedChart={this.props.selectedChart}
  484. whichDataDrillBrushed={this.props.whichDataDrillBrushed}
  485. selectedItems={this.props.selectedItems}
  486. onSelectChartsItems={this.props.onSelectChartsItems}
  487. />
  488. )
  489. } else {
  490. if (colKeys.length && rowKeys.length) {
  491. rowKeys.forEach((rk) => {
  492. const flatRowKey = rk.join(String.fromCharCode(0))
  493. const line = []
  494. tableWidth = 0
  495. colKeys.forEach((ck) => {
  496. const flatColKey = ck.join(String.fromCharCode(0))
  497. const records = tree[flatRowKey][flatColKey]
  498. const { width, height } = colTree[flatColKey]
  499. const cellWidth = getPivotCellWidth(width)
  500. tableWidth += cellWidth
  501. line.push(
  502. <Cell
  503. key={`${flatRowKey}${flatColKey}`}
  504. colKey={flatColKey}
  505. rowKey={flatRowKey}
  506. width={cellWidth}
  507. interacting={this.props.interacting}
  508. height={getPivotCellHeight(height)}
  509. metrics={metrics}
  510. data={records}
  511. chartStyles={chartStyles}
  512. color={color}
  513. legend={legend}
  514. ifSelectedTdToDrill={ifSelectedTdToDrill}
  515. isDrilling={isDrilling}
  516. />
  517. )
  518. })
  519. cells.push(
  520. <tr key={flatRowKey}>
  521. {line}
  522. </tr>
  523. )
  524. })
  525. } else if (colKeys.length) {
  526. const line = []
  527. tableWidth = 0
  528. colKeys.forEach((ck) => {
  529. const flatColKey = ck.join(String.fromCharCode(0))
  530. const { width, height, records } = colTree[flatColKey]
  531. const cellWidth = getPivotCellWidth(width)
  532. tableWidth += cellWidth
  533. line.push(
  534. <Cell
  535. key={flatColKey}
  536. colKey={flatColKey}
  537. width={cellWidth}
  538. interacting={this.props.interacting}
  539. height={getPivotCellHeight(height)}
  540. metrics={metrics}
  541. data={records}
  542. chartStyles={chartStyles}
  543. color={color}
  544. legend={legend}
  545. ifSelectedTdToDrill={ifSelectedTdToDrill}
  546. isDrilling={isDrilling}
  547. />
  548. )
  549. })
  550. cells.push(
  551. // <tr key={uuid(8, 16)}>
  552. <tr key="colKeyLength">
  553. {line}
  554. </tr>
  555. )
  556. } else if (rowKeys.length) {
  557. rowKeys.forEach((rk) => {
  558. const flatRowKey = rk.join(String.fromCharCode(0))
  559. const { height, records } = rowTree[flatRowKey]
  560. const line = []
  561. tableWidth = 0
  562. const cellWidth = getPivotCellWidth(rowWidths[rowWidths.length - 1])
  563. tableWidth += cellWidth
  564. line.push(
  565. <Cell
  566. key={flatRowKey}
  567. rowKey={flatRowKey}
  568. width={cellWidth}
  569. height={getPivotCellHeight(height)}
  570. metrics={metrics}
  571. data={records}
  572. interacting={this.props.interacting}
  573. chartStyles={chartStyles}
  574. color={color}
  575. legend={legend}
  576. ifSelectedTdToDrill={ifSelectedTdToDrill}
  577. isDrilling={isDrilling}
  578. />
  579. )
  580. if (line.length) {
  581. cells.push(
  582. <tr key={flatRowKey}>
  583. {line}
  584. </tr>
  585. )
  586. }
  587. })
  588. } else {
  589. const records = tree[0]
  590. if (records && metrics.length) {
  591. let width = 0
  592. metrics.forEach((m) => {
  593. const text = records[`${m.agg}(${m.name})`]
  594. width = Math.max(width, getPivotContentTextWidth(text))
  595. })
  596. const height = getPivotCellHeight()
  597. cells.push(
  598. <tr key="metricLength">
  599. <Cell
  600. key="metricsLength"
  601. width={width}
  602. height={height}
  603. metrics={metrics}
  604. data={records}
  605. interacting={this.props.interacting}
  606. chartStyles={chartStyles}
  607. color={color}
  608. legend={legend}
  609. ifSelectedTdToDrill={ifSelectedTdToDrill}
  610. isDrilling={isDrilling}
  611. />
  612. </tr>
  613. )
  614. }
  615. }
  616. tableBody = (
  617. <table className={styles.pivot} style={{width: tableWidth}}>
  618. <tbody>
  619. {cells}
  620. </tbody>
  621. </table>
  622. )
  623. }
  624. const containerClass = classnames({
  625. [styles.columnBody]: true,
  626. [styles.bodyCollapsed]: tableBodyCollapsed,
  627. [styles.raw]: !dimetionAxis
  628. })
  629. return (
  630. <div className={containerClass}>
  631. {tableBody}
  632. </div>
  633. )
  634. }
  635. }
  636. export default TableBody