Chart.tsx 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
  1. import React from 'react'
  2. import * as echarts from 'echarts/lib/echarts'
  3. import { IDrawingData, IMetricAxisConfig, ILegend } from './Pivot'
  4. import { IWidgetMetric, DimetionType, RenderType, IChartStyles } from '../Widget'
  5. import chartOptionGenerator from '../../render/pivot'
  6. import { PIVOT_DEFAULT_SCATTER_SIZE } from 'app/globalConstants'
  7. import { decodeMetricName, getTooltipPosition, getPivotTooltipLabel, getSizeValue, getChartLabel, getTriggeringRecord } from '../util'
  8. import { uuid } from 'utils/util'
  9. import { IDataParamProperty } from '../Workbench/OperatingPanel'
  10. import PivotTypes from '../../config/pivot/PivotTypes'
  11. const styles = require('./Pivot.less')
  12. export interface IChartUnit {
  13. key?: string
  14. width: number
  15. records: any[]
  16. ended: boolean
  17. }
  18. export interface IChartLine {
  19. key?: string
  20. height?: number
  21. data: IChartUnit[]
  22. }
  23. export interface IChartBlock {
  24. key?: string
  25. width?: number
  26. data: IChartLine[]
  27. pieces: number
  28. }
  29. export interface IChartChunk {
  30. key?: string
  31. height?: number
  32. data: IChartBlock[]
  33. }
  34. interface IChartProps {
  35. width: number
  36. height: number
  37. cols: string[]
  38. rows: string[]
  39. dimetionAxisCount: number
  40. metricAxisCount: number
  41. metrics: IWidgetMetric[]
  42. data: IChartChunk[]
  43. chartStyles: IChartStyles
  44. drawingData: IDrawingData
  45. dimetionAxis: DimetionType
  46. metricAxisConfig?: IMetricAxisConfig
  47. color?: IDataParamProperty
  48. label?: IDataParamProperty
  49. size?: IDataParamProperty
  50. xAxis?: IDataParamProperty
  51. tip?: IDataParamProperty
  52. renderType: RenderType
  53. legend: ILegend
  54. onCheckTableInteract?: () => boolean
  55. onDoInteract?: (triggerData: any) => void
  56. getDataDrillDetail?: (position: string) => void
  57. isDrilling?: boolean
  58. whichDataDrillBrushed?: boolean | object []
  59. selectedChart: number
  60. selectedItems?: string[] | number[]
  61. onSelectChartsItems?: (selectedItems: number[]) => void
  62. // onHideDrillPanel?: (swtich: boolean) => void
  63. }
  64. interface IChartStates {
  65. renderSign: string
  66. }
  67. export class Chart extends React.Component<IChartProps, IChartStates> {
  68. constructor (props) {
  69. super(props)
  70. this.state = {
  71. renderSign: ''
  72. }
  73. }
  74. private containers: { [key: string]: HTMLDivElement } = {}
  75. public componentDidMount () {
  76. this.renderChart()
  77. }
  78. public componentDidUpdate () {
  79. this.renderChart()
  80. }
  81. public componentWillReceiveProps (nextProps) {
  82. if (nextProps.renderType === 'rerender') {
  83. this.setState({ renderSign: uuid(8, 16) })
  84. }
  85. }
  86. public componentWillUnmount () {
  87. // dispose chart instances
  88. }
  89. private getChartPieceData = (data, pieces) => {
  90. const dataLength = data.length
  91. return data.reduce((arr, d, i) => {
  92. let renderLine
  93. if (i % (Math.ceil(dataLength / pieces)) === 0) {
  94. renderLine = []
  95. arr.push(renderLine)
  96. } else {
  97. renderLine = arr[arr.length - 1]
  98. }
  99. renderLine.push(d)
  100. return arr
  101. }, [])
  102. }
  103. private getXaxisOption = (index, type, data?) => {
  104. const {
  105. showVerticalLine = false,
  106. verticalLineStyle = '',
  107. verticalLineSize = '',
  108. verticalLineColor = ''
  109. } = this.props.chartStyles.splitLine || {}
  110. return {
  111. gridIndex: index,
  112. type,
  113. axisTick: { show: false },
  114. axisLabel: { show: false },
  115. ...type === 'value' && {
  116. axisLine: { show: false }
  117. },
  118. ...type === 'category' && {
  119. axisLine: {
  120. lineStyle: {
  121. color: verticalLineColor
  122. }
  123. },
  124. data
  125. },
  126. splitLine: {
  127. show: showVerticalLine,
  128. interval: 0,
  129. lineStyle: {
  130. color: verticalLineColor,
  131. width: verticalLineSize,
  132. type: verticalLineStyle
  133. }
  134. }
  135. }
  136. }
  137. private getYaxisOption = (index, metricAxisConfig, coordinate, isScatterXAxis?) => {
  138. const {
  139. showHorizontalLine = false,
  140. horizontalLineStyle = '',
  141. horizontalLineSize = '',
  142. horizontalLineColor = '',
  143. showVerticalLine = false,
  144. verticalLineStyle = '',
  145. verticalLineSize = '',
  146. verticalLineColor = ''
  147. } = this.props.chartStyles.splitLine || {}
  148. return {
  149. gridIndex: index,
  150. type: 'value',
  151. axisLine: {
  152. show: showHorizontalLine,
  153. lineStyle: {
  154. color: horizontalLineColor,
  155. width: horizontalLineSize,
  156. type: horizontalLineStyle
  157. }
  158. },
  159. axisTick: { show: false },
  160. axisLabel: { show: false },
  161. splitLine: {
  162. show: coordinate === 'cartesian' && (isScatterXAxis ? showVerticalLine : showHorizontalLine),
  163. lineStyle: {
  164. color: isScatterXAxis ? verticalLineColor : horizontalLineColor,
  165. width: isScatterXAxis ? verticalLineSize : horizontalLineSize,
  166. type: isScatterXAxis ? verticalLineStyle : horizontalLineStyle
  167. }
  168. },
  169. ...metricAxisConfig
  170. }
  171. }
  172. private renderChart = () => {
  173. const {
  174. cols,
  175. rows,
  176. metrics,
  177. data,
  178. drawingData,
  179. metricAxisConfig,
  180. dimetionAxis,
  181. color,
  182. label,
  183. size,
  184. xAxis: scatterXAxis,
  185. tip,
  186. legend,
  187. renderType,
  188. selectedItems
  189. } = this.props
  190. const { elementSize, unitMetricWidth, unitMetricHeight } = drawingData
  191. data.forEach((chunk: IChartChunk) => {
  192. chunk.data.forEach((block: IChartBlock) => {
  193. const chartPieces = this.containers[`${chunk.key}${block.key}`].children as HTMLCollectionOf<HTMLDivElement>
  194. const dataPieces = this.getChartPieceData(block.data, block.pieces)
  195. const containerWidth = block.width
  196. dataPieces.forEach((dp, i) => {
  197. const chartPiece = chartPieces[i]
  198. const containerHeight = dp.reduce((sum, line) => {
  199. const lineHeight = line.height * (dimetionAxis === 'col' ? metrics.length : 1)
  200. return sum + lineHeight
  201. }, 0)
  202. chartPiece.style.height = `${containerHeight}px`
  203. let instance = echarts.getInstanceByDom(chartPiece)
  204. if (!instance) {
  205. instance = echarts.init(chartPiece, 'default')
  206. } else {
  207. if (renderType === 'clear') {
  208. instance.clear()
  209. }
  210. }
  211. const grid = []
  212. const xAxis = []
  213. const yAxis = []
  214. const series = []
  215. const seriesData = []
  216. let xSum = 0
  217. let ySum = 0
  218. let index = 0
  219. const verticalRecordCountOfRow = dp.reduce((sum, line) => sum + line.data[0] ? line.data[0].records.length : 0, 0)
  220. dp.forEach((line: IChartLine, j) => {
  221. const { height, data: lineData } = line
  222. const horizontalRecordCountOfCol = lineData.reduce((sum, unit) => sum + unit.records.length, 0)
  223. let lineRecordSum = 0
  224. lineData.forEach((unit: IChartUnit, k) => {
  225. const { width, records } = unit
  226. metrics.forEach((m, l) => {
  227. const decodedMetricName = decodeMetricName(m.name)
  228. const xAxisData = records.map((r) => r.key)
  229. const {
  230. chartOption,
  231. stackOption,
  232. calcPieCenterAndRadius,
  233. getSymbolSize
  234. } = chartOptionGenerator(m.chart.name, drawingData)
  235. const currentColorItem = color.items.find((i) => i.config.actOn === m.name) || color.items.find((i) => i.config.actOn === 'all')
  236. const currentLabelItem = label && (label.items.find((i) => i.config.actOn === m.name) || label.items.find((i) => i.config.actOn === 'all'))
  237. const currentScatterXAxisItem = scatterXAxis && scatterXAxis.items[0]
  238. const currentSizeItem = size && (size.items.find((i) => i.config.actOn === m.name) || size.items.find((i) => i.config.actOn === 'all'))
  239. const currentSizeValue = size && (currentSizeItem ? getSizeValue(size.value[currentSizeItem.name] || size.value['all']) : getSizeValue(size.value['all']))
  240. const groupingItems = [].concat(currentColorItem)
  241. .concat(currentLabelItem && currentLabelItem.type === 'category' && currentLabelItem)
  242. .filter((i) => !!i)
  243. if (!(currentScatterXAxisItem && m.chart.id === PivotTypes.Scatter)) {
  244. grid.push({
  245. top: dimetionAxis === 'col' ? (xSum + l * height) : ySum,
  246. left: dimetionAxis === 'col' ? ySum - 1 : (xSum - 1 + l * width), // 隐藏yaxisline
  247. width,
  248. height
  249. })
  250. xAxis.push(this.getXaxisOption(index, 'category', xAxisData))
  251. yAxis.push(this.getYaxisOption(index, metricAxisConfig[m.name].yAxis, m.chart.coordinate))
  252. }
  253. if (m.chart.coordinate === 'cartesian') {
  254. if (groupingItems.length) {
  255. const grouped = {}
  256. records.forEach((recordCollection) => {
  257. const { key: colKey, value: valueCollection } = recordCollection
  258. if (valueCollection) {
  259. valueCollection.forEach((record) => {
  260. const groupingKey = groupingItems.map((item) => record[item.name]).join(',')
  261. if (currentColorItem) {
  262. const legendSelectedItem = legend[currentColorItem.name]
  263. if (legendSelectedItem && legendSelectedItem.includes(record[currentColorItem.name])) {
  264. return false
  265. }
  266. }
  267. if (!grouped[groupingKey]) {
  268. grouped[groupingKey] = {}
  269. }
  270. if (!grouped[groupingKey][colKey]) {
  271. grouped[groupingKey][colKey] = []
  272. }
  273. grouped[groupingKey][colKey].push(record)
  274. })
  275. }
  276. })
  277. if (currentScatterXAxisItem && m.chart.id === PivotTypes.Scatter) {
  278. let tempXsum = xSum
  279. let tempYsum = ySum
  280. xAxisData.forEach((colKey, xdIndex) => {
  281. if (dimetionAxis === 'col') {
  282. grid.push({
  283. top: tempXsum + l * unitMetricHeight,
  284. left: tempYsum - 1, // 隐藏yaxisline
  285. width: elementSize,
  286. height: unitMetricHeight
  287. })
  288. } else {
  289. grid.push({
  290. top: tempYsum,
  291. left: tempXsum - 1 + l * unitMetricWidth, // 隐藏yaxisline
  292. width: unitMetricWidth,
  293. height: elementSize
  294. })
  295. }
  296. xAxis.push(this.getYaxisOption(index, metricAxisConfig[m.name].scatterXAxis, m.chart.coordinate, true))
  297. yAxis.push(this.getYaxisOption(index, metricAxisConfig[m.name].yAxis, m.chart.coordinate))
  298. Object.entries((grouped)).sort().forEach(([groupingKey, groupedRecords]: [string, any[]]) => {
  299. let data
  300. if (groupedRecords[colKey]) {
  301. data = groupedRecords[colKey].reduce((sum, record) => {
  302. return [
  303. sum[0] + (Number(record[`${currentScatterXAxisItem.agg}(${decodeMetricName(currentScatterXAxisItem.name)})`]) || 0),
  304. sum[1] + (Number(record[`${m.agg}(${decodedMetricName})`]) || 0),
  305. currentSizeItem ? sum[2] + (Number(record[`${currentSizeItem.agg}(${decodeMetricName(currentSizeItem.name)})`]) || 0) : PIVOT_DEFAULT_SCATTER_SIZE
  306. ]
  307. }, [0, 0, 0])
  308. data = [{
  309. value: [data[0], data[1]],
  310. symbolSize: currentSizeItem
  311. ? getSymbolSize(m.name, data[2]) * currentSizeValue
  312. : PIVOT_DEFAULT_SCATTER_SIZE * currentSizeValue
  313. }]
  314. } else {
  315. data = [[0, 0, 0]]
  316. }
  317. series.push({
  318. data,
  319. color: currentColorItem
  320. ? currentColorItem.config.values[groupingKey.split(',')[0]]
  321. : (color.value[m.name] || color.value['all']),
  322. ...currentLabelItem && {
  323. label: {
  324. show: true,
  325. position: 'top',
  326. formatter: getChartLabel(seriesData, currentLabelItem)
  327. }
  328. },
  329. xAxisIndex: index,
  330. yAxisIndex: index,
  331. ...chartOption
  332. })
  333. seriesData.push({
  334. type: 'scatter',
  335. grouped: true,
  336. records: groupedRecords[colKey]
  337. })
  338. })
  339. if (dimetionAxis === 'col') {
  340. tempYsum += elementSize
  341. } else {
  342. tempXsum += elementSize * metrics.length
  343. }
  344. if (xdIndex !== xAxisData.length - 1) {
  345. index += 1
  346. }
  347. })
  348. } else {
  349. Object.entries((grouped)).sort().forEach(([groupingKey, groupedRecords]: [string, any[]]) => {
  350. const data = []
  351. const backupData = []
  352. xAxisData.forEach((colKey) => {
  353. if (m.chart.id === PivotTypes.Scatter) {
  354. const result = groupedRecords[colKey]
  355. ? groupedRecords[colKey].reduce(([value, size], record) => [
  356. value + (Number(record[`${m.agg}(${decodedMetricName})`]) || 0),
  357. currentSizeItem ? size + (Number(record[`${currentSizeItem.agg}(${decodeMetricName(currentSizeItem.name)})`]) || 0) : 0
  358. ], [0, 0])
  359. : [0, 0]
  360. data.push({
  361. value: result[0],
  362. symbolSize: currentSizeItem
  363. ? getSymbolSize(m.name, result[1]) * currentSizeValue
  364. : PIVOT_DEFAULT_SCATTER_SIZE * currentSizeValue
  365. })
  366. } else {
  367. if (groupedRecords[colKey]) {
  368. data.push(groupedRecords[colKey].reduce((sum, record) => sum + (Number(record[`${m.agg}(${decodedMetricName})`]) || 0), 0))
  369. } else {
  370. data.push(0)
  371. }
  372. }
  373. backupData.push(groupedRecords[colKey])
  374. })
  375. series.push({
  376. ...stackOption && {stack: `${unit.key}${m.name}`},
  377. data,
  378. color: currentColorItem
  379. ? currentColorItem.config.values[groupingKey.split(',')[0]]
  380. : (color.value[m.name] || color.value['all']),
  381. ...currentLabelItem && {
  382. label: {
  383. show: true,
  384. position: m.chart.id === PivotTypes.Bar ? 'inside' : 'top',
  385. formatter: getChartLabel(seriesData, currentLabelItem)
  386. }
  387. },
  388. xAxisIndex: index,
  389. yAxisIndex: index,
  390. ...chartOption
  391. })
  392. seriesData.push({
  393. type: 'cartesian',
  394. grouped: true,
  395. records: backupData
  396. })
  397. })
  398. }
  399. } else {
  400. if (currentScatterXAxisItem && m.chart.id === PivotTypes.Scatter) {
  401. let tempXsum = xSum
  402. let tempYsum = ySum
  403. records.forEach((recordCollection, rcIndex) => {
  404. if (dimetionAxis === 'col') {
  405. grid.push({
  406. top: tempXsum + l * unitMetricHeight,
  407. left: tempYsum - 1, // 隐藏yaxisline
  408. width: elementSize,
  409. height: unitMetricHeight
  410. })
  411. } else {
  412. grid.push({
  413. top: tempYsum,
  414. left: tempXsum - 1 + l * unitMetricWidth, // 隐藏yaxisline
  415. width: unitMetricWidth,
  416. height: elementSize
  417. })
  418. }
  419. xAxis.push(this.getYaxisOption(index, metricAxisConfig[m.name].scatterXAxis, m.chart.coordinate, true))
  420. yAxis.push(this.getYaxisOption(index, metricAxisConfig[m.name].yAxis, m.chart.coordinate))
  421. let data
  422. if (recordCollection.value) {
  423. data = recordCollection.value.reduce((sum, record) => [
  424. sum[0] + (Number(record[`${currentScatterXAxisItem.agg}(${decodeMetricName(currentScatterXAxisItem.name)})`]) || 0),
  425. sum[1] + (Number(record[`${m.agg}(${decodedMetricName})`]) || 0),
  426. currentSizeItem ? sum[2] + (Number(record[`${currentSizeItem.agg}(${decodeMetricName(currentSizeItem.name)})`]) || 0) : PIVOT_DEFAULT_SCATTER_SIZE
  427. ], [0, 0, 0])
  428. data = [{
  429. value: [data[0], data[1]],
  430. symbolSize: currentSizeItem
  431. ? getSymbolSize(m.name, data[2]) * currentSizeValue
  432. : PIVOT_DEFAULT_SCATTER_SIZE * currentSizeValue
  433. }]
  434. } else {
  435. data = [[0, 0, 0]]
  436. }
  437. series.push({
  438. data,
  439. color: color.value[m.name] || color.value['all'],
  440. ...currentLabelItem && {
  441. label: {
  442. show: true,
  443. position: 'top',
  444. formatter: getChartLabel(seriesData, currentLabelItem)
  445. }
  446. },
  447. xAxisIndex: index,
  448. yAxisIndex: index,
  449. ...chartOption
  450. })
  451. seriesData.push({
  452. type: 'scatter',
  453. grouped: false,
  454. records: recordCollection.value
  455. })
  456. if (dimetionAxis === 'col') {
  457. tempYsum += elementSize
  458. } else {
  459. tempXsum += elementSize * metrics.length
  460. }
  461. if (rcIndex !== records.length - 1) {
  462. index += 1
  463. }
  464. })
  465. } else {
  466. series.push({
  467. data: records.map((recordCollection, i) => {
  468. if (m.chart.id === PivotTypes.Scatter) {
  469. const result = recordCollection.value
  470. ? recordCollection.value.reduce(([value, size], record) => [
  471. value + (Number(record[`${m.agg}(${decodedMetricName})`]) || 0),
  472. currentSizeItem ? size + (Number(record[`${currentSizeItem.agg}(${decodeMetricName(currentSizeItem.name)})`]) || 0) : 0
  473. ], [0, 0])
  474. : [0, 0]
  475. const itemStyle = selectedItems && selectedItems.length &&
  476. selectedItems.some((item) => Number(item.split('&')[0]) === i && Number(item.split('&')[1]) === index)
  477. ? {itemStyle: {normal: {opacity: 1, borderWidth: 6}}} : null
  478. return {
  479. value: result[0],
  480. ...itemStyle,
  481. symbolSize: currentSizeItem
  482. ? getSymbolSize(m.name, result[1]) * currentSizeValue
  483. : PIVOT_DEFAULT_SCATTER_SIZE * currentSizeValue
  484. }
  485. } else {
  486. // bar line scatter
  487. const itemStyle = selectedItems && selectedItems.length &&
  488. selectedItems.some((item) => Number(item.split('&')[0]) === i && Number(item.split('&')[1]) === index)
  489. ? {itemStyle: {normal: {opacity: 1, borderWidth: 6}}} : null
  490. return recordCollection.value
  491. ? {
  492. value: recordCollection.value.reduce((sum, record) => sum + (Number(record[`${m.agg}(${decodedMetricName})`]) || 0), 0),
  493. ...itemStyle
  494. }
  495. : {
  496. value: 0,
  497. ...itemStyle
  498. }
  499. }
  500. }),
  501. itemStyle: {
  502. normal: {
  503. opacity: selectedItems && selectedItems.length > 0 ? 0.25 : 1
  504. }
  505. },
  506. color: color.value[m.name] || color.value['all'],
  507. ...currentLabelItem && {
  508. label: {
  509. show: true,
  510. position: 'top',
  511. formatter: getChartLabel(seriesData, currentLabelItem)
  512. }
  513. },
  514. xAxisIndex: index,
  515. yAxisIndex: index,
  516. ...chartOption
  517. })
  518. seriesData.push({
  519. type: 'cartesian',
  520. grouped: false,
  521. records
  522. })
  523. }
  524. }
  525. } else {
  526. records.forEach((recordCollection, r) => {
  527. const centerAndRadius = calcPieCenterAndRadius(
  528. dimetionAxis,
  529. containerWidth,
  530. containerHeight,
  531. elementSize,
  532. [unitMetricHeight, unitMetricWidth],
  533. horizontalRecordCountOfCol,
  534. verticalRecordCountOfRow,
  535. lineRecordSum,
  536. dp.length,
  537. lineData.length,
  538. metrics.length,
  539. records.length,
  540. j,
  541. k,
  542. l,
  543. r
  544. )
  545. let data = []
  546. if (groupingItems.length) {
  547. if (recordCollection.value) {
  548. const legendSelectedItem = currentColorItem && legend[currentColorItem.name]
  549. recordCollection.value.forEach((record) => {
  550. if (legendSelectedItem && legendSelectedItem.includes(record[currentColorItem.name])) {
  551. return false
  552. }
  553. data.push({
  554. name: recordCollection.key,
  555. value: record[`${m.agg}(${decodedMetricName})`],
  556. itemStyle: {
  557. color: currentColorItem
  558. ? currentColorItem.config.values[record[currentColorItem.name]]
  559. : (color.value[m.name] || color.value['all'])
  560. }
  561. })
  562. })
  563. }
  564. } else {
  565. data = [{
  566. name: recordCollection.key,
  567. value: recordCollection.value
  568. ? recordCollection.value.reduce((sum, record) => sum + (Number(record[`${m.agg}(${decodedMetricName})`]) || 0), 0)
  569. : 0,
  570. itemStyle: {
  571. color: color.value[m.name] || color.value['all']
  572. }
  573. }]
  574. }
  575. series.push({
  576. data,
  577. ...currentLabelItem
  578. ? {
  579. label: {
  580. show: true,
  581. formatter: getChartLabel(seriesData, currentLabelItem)
  582. }
  583. }
  584. : {
  585. label: {
  586. show: false
  587. }
  588. },
  589. ...centerAndRadius,
  590. ...chartOption
  591. })
  592. seriesData.push({
  593. type: 'polar',
  594. grouped: !!groupingItems.length,
  595. records: recordCollection.value
  596. })
  597. })
  598. }
  599. index += 1
  600. })
  601. lineRecordSum += records.length
  602. if (dimetionAxis === 'col') {
  603. ySum += width
  604. } else {
  605. xSum += width * metrics.length
  606. }
  607. })
  608. if (dimetionAxis === 'col') {
  609. xSum += height * metrics.length
  610. ySum = 0
  611. } else {
  612. ySum += height
  613. xSum = 0
  614. }
  615. })
  616. const { isDrilling, whichDataDrillBrushed, getDataDrillDetail } = this.props
  617. const brushedOptions = isDrilling === true ? {
  618. // brush: {
  619. // toolbox: ['rect', 'polygon', 'keep', 'clear'],
  620. // // toolbox: ['rect', 'polygon', 'lineX', 'lineY', 'keep', 'clear'],
  621. // throttleType: 'debounce',
  622. // throttleDelay: 300,
  623. // brushStyle: {
  624. // borderWidth: 1,
  625. // color: 'rgba(255,255,255,0.2)',
  626. // borderColor: 'rgba(120,140,180,0.6)'
  627. // }
  628. // }
  629. } : null
  630. instance.setOption({
  631. tooltip: {
  632. position: getTooltipPosition,
  633. formatter: getPivotTooltipLabel(seriesData, cols, rows, metrics, color, label, size, scatterXAxis, tip)
  634. },
  635. // ...brushedOptions,
  636. grid,
  637. xAxis,
  638. yAxis,
  639. series
  640. })
  641. const { onDoInteract, onCheckTableInteract } = this.props
  642. if (onDoInteract) {
  643. instance.off('click')
  644. instance.on('click', (params) => {
  645. this.collectSelectedItems(params, seriesData)
  646. const isInteractiveChart = onCheckTableInteract && onCheckTableInteract()
  647. if (isInteractiveChart) {
  648. const triggerData = getTriggeringRecord(params, seriesData)
  649. onDoInteract(triggerData)
  650. }
  651. })
  652. }
  653. // drill
  654. // instance.off('click')
  655. // instance.on('click', (params) => {
  656. // this.collectSelectedItems(params, seriesData)
  657. // })
  658. // if (isDrilling && whichDataDrillBrushed === false) {
  659. if (whichDataDrillBrushed === false) {
  660. // instance.off('brushselected')
  661. // instance.on('brushselected', brushselected)
  662. // setTimeout(function () {
  663. // instance.dispatchAction({
  664. // type: 'takeGlobalCursor',
  665. // key: 'brush',
  666. // brushOption: {
  667. // brushType: 'rect',
  668. // brushMode: 'multiple'
  669. // }
  670. // })
  671. // }, 0)
  672. }
  673. function brushselected (params) {
  674. const brushComponent = params.batch[0]
  675. const brushed = []
  676. let sourceData = []
  677. let range: any[] = []
  678. if (brushComponent.areas && brushComponent.areas.length) {
  679. brushComponent.areas.forEach((area) => {
  680. range = range.concat(area.range)
  681. })
  682. }
  683. if (brushComponent.selected && brushComponent.selected.length) {
  684. for (let i = 0; i < brushComponent.selected.length; i++) {
  685. const rawIndices = brushComponent.selected[i].dataIndex
  686. const seriesIndex = brushComponent.selected[i].seriesIndex
  687. brushed.push({[i]: rawIndices})
  688. if (rawIndices && rawIndices.length) {
  689. rawIndices.forEach((raw) => {
  690. const params = {
  691. dataIndex: raw,
  692. seriesIndex
  693. }
  694. sourceData = sourceData.concat(getTriggeringRecord(params, seriesData))
  695. })
  696. }
  697. }
  698. }
  699. if (getDataDrillDetail) {
  700. getDataDrillDetail(JSON.stringify({range, brushed, sourceData}))
  701. }
  702. }
  703. instance.resize()
  704. })
  705. })
  706. })
  707. }
  708. public collectSelectedItems = (params, seriesData) => {
  709. const { data, onSelectChartsItems, selectedChart } = this.props
  710. // 透视驱动下的 selectedChart 均为1
  711. let selectedItems = []
  712. if (this.props.selectedItems && this.props.selectedItems.length) {
  713. selectedItems = [...this.props.selectedItems]
  714. }
  715. const { getDataDrillDetail } = this.props
  716. const dataIndex = params.dataIndex
  717. const seriesIndex = params.seriesIndex
  718. const formatterIndex = `${dataIndex}&${seriesIndex}`
  719. if (selectedItems.length === 0) {
  720. selectedItems.push(formatterIndex)
  721. } else {
  722. const isb = selectedItems.some((item) => item === formatterIndex)
  723. if (isb) {
  724. for (let index = 0, l = selectedItems.length; index < l; index++) {
  725. if (selectedItems[index] === formatterIndex) {
  726. selectedItems.splice(index, 1)
  727. break
  728. }
  729. }
  730. } else {
  731. selectedItems.push(formatterIndex)
  732. }
  733. }
  734. const resultData = selectedItems.map((item) => {
  735. return data[item]
  736. })
  737. const brushed = [{0: Object.values(resultData)}]
  738. setTimeout(() => {
  739. let sourceData = []
  740. selectedItems.forEach((item) => {
  741. const [dataIndex, seriesIndex] = item.split('&')
  742. sourceData = sourceData.concat(getTriggeringRecord({
  743. dataIndex, seriesIndex
  744. }, seriesData))
  745. })
  746. getDataDrillDetail(JSON.stringify({range: null, brushed, sourceData}))
  747. }, 500)
  748. if (onSelectChartsItems) {
  749. onSelectChartsItems(selectedItems)
  750. }
  751. }
  752. public render () {
  753. const { width, height, data } = this.props
  754. const { renderSign } = this.state
  755. const chunks = data.map((chunk, i) => {
  756. const blocks = chunk.data.map((block, j) => {
  757. const pieces = Array.from(Array(block.pieces), (u, k) => (
  758. <div key={`${renderSign}${i}${j}${k}`} />
  759. ))
  760. return (
  761. <div
  762. key={block.key}
  763. className={styles.chartColumn}
  764. style={{width: block.width}}
  765. ref={(f) => this.containers[`${chunk.key}${block.key}`] = f}
  766. >
  767. {pieces}
  768. </div>
  769. )
  770. })
  771. return (
  772. <div
  773. key={chunk.key}
  774. className={styles.chartRow}
  775. style={{height: chunk.height}}
  776. >
  777. {blocks}
  778. </div>
  779. )
  780. })
  781. return (
  782. <div className={styles.chartContainer} style={{width, height}}>
  783. {chunks}
  784. </div>
  785. )
  786. }
  787. }
  788. export default Chart