Pivot.tsx 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895
  1. import React from 'react'
  2. import { findDOMNode } from 'react-dom'
  3. import {
  4. naturalSort,
  5. decodeMetricName,
  6. getPivotContentTextWidth,
  7. getTableBodyWidth,
  8. getTableBodyHeight,
  9. getChartElementSize,
  10. shouldTableBodyCollapsed,
  11. getChartUnitMetricWidth,
  12. getChartUnitMetricHeight,
  13. getAxisInterval,
  14. getTextWidth,
  15. getSizeRate,
  16. getAggregatorLocale
  17. } from '../util'
  18. import {
  19. PIVOT_LINE_HEIGHT,
  20. PIVOT_CHART_SPLIT_SIZE,
  21. PIVOT_LEGEND_ITEM_PADDING,
  22. PIVOT_LEGEND_PADDING,
  23. PIVOT_DEFAULT_SCATTER_SIZE,
  24. DEFAULT_SPLITER
  25. } from 'app/globalConstants'
  26. import Corner from './Corner'
  27. import RowTitle from './RowTitle'
  28. import RowHeader from './RowHeader'
  29. import RowFooter from './RowFooter'
  30. import ColumnTitle from './ColumnTitle'
  31. import ColumnHeader from './ColumnHeader'
  32. import TableBody from './TableBody'
  33. import ColumnFooter from './ColumnFooter'
  34. import Legend from './Legend'
  35. import { RenderType, IWidgetProps } from '../Widget'
  36. import PivotTypes from '../../config/pivot/PivotTypes'
  37. const styles = require('./Pivot.less')
  38. export interface IDrawingData {
  39. elementSize: number
  40. unitMetricWidth: number
  41. unitMetricHeight: number
  42. tableBodyCollapsed: boolean
  43. multiCoordinate: boolean
  44. sizeRate: { [key: string]: number }
  45. }
  46. export interface IMetricAxisConfig {
  47. [key: string]: {
  48. [key: string]: {
  49. min: number
  50. max: number
  51. interval: number
  52. }
  53. }
  54. }
  55. export interface ILegend {
  56. [key: string]: string[]
  57. }
  58. export interface IPivotProps extends IWidgetProps {
  59. width: number
  60. height: number
  61. }
  62. interface IPivotStates {
  63. legendSelected: ILegend
  64. renderType: RenderType
  65. cellSelected: object
  66. }
  67. export class Pivot extends React.PureComponent<IPivotProps, IPivotStates> {
  68. constructor(props) {
  69. super(props)
  70. this.state = {
  71. legendSelected: {},
  72. renderType: 'rerender',
  73. cellSelected: {}
  74. }
  75. }
  76. private tableBodyWidth = 0
  77. private tableBodyHeight = 0
  78. private rowKeys = []
  79. private colKeys = []
  80. private rowTree: {
  81. [key: string]: {
  82. width?: number
  83. height?: number
  84. records: any[]
  85. }
  86. } = {}
  87. private colTree: {
  88. [key: string]: {
  89. width?: number
  90. height?: number
  91. records: any[]
  92. }
  93. } = {}
  94. private tree: {
  95. [key: string]: {
  96. [key: string]: any[]
  97. }
  98. } = {}
  99. private rowHeaderWidths = []
  100. private drawingData: IDrawingData = {
  101. elementSize: 0,
  102. unitMetricWidth: 0,
  103. unitMetricHeight: 0,
  104. tableBodyCollapsed: false,
  105. multiCoordinate: false,
  106. sizeRate: {}
  107. }
  108. private groupedData: {
  109. [key: string]: {
  110. yAxisMin: number
  111. yAxisMax: number
  112. scatterXAxisMin: number
  113. scatterXAxisMax: number
  114. sizeMin: number
  115. sizeMax: number
  116. }
  117. } = {}
  118. private metricAxisConfig: IMetricAxisConfig = void 0
  119. public rowHeader: HTMLElement = null
  120. public columnHeader: HTMLElement = null
  121. public tableBody: HTMLElement = null
  122. public columnFooter: HTMLElement = null
  123. public componentWillMount() {
  124. this.getRenderData(this.props)
  125. }
  126. public componentWillReceiveProps(nextProps) {
  127. const { renderType, color, isDrilling } = nextProps
  128. const { legendSelected } = this.state
  129. this.setState({
  130. renderType,
  131. legendSelected:
  132. color && !color.items.length && Object.keys(legendSelected).length
  133. ? {}
  134. : legendSelected
  135. })
  136. if (isDrilling === false) {
  137. this.setState({
  138. cellSelected: {}
  139. })
  140. }
  141. if (nextProps.data !== this.props.data) {
  142. this.setState({
  143. cellSelected: {}
  144. })
  145. }
  146. }
  147. public componentWillUpdate(nextProps: IPivotProps) {
  148. this.rowKeys = []
  149. this.colKeys = []
  150. this.rowTree = {}
  151. this.colTree = {}
  152. this.tree = {}
  153. this.drawingData = {
  154. elementSize: 0,
  155. unitMetricWidth: 0,
  156. unitMetricHeight: 0,
  157. tableBodyCollapsed: false,
  158. multiCoordinate: false,
  159. sizeRate: {}
  160. }
  161. this.groupedData = {}
  162. this.metricAxisConfig = void 0
  163. this.getRenderData(nextProps)
  164. this.rowHeader.scrollTop = 0
  165. this.columnHeader.scrollLeft = 0
  166. this.tableBody.scrollTop = this.tableBody.scrollLeft = 0
  167. }
  168. public componentWillUnmount() {
  169. this.rowKeys = []
  170. this.colKeys = []
  171. this.rowHeaderWidths = []
  172. this.rowTree = {}
  173. this.colTree = {}
  174. this.tree = {}
  175. this.drawingData = {
  176. elementSize: 0,
  177. unitMetricWidth: 0,
  178. unitMetricHeight: 0,
  179. tableBodyCollapsed: false,
  180. multiCoordinate: false,
  181. sizeRate: {}
  182. }
  183. this.groupedData = {}
  184. this.metricAxisConfig = void 0
  185. }
  186. private getRenderData = (props) => {
  187. const {
  188. width,
  189. height,
  190. cols,
  191. rows,
  192. metrics,
  193. data,
  194. xAxis,
  195. dimetionAxis
  196. } = props
  197. this.rowHeaderWidths = rows.map((r) => getPivotContentTextWidth(r, 'bold'))
  198. if (!cols.length && !rows.length) {
  199. this.tree[0] = data.slice()
  200. } else {
  201. data.forEach((record) => {
  202. this.getRowKeyAndColKey(props, record, !!dimetionAxis)
  203. })
  204. }
  205. if (dimetionAxis) {
  206. this.tableBodyWidth = getTableBodyWidth(
  207. dimetionAxis,
  208. width - this.getLegendWidth(props),
  209. this.rowHeaderWidths
  210. )
  211. this.tableBodyHeight = getTableBodyHeight(
  212. dimetionAxis,
  213. height,
  214. cols.length
  215. )
  216. this.drawingData.unitMetricWidth = getChartUnitMetricWidth(
  217. this.tableBodyWidth,
  218. this.colKeys.length || 1,
  219. metrics.length
  220. )
  221. this.drawingData.unitMetricHeight = getChartUnitMetricHeight(
  222. this.tableBodyHeight,
  223. this.rowKeys.length || 1,
  224. metrics.length
  225. )
  226. this.drawingData.multiCoordinate =
  227. metrics.some((m) => m.chart.coordinate === 'polar') ||
  228. (xAxis && xAxis.items.length)
  229. this.drawingData.elementSize = getChartElementSize(
  230. dimetionAxis,
  231. [this.tableBodyWidth, this.tableBodyHeight],
  232. [this.colKeys.length, this.rowKeys.length],
  233. this.drawingData.multiCoordinate
  234. )
  235. this.drawingData.tableBodyCollapsed = shouldTableBodyCollapsed(
  236. dimetionAxis,
  237. this.drawingData.elementSize,
  238. this.tableBodyHeight,
  239. this.rowKeys.length
  240. )
  241. const treeData = this.getTreeData()
  242. this.recordGrouping(props, treeData)
  243. this.metricAxisConfig = metrics.reduce((obj: IMetricAxisConfig, m) => {
  244. const {
  245. yAxisMin,
  246. yAxisMax,
  247. scatterXAxisMin,
  248. scatterXAxisMax,
  249. sizeMin,
  250. sizeMax
  251. } = this.groupedData[m.name]
  252. const yAxisSplitNumber =
  253. dimetionAxis === 'col'
  254. ? Math.ceil(
  255. this.drawingData.unitMetricHeight / PIVOT_CHART_SPLIT_SIZE
  256. )
  257. : Math.ceil(
  258. this.drawingData.unitMetricWidth / PIVOT_CHART_SPLIT_SIZE
  259. )
  260. const scatterXAxisSplitNumber = Math.ceil(
  261. this.drawingData.elementSize / PIVOT_CHART_SPLIT_SIZE
  262. )
  263. const yAxisInterval = getAxisInterval(yAxisMax, yAxisSplitNumber)
  264. const scatterXAxisInterval = getAxisInterval(
  265. scatterXAxisMax,
  266. scatterXAxisSplitNumber
  267. )
  268. this.drawingData.sizeRate[m.name] = getSizeRate(sizeMin, sizeMax)
  269. obj[m.name] = {
  270. yAxis: {
  271. min: yAxisMin,
  272. max: yAxisInterval * yAxisSplitNumber,
  273. interval: yAxisInterval
  274. },
  275. scatterXAxis: {
  276. min: scatterXAxisMin,
  277. max: scatterXAxisInterval * scatterXAxisSplitNumber,
  278. interval: scatterXAxisInterval
  279. }
  280. }
  281. return obj
  282. }, {})
  283. }
  284. }
  285. private getTreeData = (): any[][] => {
  286. if (this.colKeys.length && this.rowKeys.length) {
  287. return Object.values(this.tree).reduce(
  288. (data: any[], row) => data.concat(Object.values(row)),
  289. []
  290. )
  291. } else if (this.colKeys.length) {
  292. return Object.values(this.colTree).map((col: any) => col.records)
  293. } else if (this.rowKeys.length) {
  294. return Object.values(this.rowTree).map((row: any) => row.records)
  295. } else {
  296. return [(this.tree[0] as any) || []]
  297. }
  298. }
  299. private getRowKeyAndColKey = (
  300. props: IPivotProps,
  301. record: object,
  302. hasDimetionAxis: boolean
  303. ) => {
  304. const { cols, rows, metrics } = props
  305. const rowKey = []
  306. const colKey = []
  307. let flatRowKeys
  308. let flatColKeys
  309. const metricNames = metrics.map(
  310. (m) => `${m.name}${DEFAULT_SPLITER}${m.agg}`
  311. )
  312. if (!metricNames.length) {
  313. metricNames.push('无指标值')
  314. }
  315. if (~rows.findIndex((r) => r.name === '指标名称')) {
  316. metricNames.forEach((mn) => {
  317. const keyArr = []
  318. const [name, id, agg] = mn.split(DEFAULT_SPLITER)
  319. const metricTextWidth = getPivotContentTextWidth(
  320. `[${getAggregatorLocale(agg)}]${name}`,
  321. 'bold'
  322. )
  323. rows.forEach((r, i) => {
  324. const value = r.name === '指标名称' ? mn : record[r.name]
  325. const textWidth =
  326. r.name === '指标名称'
  327. ? metricTextWidth
  328. : getPivotContentTextWidth(value, 'bold')
  329. this.rowHeaderWidths[i] = Math.max(
  330. textWidth,
  331. this.rowHeaderWidths[i] || 0
  332. )
  333. keyArr.push(value)
  334. })
  335. rowKey.push(keyArr)
  336. })
  337. flatRowKeys = rowKey.reduce(
  338. (arr, keys) => arr.concat(keys.join(String.fromCharCode(0))),
  339. []
  340. )
  341. } else {
  342. rows.forEach((r, i) => {
  343. const value = record[r.name]
  344. const textWidth = getPivotContentTextWidth(value, 'bold')
  345. this.rowHeaderWidths[i] = Math.max(
  346. textWidth,
  347. this.rowHeaderWidths[i] || 0
  348. )
  349. rowKey.push(value)
  350. })
  351. flatRowKeys = [rowKey.join(String.fromCharCode(0))]
  352. }
  353. if (~cols.findIndex((c) => c.name === '指标名称')) {
  354. metricNames.forEach((mn) => {
  355. const keyArr = []
  356. cols.forEach((c) => {
  357. const value = c.name === '指标名称' ? mn : record[c.name]
  358. keyArr.push(value)
  359. })
  360. colKey.push(keyArr)
  361. })
  362. flatColKeys = colKey.reduce(
  363. (arr, keys) => arr.concat(keys.join(String.fromCharCode(0))),
  364. []
  365. )
  366. } else {
  367. cols.forEach((c) => {
  368. colKey.push(record[c.name])
  369. })
  370. flatColKeys = [colKey.join(String.fromCharCode(0))]
  371. }
  372. flatRowKeys.forEach((flatRowKey) => {
  373. flatColKeys.forEach((flatColKey) => {
  374. if (rowKey.length) {
  375. if (!this.rowTree[flatRowKey]) {
  376. const height = !hasDimetionAxis && { height: PIVOT_LINE_HEIGHT }
  377. this.rowTree[flatRowKey] = { ...height, records: [] }
  378. this.rowKeys.push(flatRowKey.split(String.fromCharCode(0)))
  379. }
  380. this.rowTree[flatRowKey].records.push(record)
  381. if (metrics.length) {
  382. if (!hasDimetionAxis) {
  383. const cellHeight = [rows, cols].some(
  384. (items) =>
  385. items.findIndex((item) => item.name === '指标名称') >= 0
  386. )
  387. ? PIVOT_LINE_HEIGHT
  388. : (PIVOT_LINE_HEIGHT + 1) * metrics.length - 1
  389. this.rowTree[flatRowKey].height = cellHeight
  390. }
  391. }
  392. }
  393. if (colKey.length) {
  394. if (!this.colTree[flatColKey]) {
  395. const width = !hasDimetionAxis && {
  396. width: Math.max(
  397. ...flatColKey.split(String.fromCharCode(0)).map((c) => {
  398. if (c.includes(DEFAULT_SPLITER)) {
  399. const [name, id, agg] = c.split(DEFAULT_SPLITER)
  400. return getPivotContentTextWidth(
  401. `[${getAggregatorLocale(agg)}]${name}`,
  402. 'bold'
  403. )
  404. } else {
  405. return getPivotContentTextWidth(c, 'bold')
  406. }
  407. })
  408. )
  409. }
  410. const height = !hasDimetionAxis && { height: PIVOT_LINE_HEIGHT }
  411. this.colTree[flatColKey] = { ...width, ...height, records: [] }
  412. this.colKeys.push(flatColKey.split(String.fromCharCode(0)))
  413. }
  414. this.colTree[flatColKey].records.push(record)
  415. if (metrics.length) {
  416. if (!hasDimetionAxis) {
  417. const maxTextWidth = Math.max(
  418. ...metrics.map((m) =>
  419. getPivotContentTextWidth(
  420. record[`${m.agg}(${decodeMetricName(m.name)})`]
  421. )
  422. )
  423. )
  424. const cellHeight = [rows, cols].some(
  425. (items) =>
  426. items.findIndex((item) => item.name === '指标名称') >= 0
  427. )
  428. ? PIVOT_LINE_HEIGHT
  429. : (PIVOT_LINE_HEIGHT + 1) * metrics.length - 1
  430. this.colTree[flatColKey].width = Math.max(
  431. this.colTree[flatColKey].width,
  432. maxTextWidth
  433. )
  434. this.colTree[flatColKey].height = cellHeight
  435. }
  436. }
  437. }
  438. if (rowKey.length && colKey.length) {
  439. if (!this.tree[flatRowKey]) {
  440. this.tree[flatRowKey] = {}
  441. }
  442. if (!this.tree[flatRowKey][flatColKey]) {
  443. this.tree[flatRowKey][flatColKey] = []
  444. }
  445. this.tree[flatRowKey][flatColKey].push(record)
  446. }
  447. })
  448. })
  449. }
  450. private sortingKeys = (keys) => (a, b) => {
  451. for (let i = 0; i < keys.length; i += 1) {
  452. const comparison = naturalSort(a[i], b[i])
  453. if (comparison) {
  454. return comparison
  455. }
  456. }
  457. return 0
  458. }
  459. private recordGrouping = (props: IPivotProps, records: any[][]) => {
  460. const { cols, rows, metrics, xAxis, size, color, label } = props
  461. const colAndRows = [...cols, ...rows]
  462. metrics.forEach((metric) => {
  463. if (!this.groupedData[metric.name]) {
  464. this.groupedData[metric.name] = {
  465. yAxisMin: 0,
  466. yAxisMax: 0,
  467. scatterXAxisMin: 0,
  468. scatterXAxisMax: 0,
  469. sizeMin: PIVOT_DEFAULT_SCATTER_SIZE,
  470. sizeMax: PIVOT_DEFAULT_SCATTER_SIZE
  471. }
  472. }
  473. const colorConditions =
  474. color &&
  475. (color.items.find((item) => item.config.actOn === metric.name) ||
  476. color.items.find((item) => item.config.actOn === 'all'))
  477. const labelConditions =
  478. label &&
  479. label.items
  480. .filter(
  481. (i) =>
  482. i.type === 'category' &&
  483. !~colAndRows.findIndex((item) => item.name === i.name)
  484. )
  485. .filter(
  486. (i) => i.config.actOn === metric.name || i.config.actOn === 'all'
  487. )
  488. const actingConditions = []
  489. .concat(colorConditions)
  490. .concat(labelConditions)
  491. .filter((i) => !!i)
  492. const scatterXAxisItem = xAxis && xAxis.items[0]
  493. const sizeItem =
  494. size &&
  495. (size.items.find((i) => i.config.actOn === metric.name) ||
  496. size.items.find((i) => i.config.actOn === 'all'))
  497. const decodedMetricName = decodeMetricName(metric.name)
  498. const decodedScatterXAxisItemName =
  499. scatterXAxisItem && decodeMetricName(scatterXAxisItem.name)
  500. const decodedSizeItemName = sizeItem && decodeMetricName(sizeItem.name)
  501. if (actingConditions.length && metric.chart.id !== PivotTypes.Bar) {
  502. this.groupedData[metric.name] = records.reduce(
  503. (
  504. {
  505. yAxisMin,
  506. yAxisMax,
  507. scatterXAxisMin,
  508. scatterXAxisMax,
  509. sizeMin,
  510. sizeMax
  511. },
  512. recordCollection
  513. ) => {
  514. const groupedRecordCollection = {}
  515. recordCollection.forEach((record) => {
  516. const groupKey = actingConditions
  517. .map((con) => record[con.name])
  518. .join(',')
  519. if (!groupedRecordCollection[groupKey]) {
  520. groupedRecordCollection[groupKey] = []
  521. }
  522. groupedRecordCollection[groupKey].push(record)
  523. })
  524. const groupedSum = Object.values(groupedRecordCollection).map(
  525. (collection: any[]) =>
  526. collection.reduce(
  527. (sum, record) => {
  528. return {
  529. yAxis:
  530. sum.yAxis +
  531. (Number(
  532. record[`${metric.agg}(${decodedMetricName})`]
  533. ) || 0),
  534. scatterXAxis: scatterXAxisItem
  535. ? sum.scatterXAxis +
  536. (Number(
  537. record[
  538. `${scatterXAxisItem.agg}(${decodedScatterXAxisItemName})`
  539. ]
  540. ) || 0)
  541. : 0,
  542. size: sizeItem
  543. ? sum.size +
  544. (Number(
  545. record[`${sizeItem.agg}(${decodedSizeItemName})`]
  546. ) || 0)
  547. : 0
  548. }
  549. },
  550. { yAxis: 0, scatterXAxis: 0, size: 0 }
  551. )
  552. )
  553. const groupedYAxis = groupedSum.map((gs) => gs.yAxis)
  554. const groupedScatterXAxis = groupedSum.map((gs) => gs.scatterXAxis)
  555. const groupedSize = groupedSum.map((gs) => gs.size)
  556. return {
  557. yAxisMin: Math.min(yAxisMin, ...groupedYAxis, 0),
  558. yAxisMax: Math.max(yAxisMax, ...groupedYAxis),
  559. scatterXAxisMin: Math.min(
  560. scatterXAxisMin,
  561. ...groupedScatterXAxis,
  562. 0
  563. ),
  564. scatterXAxisMax: Math.max(
  565. scatterXAxisMax,
  566. ...groupedScatterXAxis
  567. ),
  568. sizeMin: Math.min(sizeMin, ...groupedSize),
  569. sizeMax: Math.max(sizeMax, ...groupedSize)
  570. }
  571. },
  572. {
  573. yAxisMin: 0,
  574. yAxisMax: 0,
  575. scatterXAxisMin: 0,
  576. scatterXAxisMax: 0,
  577. sizeMin: PIVOT_DEFAULT_SCATTER_SIZE,
  578. sizeMax: PIVOT_DEFAULT_SCATTER_SIZE
  579. }
  580. )
  581. } else {
  582. this.groupedData[metric.name] = records.reduce(
  583. (
  584. {
  585. yAxisMin,
  586. yAxisMax,
  587. scatterXAxisMin,
  588. scatterXAxisMax,
  589. sizeMin,
  590. sizeMax
  591. },
  592. recordCollection
  593. ) => {
  594. const sum = recordCollection.reduce(
  595. (s, record) => {
  596. return {
  597. yAxis:
  598. s.yAxis +
  599. (Number(record[`${metric.agg}(${decodedMetricName})`]) ||
  600. 0),
  601. scatterXAxis: scatterXAxisItem
  602. ? s.scatterXAxis +
  603. (Number(
  604. record[
  605. `${scatterXAxisItem.agg}(${decodedScatterXAxisItemName})`
  606. ]
  607. ) || 0)
  608. : 0,
  609. size: sizeItem
  610. ? s.size +
  611. (Number(
  612. record[`${sizeItem.agg}(${decodedSizeItemName})`]
  613. ) || 0)
  614. : 0
  615. }
  616. },
  617. { yAxis: 0, scatterXAxis: 0, size: 0 }
  618. )
  619. return {
  620. yAxisMin: Math.min(yAxisMin, sum.yAxis, 0),
  621. yAxisMax: Math.max(yAxisMax, sum.yAxis),
  622. scatterXAxisMin: Math.min(scatterXAxisMin, sum.scatterXAxis, 0),
  623. scatterXAxisMax: Math.max(scatterXAxisMax, sum.scatterXAxis),
  624. sizeMin: Math.min(sizeMin, sum.size),
  625. sizeMax: Math.max(sizeMax, sum.size)
  626. }
  627. },
  628. {
  629. yAxisMin: 0,
  630. yAxisMax: 0,
  631. scatterXAxisMin: 0,
  632. scatterXAxisMax: 0,
  633. sizeMin: PIVOT_DEFAULT_SCATTER_SIZE,
  634. sizeMax: PIVOT_DEFAULT_SCATTER_SIZE
  635. }
  636. )
  637. }
  638. })
  639. }
  640. private legendSelect = (name, key) => {
  641. const { legendSelected } = this.state
  642. if (!legendSelected[name]) {
  643. legendSelected[name] = []
  644. }
  645. legendSelected[name] = legendSelected[name].includes(key)
  646. ? legendSelected[name].filter((ls) => ls !== key)
  647. : legendSelected[name].concat(key)
  648. this.setState({
  649. legendSelected: { ...legendSelected },
  650. renderType: 'clear'
  651. })
  652. }
  653. private getLegendWidth = (props) => {
  654. const { color } = props
  655. if (color) {
  656. return (
  657. color.items.reduce(
  658. (max, item) =>
  659. Math.max(
  660. max,
  661. getTextWidth(item.name),
  662. Object.keys(item.config.values).reduce(
  663. (keyMax, key) => Math.max(keyMax, getTextWidth(key)),
  664. 0
  665. ) + PIVOT_LEGEND_ITEM_PADDING
  666. ),
  667. 0
  668. ) + PIVOT_LEGEND_PADDING
  669. )
  670. }
  671. return 0
  672. }
  673. private ifSelectedTdToDrill = (obj) => {
  674. const {
  675. getDataDrillDetail,
  676. onCheckTableInteract,
  677. onDoInteract
  678. } = this.props
  679. const isInteractiveChart = onCheckTableInteract && onCheckTableInteract()
  680. const { cellSelected } = this.state
  681. let assignObj = {}
  682. const values = Object.values(obj.data)
  683. if (values[0]) {
  684. const isMultiObj =
  685. isInteractiveChart && onDoInteract ? null : cellSelected
  686. assignObj = {
  687. ...isMultiObj,
  688. ...obj.data
  689. }
  690. } else {
  691. const key = Object.keys(obj.data)[0]
  692. assignObj = {
  693. ...cellSelected
  694. }
  695. delete assignObj[key]
  696. }
  697. this.setState(
  698. {
  699. cellSelected: assignObj
  700. },
  701. () => {
  702. const { cellSelected } = this.state
  703. const range = obj.range
  704. const brushed = [{ 0: Object.values(cellSelected) }]
  705. const sourceData = Object.values(cellSelected)
  706. if (isInteractiveChart && onDoInteract) {
  707. const triggerData = sourceData
  708. onDoInteract(triggerData)
  709. }
  710. console.log(obj)
  711. console.log(sourceData)
  712. setTimeout(() => {
  713. getDataDrillDetail(JSON.stringify({ range, brushed, sourceData }))
  714. }, 500)
  715. }
  716. )
  717. }
  718. public render() {
  719. const {
  720. cols,
  721. rows,
  722. metrics,
  723. chartStyles,
  724. color,
  725. label,
  726. size,
  727. xAxis,
  728. tip,
  729. dimetionAxis,
  730. onCheckTableInteract,
  731. onDoInteract,
  732. getDataDrillDetail,
  733. isDrilling
  734. } = this.props
  735. const { legendSelected, renderType } = this.state
  736. const rowNames = rows.map((r) => r.name)
  737. const colNames = cols.map((c) => c.name)
  738. const hasMetricNameDimension = [rows, cols].some(
  739. (items) => items.findIndex((item) => item.name === '指标名称') >= 0
  740. )
  741. return (
  742. <div className={styles.block}>
  743. <div className={styles.leftSide}>
  744. <Corner
  745. cols={colNames}
  746. rows={rowNames}
  747. rowWidths={this.rowHeaderWidths}
  748. chartStyles={chartStyles}
  749. dimetionAxis={dimetionAxis}
  750. />
  751. <div className={styles.rowHeader}>
  752. <RowTitle
  753. rows={rowNames}
  754. rowKeys={this.rowKeys}
  755. chartStyles={chartStyles}
  756. drawingData={this.drawingData}
  757. dimetionAxis={dimetionAxis}
  758. />
  759. <RowHeader
  760. rows={rows}
  761. rowKeys={this.rowKeys}
  762. colKeys={this.colKeys}
  763. rowWidths={this.rowHeaderWidths}
  764. rowTree={this.rowTree}
  765. colTree={this.colTree}
  766. tree={this.tree}
  767. metrics={metrics}
  768. chartStyles={chartStyles}
  769. drawingData={this.drawingData}
  770. dimetionAxis={dimetionAxis}
  771. metricAxisConfig={this.metricAxisConfig}
  772. hasMetricNameDimetion={hasMetricNameDimension}
  773. ref={(f) => (this.rowHeader = findDOMNode(f) as HTMLElement)}
  774. />
  775. </div>
  776. <RowFooter />
  777. </div>
  778. <div className={styles.rightSide}>
  779. <ColumnTitle
  780. cols={colNames}
  781. colKeys={this.colKeys}
  782. colTree={this.colTree}
  783. chartStyles={chartStyles}
  784. drawingData={this.drawingData}
  785. dimetionAxis={dimetionAxis}
  786. />
  787. <ColumnHeader
  788. cols={colNames}
  789. colKeys={this.colKeys}
  790. colTree={this.colTree}
  791. metrics={metrics}
  792. chartStyles={chartStyles}
  793. drawingData={this.drawingData}
  794. dimetionAxis={dimetionAxis}
  795. ref={(f) => (this.columnHeader = findDOMNode(f) as HTMLElement)}
  796. />
  797. <TableBody
  798. cols={colNames}
  799. rows={rowNames}
  800. rowKeys={this.rowKeys}
  801. colKeys={this.colKeys}
  802. rowWidths={this.rowHeaderWidths}
  803. rowTree={this.rowTree}
  804. colTree={this.colTree}
  805. tree={this.tree}
  806. metrics={metrics}
  807. metricAxisConfig={this.metricAxisConfig}
  808. chartStyles={chartStyles}
  809. drawingData={this.drawingData}
  810. dimetionAxis={dimetionAxis}
  811. color={color}
  812. label={label}
  813. size={size}
  814. xAxis={xAxis}
  815. tip={tip}
  816. interacting={this.props.interacting}
  817. renderType={renderType}
  818. legend={legendSelected}
  819. onCheckTableInteract={onCheckTableInteract}
  820. onDoInteract={onDoInteract}
  821. getDataDrillDetail={getDataDrillDetail}
  822. isDrilling={isDrilling}
  823. whichDataDrillBrushed={this.props.whichDataDrillBrushed}
  824. ifSelectedTdToDrill={this.ifSelectedTdToDrill}
  825. onSelectChartsItems={this.props.onSelectChartsItems}
  826. selectedItems={this.props.selectedItems}
  827. selectedChart={this.props.selectedChart}
  828. // onHideDrillPanel={onHideDrillPanel}
  829. ref={(f) => (this.tableBody = findDOMNode(f) as HTMLElement)}
  830. />
  831. <ColumnFooter
  832. rowKeys={this.rowKeys}
  833. colKeys={this.colKeys}
  834. rowTree={this.rowTree}
  835. colTree={this.colTree}
  836. tree={this.tree}
  837. metrics={metrics}
  838. metricAxisConfig={this.metricAxisConfig}
  839. chartStyles={chartStyles}
  840. drawingData={this.drawingData}
  841. dimetionAxis={dimetionAxis}
  842. ref={(f) => (this.columnFooter = findDOMNode(f) as HTMLElement)}
  843. />
  844. </div>
  845. <Legend
  846. color={color}
  847. chartStyles={chartStyles}
  848. onLegendSelect={this.legendSelect}
  849. />
  850. </div>
  851. )
  852. }
  853. }
  854. export default Pivot