/* * << * Davinci * == * Copyright (C) 2016 - 2017 EDP * == * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * >> */ import { IChartProps } from '../../components/Chart' import { DEFAULT_SPLITER } from 'app/globalConstants' import { IFieldFormatConfig, getFormattedValue, FieldFormatTypes } from 'containers/Widget/components/Config/Format' import { decodeMetricName, getChartTooltipLabel } from '../../components/util' import { getDimetionAxisOption, getMetricAxisOption, getLabelOption, getLegendOption, getGridPositions, makeGrouped, getGroupedXaxis, getCartesianChartMetrics, getCartesianChartReferenceOptions } from './util' import { getStackName, EmptyStack } from 'containers/Widget/components/Config/Stack' const defaultTheme = require('assets/json/echartsThemes/default.project.json') const defaultThemeColors = defaultTheme.theme.color import { inGroupColorSort } from '../../components/Config/Sort/util' import { FieldSortTypes } from '../../components/Config/Sort' import ChartTypes from '../../config/chart/ChartTypes' export default function (chartProps: IChartProps, drillOptions) { const { data, cols, chartStyles, color, tip, references } = chartProps const { isDrilling, getDataDrillDetail, instance, selectedItems, callback } = drillOptions const metrics = getCartesianChartMetrics(chartProps.metrics) const { bar, label, legend, xAxis, yAxis, splitLine } = chartStyles const { barChart, border: barBorder, gap: barGap, width: barWidth, stack: stackConfig } = bar const { color: borderColor, width: borderWidth, type: borderType, radius: barBorderRadius } = barBorder const { on: turnOnStack, percentage } = stackConfig || EmptyStack const { showVerticalLine, verticalLineColor, verticalLineSize, verticalLineStyle, showHorizontalLine, horizontalLineColor, horizontalLineSize, horizontalLineStyle } = splitLine const labelOption = { label: { ...getLabelOption('bar', label, metrics, false, { formatter: (params) => { const { value, seriesId } = params const m = metrics.find( (m) => m.name === seriesId.split(`${DEFAULT_SPLITER}${DEFAULT_SPLITER}`)[0] ) let format: IFieldFormatConfig = m.format let formattedValue = value if (percentage) { format = { formatType: FieldFormatTypes.Percentage, [FieldFormatTypes.Percentage]: { decimalPlaces: 0 } } formattedValue /= 100 } const formatted = getFormattedValue(formattedValue, format) return formatted } }) } } const referenceOptions = getCartesianChartReferenceOptions( references, ChartTypes.Bar, metrics, data, barChart ) const xAxisColumnName = cols.length ? cols[0].name : '' let xAxisData = [] let grouped = {} let percentGrouped = {} if (color.items.length) { xAxisData = getGroupedXaxis(data, xAxisColumnName, metrics) grouped = makeGrouped( data, color.items.map((c) => c.name), xAxisColumnName, metrics, xAxisData ) const configValue = color.items[0].config.values const configKeys = [] Object.entries(configValue).forEach(([k, v]: [string, string]) => { configKeys.push(k) }) percentGrouped = makeGrouped( data, cols.map((c) => c.name), color.items[0].name, metrics, configKeys ) } else { xAxisData = data.map((d) => d[xAxisColumnName] || '') } const series = [] const seriesData = [] metrics.forEach((m, i) => { const decodedMetricName = decodeMetricName(m.name) const stackOption = turnOnStack ? { stack: getStackName(m.name, stackConfig) } : null if (color.items.length) { const sumArr = [] Object.entries(percentGrouped).forEach(([k, v]: [string, any[]]) => { sumArr.push(getColorDataSum(v, metrics)) }) const groupEntries = Object.entries(grouped) const customColorSort = color.items .filter(({ sort }) => sort && sort.sortType === FieldSortTypes.Custom) .map(({ name, sort }) => ({ name, list: sort[FieldSortTypes.Custom].sortList })) if (customColorSort.length) { inGroupColorSort(groupEntries, customColorSort[0]) } groupEntries.forEach(([k, v]: [string, any[]], gIndex) => { const serieObj = { id: `${m.name}${DEFAULT_SPLITER}${DEFAULT_SPLITER}${k}`, name: `${k}${metrics.length > 1 ? ` ${m.displayName}` : ''}`, type: 'bar', ...stackOption, sampling: 'average', data: v.map((g, index) => { if ( selectedItems && selectedItems.length && selectedItems.some((item) => item === index) ) { return { value: percentage ? (g[`${m.agg}(${decodedMetricName})`] / sumArr[index]) * 100 : g[`${m.agg}(${decodedMetricName})`], itemStyle: { normal: { opacity: 1 } } } } else { if (percentage) { return ( (g[`${m.agg}(${decodedMetricName})`] / sumArr[index]) * 100 ) } else { return g[`${m.agg}(${decodedMetricName})`] } } }), itemStyle: { normal: { opacity: selectedItems && selectedItems.length > 0 ? 0.25 : 1, color: color.items[0].config.values[k] } }, ...labelOption, ...(gIndex === groupEntries.length - 1 && i === metrics.length - 1 && referenceOptions) } series.push(serieObj) seriesData.push(grouped[k]) }) } else { const serieObj = { id: m.name, name: m.displayName, type: 'bar', ...stackOption, sampling: 'average', data: data.map((d, index) => { if ( selectedItems && selectedItems.length && selectedItems.some((item) => item === index) ) { return { value: percentage ? (d[`${m.agg}(${decodedMetricName})`] / getDataSum(data, metrics)[index]) * 100 : d[`${m.agg}(${decodedMetricName})`], itemStyle: { normal: { opacity: 1 } } } } else { if (percentage) { return ( (d[`${m.agg}(${decodedMetricName})`] / getDataSum(data, metrics)[index]) * 100 ) } else { return d[`${m.agg}(${decodedMetricName})`] } } }), itemStyle: { normal: { opacity: selectedItems && selectedItems.length > 0 ? 0.25 : 1, borderColor, borderWidth, borderType, barBorderRadius, color: color.value[m.name] || defaultThemeColors[i % defaultThemeColors.length] } }, barGap: `${barGap}%`, barWidth: barWidth ? `${barWidth}%` : undefined, // lineStyle: { // normal: { // opacity: interactIndex === undefined ? 1 : 0.25 // } // }, // itemStyle: { // normal: { // opacity: interactIndex === undefined ? 1 : 0.25 // color: color.value[m.name] || defaultThemeColors[i] // } // }, ...labelOption, ...(i === metrics.length - 1 && referenceOptions) } series.push(serieObj) seriesData.push([...data]) } }) const seriesNames = series.map((s) => s.name) if (turnOnStack && stackConfig.sum.show) { const { fontFamily, fontStyle, fontColor, fontSize, fontWeight } = stackConfig.sum.font const sumSeries = series.reduce((acc, serie, serieIdx) => { const stackName = serie.stack if (acc[stackName]) { return acc } acc[stackName] = { name: stackName, type: 'bar', stack: stackName, label: { normal: { show: true, color: fontColor, fontStyle, fontWeight, fontFamily, fontSize, position: barChart ? 'right' : 'top', formatter: (params) => { let val = series .filter((s) => s.stack === stackName) .reduce((acc, s) => { const dataIndex = params.dataIndex if (typeof s.data[dataIndex] === 'number') { return acc + s.data[params.dataIndex] } else { const { value } = s.data[dataIndex] return acc + value } }, 0) let format = metrics[serieIdx].format if (percentage) { format = { formatType: FieldFormatTypes.Percentage, [FieldFormatTypes.Percentage]: { decimalPlaces: 0 } } val /= 100 } const formattedValue = getFormattedValue(val, format) return formattedValue } } }, data: Array.from(xAxisData).fill(0) } return acc }, {}) series.push(...Object.values(sumSeries)) } const brushedOptions = isDrilling === true ? { brush: { toolbox: ['rect', 'polygon', 'keep', 'clear'], throttleType: 'debounce', throttleDelay: 300, brushStyle: { borderWidth: 1, color: 'rgba(255,255,255,0.2)', borderColor: 'rgba(120,140,180,0.6)' } } } : null // if (isDrilling) { // // instance.off('brushselected') // instance.on('brushselected', brushselected) // setTimeout(() => { // instance.dispatchAction({ // type: 'takeGlobalCursor', // key: 'brush', // brushOption: { // brushType: 'rect', // brushMode: 'multiple' // } // }) // }, 0) // } if (callback) { callback.call(null, seriesData) } function brushselected(params) { const brushComponent = params.batch[0] const brushed = [] const sourceData = seriesData[0] let range: any[] = [] if (brushComponent && brushComponent.areas && brushComponent.areas.length) { brushComponent.areas.forEach((area) => { range = range.concat(area.range) }) } if ( brushComponent && brushComponent.selected && brushComponent.selected.length ) { for (let i = 0; i < brushComponent.selected.length; i++) { const rawIndices = brushComponent.selected[i].dataIndex const seriesIndex = brushComponent.selected[i].seriesIndex brushed.push({ [i]: rawIndices }) } } if (getDataDrillDetail) { getDataDrillDetail(JSON.stringify({ range, brushed, sourceData })) } } const xAxisSplitLineConfig = { showLine: showVerticalLine, lineColor: verticalLineColor, lineSize: verticalLineSize, lineStyle: verticalLineStyle } const yAxisSplitLineConfig = { showLine: showHorizontalLine, lineColor: horizontalLineColor, lineSize: horizontalLineSize, lineStyle: horizontalLineStyle } const dimetionAxisOption = getDimetionAxisOption( xAxis, xAxisSplitLineConfig, xAxisData ) const metricAxisOption = getMetricAxisOption( yAxis, yAxisSplitLineConfig, metrics.map((m) => decodeMetricName(m.name)).join(` / `), 'x', percentage ) return { xAxis: barChart ? metricAxisOption : dimetionAxisOption, yAxis: barChart ? dimetionAxisOption : metricAxisOption, series, tooltip: { formatter: getChartTooltipLabel('bar', seriesData, { cols, metrics, color, tip }) }, legend: getLegendOption(legend, seriesNames), grid: getGridPositions( legend, seriesNames, '', barChart, yAxis, xAxis, xAxisData ) // ...brushedOptions } } export function getDataSum(data, metrics) { const dataSum = data.map((d, index) => { const metricArr = [] let maSum = 0 metrics.forEach((m, i) => { const decodedMetricName = decodeMetricName(m.name) const metricName = d[`${m.agg}(${decodedMetricName})`] metricArr.push(metricName) if (metricArr.length === metrics.length) { metricArr.forEach((mr) => { maSum += mr }) } }) return maSum }) return dataSum } export function getColorDataSum(data, metrics) { let maSum = 0 const dataSum = data.map((d, index) => { let metricArr = 0 metrics.forEach((m, i) => { const decodedMetricName = decodeMetricName(m.name) const metricName = d[`${m.agg}(${decodedMetricName})`] metricArr += metricName }) return metricArr }) dataSum.forEach((mr) => { maSum += mr }) return maSum }