import { formatToNumber } from 'utils/tool'; const chartColor = ['#5dbde0', '#f8c969', '#50ccad', '#b09ae4', '#ed7764', '#8cd481', '#6c9fe3', '#bac2d2']; const hundred = 100; const thousand = 1000; const tenThousand = 10000; const million = 1000000; const initLinearEchart = (barData: any, config: any) => { const xAxis = config.x || 'xAxis'; const yAxis = config.y || 'yAxis'; const option = { title: { text: '', }, grid: { left: 80, right: 80, }, legend: { data: barData.legendData, top: 20, bottom: 20, orient: 'horizontal', textStyle: { color: '#666666', fontStyle: 'normal', fontWeight: 'normal', fontFamily: 'Microsoft YaHei, sans-serif', fontSize: 12, }, }, color: chartColor, tooltip: { trigger: 'axis', }, series: barData.series, animationEasing: 'elasticOut', animationDelayUpdate: (idx: number) => idx * 5, }; option[xAxis] = { data: barData.xAxisData, name: barData.xAxisName, nameTextStyle: { color: 'white', }, boundaryGap: config.boundaryGap, showAllSymbol: false, silent: false, }; option[yAxis] = []; for (let i = 0; i < (config.yLen || 1); i += 1) { option[yAxis].push({ scale: config.scale || false, type: 'value', axisLabel: { formatter: (value: number) => { const newValue = Math.abs(value); const f = value >= 0 ? '' : '-'; let result = ''; if (newValue >= thousand && newValue < million) { result = f + formatToNumber(newValue / thousand); result += 'K'; } else if (newValue >= million) { result = f + formatToNumber(newValue / million); result += 'M'; } else { result = f + formatToNumber(newValue); } return result; }, }, }); } return option; }; const initPieEchart = (barData: any) => { const { legends: legendData, series: serieData, percs, colors, } = barData; const totalData = serieData.reduce((total: number, item: any) => total + Number(item.value), 0); const somePercent = percs.length > 0 && serieData.length > 0 ? ((serieData[0].value / totalData) * 100).toFixed(2) : ''; const option = { title: { text: somePercent ? `完成度:${somePercent}%` : `总量:${totalData}`, subtext: '', x: 'center', }, tooltip: { trigger: 'item', }, color: colors && colors.length > 0 ? colors : chartColor, legend: { data: legendData, right: 10, orient: 'vertical', // default: horizontal textStyle: { lineHeight: 18, color: '#666666', fontStyle: 'normal', fontWeight: 'normal', fontFamily: 'Microsoft YaHei, sans-serif', fontSize: 12, }, // selector: [ // { // type: 'all', // title: '全选', // }, // { // type: 'inverse', // title: '反选', // }, // ], selectorPosition: 'start', selectorItemGap: 12, selectorLabel: { align: 'center', verticalAlign: 'middle', lineHeight: 20, padding: [2, 8, 0, 8], color: '#666666', borderColor: '#666666', borderWidth: 0.5, borderRadius: [5], }, emphasis: { selectorLabel: { backgroundColor: '#15CCCC', borderColor: '#15CCCC', }, }, }, series: [ { name: '', type: 'pie', left: '20%', width: '60%', radius: '55%', center: ['50%', 200], data: serieData, itemStyle: { normal: { label: { show: true, formatter: '{b} : {c} ({d}%)', }, labelLine: { show: true, }, }, }, }, ], }; return option; }; const initRingPieEchart = (barData: any) => { const { legends: legendData, series: serieData, percs, colors, } = barData; const totalData = serieData.reduce((total: number, item: any) => total + Number(item.value), 0); const somePercent = percs.length > 0 && serieData.length > 0 ? ((serieData[0].value / totalData) * 100).toFixed(2) : ''; const option = { title: { text: somePercent ? `完成度:${somePercent}%` : `总量:${totalData}`, subtext: '', x: 'center', left: '30%', }, tooltip: { trigger: 'item', formatter: '{b} : {c} ({d}%)', }, color: colors && colors.length > 0 ? colors : chartColor, legend: { data: legendData, right: 10, orient: 'vertical', // default: horizontal textStyle: { lineHeight: 18, color: '#666666', fontStyle: 'normal', fontWeight: 'normal', fontFamily: 'Microsoft YaHei, sans-serif', fontSize: 12, }, // selector: [ // { // type: 'all', // title: '全选', // }, // { // type: 'inverse', // title: '反选', // }, // ], selectorPosition: 'start', selectorItemGap: 12, selectorLabel: { align: 'center', verticalAlign: 'middle', lineHeight: 20, padding: [2, 8, 0, 8], color: '#666666', borderColor: '#666666', borderWidth: 0.5, borderRadius: [5], }, emphasis: { selectorLabel: { backgroundColor: '#15CCCC', borderColor: '#15CCCC', }, }, }, series: [ { name: '', type: 'pie', left: '20%', width: '60%', radius: ['50%', '70%'], center: ['40%', 150], data: serieData, avoidLabelOverlap: false, itemStyle: { normal: { label: { show: false, formatter: '{b} : {c} ({d}%)', }, }, }, }, ], }; return option; }; const getNameByColumn = (data: any, valueParam: any) => { const columnMetas = data.columnMetas || []; let value = `${valueParam}`; const len = value.indexOf('('); for (let i = 0; i < columnMetas.length; i += 1) { const key = columnMetas[i].name; if (len > 0) { value = value.substr(0, len); } if (value.trim() === key.trim() && (len === -1 || key.length === len - 1)) { return columnMetas[i].label.trim() || '未命名'; } } return '未命名'; }; const parseData = (jsonArr: any, obj: any) => { const keys = obj.keys || []; const values = obj.values || []; const rightVals = obj.rightVals || []; const percs = obj.percs || []; const groups = obj.groups || []; const checks = obj.checks || []; const stacks = obj.stacks || []; const data = { rows: jsonArr || [] }; if (keys.length === 0 && values.length === 0 && groups.length === 0) { return { keys, values, rightVals, percs, groups, stacks, checks, schema: {}, rows: jsonArr || [], allData: obj.allData || jsonArr || [], }; } const aggrFunc = { sum: (a: any, b: any) => { const aIsNaN = Number.isNaN(a) ? 1 : parseFloat(a); const aIsNULL = a === null ? '' : (aIsNaN); const constA = (a !== undefined) ? (aIsNULL) : 0; const bIsNaN = Number.isNaN(b) ? 1 : parseFloat(b); const bIsNULL = b === null ? '' : (bIsNaN); const constB = (b !== undefined) ? (bIsNULL) : 0; return Number(constA) + Number(constB); }, count: (a: any, b: any) => { const aIsNULL = a === null ? '' : parseInt(a, 10); const constA = (a !== undefined) ? (aIsNULL) : 0; const bIsNULL = b === null ? '' : 1; const constB = (b !== undefined) ? (bIsNULL) : 0; return Number(constA) + Number(constB); }, min: (a: any, b: any) => { const aIsNaN = Number.isNaN(a) ? 1 : parseFloat(a); const aIsNULL = a === null ? '' : (aIsNaN); const constA = (a !== undefined) ? (aIsNULL) : 0; const bIsNaN = Number.isNaN(b) ? 1 : parseFloat(b); const bIsNULL = b === null ? '' : (bIsNaN); const constB = (b !== undefined) ? (bIsNULL) : 0; return Math.min(Number(constA), Number(constB)); }, max: (a: any, b: any) => { const aIsNaN = Number.isNaN(a) ? 1 : parseFloat(a); const aIsNULL = a === null ? '' : (aIsNaN); const constA = (a !== undefined) ? (aIsNULL) : 0; const bIsNaN = Number.isNaN(b) ? 1 : parseFloat(b); const bIsNULL = b === null ? '' : (bIsNaN); const constB = (b !== undefined) ? (bIsNULL) : 0; return Math.max(Number(constA), Number(constB)); }, avg: (a: any, b: any) => { const aIsNaN = Number.isNaN(a) ? 1 : parseFloat(a); const aIsNULL = a === null ? '' : (aIsNaN); const constA = (a !== undefined) ? (aIsNULL) : 0; const bIsNaN = Number.isNaN(b) ? 1 : parseFloat(b); const bIsNULL = b === null ? '' : (bIsNaN); const constB = (b !== undefined) ? (bIsNULL) : 0; return Number(constA) + Number(constB); }, }; const aggrFuncDiv = { sum: false, count: false, min: false, max: false, avg: true, }; const schema = {}; const rows = {}; for (let i = 0; i < data.rows.length; i += 1) { const row = data.rows[i]; let s = schema; let p = rows; for (let k = 0; k < keys.length; k += 1) { const key = keys[k]; // add key to schema if (!s[key.name]) { s[key.name] = { order: k, index: key.index, type: 'key', children: {}, }; } s = s[key.name].children; // add key to row const keyKey = row[key.index]; if (!p[keyKey]) { p[keyKey] = {}; } p = p[keyKey]; } for (let g = 0; g < groups.length; g += 1) { const group = groups[g]; const groupKey = row[group.index] || ''; // add group to schema if (!s[groupKey]) { s[groupKey] = { order: g, index: group.index, type: 'group', children: {}, }; } s = s[groupKey].children; // add key to row if (!p[groupKey]) { p[groupKey] = {}; } p = p[groupKey]; } for (let v = 0; v < values.length; v += 1) { const value = values[v]; const valueKey = `${value.name}(${value.aggr})`; // add value to schema if (!s[valueKey]) { s[valueKey] = { type: 'value', order: v, index: value.index, }; } // add value to row if (!p[valueKey]) { p[valueKey] = { value: (value.aggr !== 'count') ? row[value.index] : 1, count: 1, }; } else { p[valueKey] = { value: aggrFunc[value.aggr](p[valueKey].value, row[value.index], p[valueKey].count + 1), count: (aggrFuncDiv[value.aggr]) ? p[valueKey].count + 1 : p[valueKey].count, }; } } for (let v = 0; rightVals && v < rightVals.length; v += 1) { const value = rightVals[v]; const valueKey = `${value.name}(${value.aggr})`; // add value to schema if (!s[valueKey]) { s[valueKey] = { type: 'value', order: v, index: value.index, }; } // add value to row if (!p[valueKey]) { p[valueKey] = { value: (value.aggr !== 'count') ? row[value.index] : 1, count: 1, }; } else { p[valueKey] = { value: aggrFunc[value.aggr](p[valueKey].value, row[value.index], p[valueKey].count + 1), count: (aggrFuncDiv[value.aggr]) ? p[valueKey].count + 1 : p[valueKey].count, }; } } } return { keys, percs, values, rightVals, groups, stacks, schema, rows, allData: jsonArr || [], }; }; const parseJSONAll = (json: any, result: any, path?: any) => { let ret = []; if (path !== undefined) { for (let j = 0; j < path.length; j += 1) { ret.push(path[j]); } } let end = false; Object.keys(json).forEach((i) => { /* if (typeof json[i] === 'object' && json[i] !== null) { ret = []; if (path !== undefined) { for (let j = 0; j < path.length; j+=1) { ret.push(path[j]); } } ret.push(i); parseJSONAll(json[i], result, ret); } else { if (json[i] === null || json[i] === '') { ret.push('placeholder'); } else { if (json[i].value === 'NULL') { ret.push('NULL'); } else { if (json.value === null || json.value === '') { ret.push('placeholder'); } else { ret.push(Math.round(json.value / json.count * tenThousand) / tenThousand); } } end = true; } } */ if (typeof json[i] === 'object' && json[i] !== null) { ret = []; if (path !== undefined) { for (let j = 0; j < path.length; j += 1) { ret.push(path[j]); } } ret.push(i); parseJSONAll(json[i], result, ret); } else if (json[i] === null || json[i] === '') { ret.push('placeholder'); } else if (json[i].value === 'NULL') { ret.push('NULL'); end = true; } else if (json.value === null || json.value === '') { ret.push('placeholder'); end = true; } else { // eslint-disable-next-line no-mixed-operators ret.push(Math.round(json.value / json.count * tenThousand) / tenThousand); end = true; } }); if (end && ret.length > 0) { result.push(ret); } }; const getCount = (str: any, subStr: any) => { const regex = new RegExp(subStr, 'g'); const result = str.match(regex); return !result ? 0 : result.length; }; const parseDataToLinearEchart = (src: any, data: any, typeConfig: any, seriesConfigParam: any) => { const seriesConfig = seriesConfigParam || {}; let xAxisData: any[] = []; const series = []; const legendData = []; const result: any[] = []; const keysLen = data.keys.length; const groupsLen = data.groups.length; const valuesLen = data.values.length; const rightValsLen = data.rightVals ? data.rightVals.length : 0; if (groupsLen === 0 && valuesLen === 0 && rightValsLen === 0) { if (keysLen !== 0) { xAxisData = Object.keys(data.rows); } } else { parseJSONAll(data.rows, result); } for (let i = 0; i < result.length; i += 1) { if (xAxisData.indexOf(result[i][0]) === -1) { xAxisData.push(result[i][0]); } let xName = typeConfig.isDouble ? typeConfig.left.name : ''; for (let k = 1; k < groupsLen + 1; k += 1) { if (result[i][k]) { xName += `${result[i][k]}-`; } } let flag = true; const tmp = `${result[i][groupsLen + 1]}`; xName += getNameByColumn(src, tmp); const lastIndex = tmp.substr(0, tmp.indexOf(' (')); if (typeConfig.isDouble || false) { for (let r = 0; r < data.rightVals.length; r += 1) { if (data.rightVals[r].name === (`${lastIndex} `)) { xName = xName.replace(typeConfig.left.name, typeConfig.right.name); } } } for (let s = 0; s < series.length; s += 1) { if (series[s].name === xName) { const value = result[i][groupsLen + 3]; if (!series[s].data[series[s].data.length - 1] && series[s].data[series[s].data.length - 1] !== 0) { series[s].data[series[s].data.length - 1] = value; } else { series[s].data.push(value); } flag = false; } else if (xAxisData.length > series[s].data.length) { series[s].data.push(''); } } if (flag) { legendData.push(xName); const json = { ...{ name: xName, type: typeConfig.left.type, data: [], stack: data.stacks.includes(lastIndex), tooltip: { trigger: 'axis', formatter: (chartData: any) => { const newValue = Math.abs(chartData.data); let showData = chartData.data; const f = chartData.data >= 0 ? '' : '-'; if (newValue >= thousand && newValue < million) { showData = f + formatToNumber(newValue / thousand); showData += 'K'; } else if (newValue >= million) { showData = f + formatToNumber(newValue / million); showData += 'M'; } else { showData = f + formatToNumber(newValue); } return `${chartData.seriesName}
${(chartData.marker || '')}${chartData.name}: ${showData}`; }, }, markLine: { symbol: '', precision: 4, data: [ { type: 'average', name: '平均值', label: { normal: { formatter: (obj: any) => { const newValue = Math.abs(obj.value); const f = obj.value >= 0 ? '' : '-'; let returnVal = ''; if (newValue >= thousand && newValue < million) { returnVal = f + formatToNumber(newValue / thousand); returnVal += 'K'; } else if (newValue >= million) { returnVal = f + formatToNumber(newValue / million); returnVal += 'M'; } else { returnVal = f + formatToNumber(newValue); } return returnVal; }, }, }, }, ], }, animationDelay: (idx: number) => idx * 10, }, ...seriesConfig, }; for (let r = 0; typeConfig.right && data.rightVals && r < data.rightVals.length; r += 1) { if (data.rightVals[r].name === (`${lastIndex} `)) { json.yAxisIndex = 1; json.type = typeConfig.right.type; json.name = json.name.replace(typeConfig.left.name, typeConfig.right.name); legendData[legendData.length - 1] = legendData[legendData.length - 1] .replace(typeConfig.left.name, typeConfig.right.name); } } const value = result[i][groupsLen + 3]; const sk = result[i][groupsLen + 1].substr(0, result[i][groupsLen + 1].indexOf('(')); if (data.percs.indexOf(sk) > -1) { json.markLine.data[0].label.normal.formatter = (obj: any) => { const newValue = Math.abs(obj.value); const f = obj.value >= 0 ? '' : '-'; let returnVal = ''; if (newValue >= thousand && newValue < million) { returnVal = f + formatToNumber((newValue * hundred) / thousand); returnVal += 'K%'; } else if (newValue >= million) { returnVal = f + formatToNumber((newValue * hundred) / million); returnVal += 'M%'; } else { returnVal = f + formatToNumber(newValue * hundred); returnVal += '%'; } return returnVal; }; json.tooltip = { trigger: 'axis', formatter: (chartData: any) => { const newValue = Math.abs(chartData.data); let showData = chartData.data; const f = chartData.data >= 0 ? '' : '-'; if (newValue >= thousand && newValue < million) { showData = f + formatToNumber((newValue * hundred) / thousand); showData += 'K%'; } else if (newValue >= million) { showData = f + formatToNumber((newValue * hundred) / million); showData += 'M%'; } else { showData = f + formatToNumber(newValue * hundred); showData += '%'; } return `${chartData.seriesName}
${(chartData.marker || '')}${chartData.name}: ${showData}`; }, }; } for (let x = 0; x < xAxisData.length - 1; x += 1) { json.data.push(''); } json.data.push(value); series.push(json); } } const { keys } = data; let xAxisName = '未命名'; if (keys.length > 0) { xAxisName = getNameByColumn(src, keys[0].name); } for (let s = 0; s < series.length; s += 1) { const subSerData = series[s].data; if (subSerData.length !== getCount(subSerData.join(','), ',')) { for (let sub = 0; sub < subSerData.length; sub += 1) { if (subSerData[sub] === 'placeholder') { subSerData[sub] = ''; } } } else { series[s].data = subSerData.join(',').replace(/placeholder/g, '').split(','); } } return { xAxisName, xAxisData, series, legendData, percs: data.percs, values: data.values, rightVals: data.rightVals, cacheID: data.cacheID, }; }; const parseDataToPie = (src: any, data: any) => { const serieData: any[] = []; const legendData: any[] = []; let serieName = ''; if (data.keys.length <= 0) { return { legendData, serieName, series: serieData, colors: data.colors, percs: data.percs, legends: data.legends, values: data.values, }; } Object.keys(data.rows).forEach((i: any) => { legendData.push(i); const json = { value: 0, name: i, }; serieData.push(json); const rowsItemKeys = Object.keys(data.rows[i]); for (let j = 0, len = rowsItemKeys.length; j < len; j += 1) { const jName = getNameByColumn(src, ''); serieName = jName; for (let s = 0; s < serieData.length; s += 1) { if (serieData[s].name === i) { serieData[s].value = Math.round( // eslint-disable-next-line no-mixed-operators data.rows[i][rowsItemKeys[j]].value / data.rows[i][rowsItemKeys[j]].count * 10000, ) / 10000; } } } }); return { legendData, serieName, colors: data.colors, series: serieData, percs: data.percs, legends: data.legends, values: data.values, }; }; const getIndex = (data: any, value: any) => { const columnMetas = data.columnMetas || []; for (let i = 0; i < columnMetas.length; i += 1) { if (columnMetas[i].name === value) { return i; } } return -1; }; const getConfig = (data: any, config: any) => { const keys = []; const values = []; const rightVals = []; const groups = []; if (config.x && config.x.length > 0) { const alltags = config.x; for (let i = 0; i < alltags.length; i += 1) { keys.push({ name: `${alltags[i]} `, index: getIndex(data, alltags[i]), aggr: 'sum', }); } } if (config.y && config.y.length > 0) { const alltags = config.y; for (let i = 0; i < alltags.length; i += 1) { values.push({ name: `${alltags[i]} `, index: getIndex(data, alltags[i]), aggr: 'sum', }); } } if (config.ry && config.ry.length > 0) { const alltags = config.ry; for (let i = 0; i < alltags.length; i += 1) { rightVals.push({ name: `${alltags[i]} `, index: getIndex(data, alltags[i]), aggr: 'sum', }); } } if (config.g && config.g.length > 0) { const alltags = config.g; for (let i = 0; i < alltags.length; i += 1) { groups.push({ name: `${alltags[i]} `, index: getIndex(data, alltags[i]), aggr: 'sum', }); } } return { keys, values, rightVals, groups, stacks: config.stack, }; }; const getOption = (type: any, config: any, data: any) => { const dupData = parseData(data.results || [], getConfig(data, config || {})); switch (type) { case 'bar': return initLinearEchart( parseDataToLinearEchart( data, dupData, { isDouble: false, left: { type: 'bar' }, }, null, ), { x: 'xAxis', y: 'yAxis', yLen: 1, scale: true, boundaryGap: true, }, ); case 'horbar': return initLinearEchart( parseDataToLinearEchart( data, dupData, { isDouble: false, left: { type: 'bar' }, }, null, ), { x: 'yAxis', y: 'xAxis', yLen: 1, scale: true, boundaryGap: true, }, ); case 'arealine': return initLinearEchart( parseDataToLinearEchart( data, dupData, { isDouble: false, left: { type: 'line' }, }, { areaStyle: { normal: {} }, }, ), { x: 'xAxis', y: 'yAxis', yLen: 1, scale: true, boundaryGap: false, }, ); case 'line': return initLinearEchart( parseDataToLinearEchart( data, dupData, { isDouble: false, left: { type: 'line' }, }, null, ), { x: 'xAxis', y: 'yAxis', yLen: 1, scale: true, boundaryGap: false, }, ); case 'doubley': return initLinearEchart( parseDataToLinearEchart( data, dupData, { isDouble: true, left: { name: '(左轴)', type: 'line' }, right: { name: '(右轴)', type: 'bar' }, }, null, ), { x: 'xAxis', y: 'yAxis', yLen: 2, scale: false, boundaryGap: true, }, ); case 'doubley-2': return initLinearEchart( parseDataToLinearEchart( data, dupData, { isDouble: true, left: { name: '(左轴)', type: 'bar' }, right: { name: '(右轴)', type: 'line' }, }, null, ), { x: 'xAxis', y: 'yAxis', yLen: 2, scale: false, boundaryGap: true, }, ); case 'doublebar': return initLinearEchart( parseDataToLinearEchart( data, dupData, { isDouble: true, left: { name: '(左轴)', type: 'bar' }, right: { name: '(右轴)', type: 'bar' }, }, null, ), { x: 'xAxis', y: 'yAxis', yLen: 2, scale: false, boundaryGap: true, }, ); case 'doubleline': return initLinearEchart( parseDataToLinearEchart( data, dupData, { isDouble: true, left: { name: '(左轴)', type: 'line' }, right: { name: '(右轴)', type: 'line' }, }, null, ), { x: 'xAxis', y: 'yAxis', yLen: 2, scale: true, boundaryGap: false, }, ); case 'pie': return initPieEchart(parseDataToPie(data, dupData)); case 'ringpie': return initRingPieEchart(parseDataToPie(data, dupData)); default: return ''; /* case 'pie': initPieEchart(domObj, {type:'pie'}); if((data && data.dataSrcType === 1) || config.dataSrcType === 1) { const pieData = dataMan.parseKVDataToPie(dupData); updatePie(pieData); }else{ const pieData = dataMan.parseDataToPie(dupData); updatePie(pieData); } updateGridHeight(config.gridHeight); break; case 'ringpie': initRingPieEchart(domObj, {type:'pie'}); if((data && data.dataSrcType === 1) || config.dataSrcType === 1) { const ringPieData = dataMan.parseKVDataToPie(dupData); updateRingPie(ringPieData); }else{ const ringPieData = dataMan.parseDataToPie(dupData); updateRingPie(ringPieData); } updateGridHeight(config.gridHeight); break; case 'funnel': initFunnelEchart(domObj, {type:'funnel'}); if(type === 'theme') { updateFunnel(dataMan.parse6DataToFunnel(dupData)); }else{ if((data && data.dataSrcType === 1) || config.dataSrcType === 1) { updateFunnel(dataMan.parseKVDataToFunnel(dupData)); }else{ updateFunnel(dataMan.parseDataToFunnel(dupData)); } } updateGridHeight(config.gridHeight); break; */ } }; export { parseData, getOption, };