import defaultOption from "./chartOption" import { has, isArray, extend, extendDeep, getPercent, getTotal } from "@/core/utils/utils" const compatibleMultChartData = (data: any): any => { let arr: any = [] if (has(data, "chartData")) { if (isArray(data.chartData)) { arr = extend(data.chartData) } else if (Object.keys(data.chartData)) { arr.push(extend(data.chartData)) } } return arr } //获取兼容的chart对象 const compatibleData = (data: any): any => { let obj = {} if (has(data, "chartData")) { if (isArray(data.chartData)) { obj = data.chartData[0] } else { obj = data.chartData } } else { obj = data } return obj } //获取兼容的SeriesData const compatibleSeries = (data: any): any => { let result = [] if (has(data, "seriesData")) { result = data.seriesData } else if (has(data, "series")) { result = data.series } else { result = [] } return result } /** * 获取某个配置项,并将其在原对象中删除 (保持eChart对象的干净) * @param key * @param options * @param defaultVal * @param objectType 0 值类型, 1 对象, 2 数组 * @returns */ const getOption = (options: any, key: string, defaultVal?: any) => { let type = 0 if (typeof defaultVal == "object") { type = Array.isArray(defaultVal) ? 2 : 1 } let val = type == 0 ? defaultVal : type == 1 ? Object.assign({}, defaultVal || {}) : Object.assign([], defaultVal || []) if (has(options, key)) { val = type == 0 ? options[key] : type == 1 ? extendDeep({}, val, options[key]) : extendDeep([], val, options[key]) delete options[key] } return val } const lineBarSeriesOption = (chart: any, serieData: Array, serieType: string, chartOptions: any) => { const _xAxisData = has(chart, "categories") ? chart.categories : [] const legend: any = Object.assign({}, defaultOption.SIZE.legend, { type: getOption(chartOptions, "legendType", defaultOption.SIZE.legend.type), show: true, icon: getOption(chartOptions, "legendIcon", defaultOption.SIZE.legend.icon), data: [], }) const tooltip = { show: getOption(chart, "showTooltip", true), trigger: "axis", axisPointer: { // 坐标轴指示器,坐标轴触发有效 type: "shadow", // 默认为直线,可选为:'line' | 'shadow' }, } const totalArr: Array = [] if (has(serieData[0], "data")) { for (let i = 0; i < serieData[0].data.length; i++) { let temptotal = 0 serieData.forEach((v: any) => { if (!v.data[i]) { v.data[i] = 0 } temptotal += parseFloat(v.data[i]) }) totalArr.push(temptotal) } } const series = [] let yAxisMax = 0 let maxData = 0 let showLabel = getOption(chartOptions, "showLabel", false) //const markPointStyles = getOption(chartOptions, "markPointStyles", []) //const markPointLabel = getOption(chartOptions, "markPointLabel", {}) const markLineStyles = getOption(chartOptions, "markLineStyles", [ { color: "#FF5147", }, ]) const markLineLabel = getOption(chartOptions, "markLineLabel", { color: "#FF5147", }) const markAreaStyles = getOption(chartOptions, "markAreaStyles", { color: "rgba(255,81,71,0.2)", }) const markAreaLabel = getOption(chartOptions, "markAreaLabel", { color: "#F2637B", offset: [31, 0], }) const labelFormatter = getOption(chartOptions, "labelFormatter", undefined) const markPoint = getOption(chartOptions, "markPoint", {}) const markLine = getOption(chartOptions, "markLine", {}) const markArea = getOption(chartOptions, "markArea", {}) const barGap = getOption(chartOptions, "barGap", defaultOption.SIZE.barGap) const symbol = getOption(chartOptions, "symbol", "circle") const smooth = getOption(chartOptions, "smooth", 0.5) const areaStyleColors = getOption(chartOptions, "areaStyle", [ "rgba(56,164,90,.5)", "rgb(76,136,207,.3)", "rgb(255,185,126,.3)", ]) const dataMarkLine = getOption(chart, "markLine", {}) const dataMarkArea = getOption(chart, "markArea", {}) serieData.forEach((v: any, i: number) => { legend.data.push({ name: v.name, }) if (typeof labelFormatter == "function") { showLabel = true } const type = has(v, "type") ? v.type : serieType let seriesOption: any = { type: type, name: v.name, data: v.data, stack: has(v, "stack") ? v.stack : null, itemStyle: getOption(v, "itemStyle", {}), markPoint, markLine, markArea, label: { show: showLabel, position: "", formatter: labelFormatter, fontSize: 10, }, } seriesOption = Object.assign( seriesOption, type == "bar" ? { barWidth: defaultOption.SIZE.barWidth, barGap, //barMinWidth: defaultOption.SIZE.barMinWidth, barMaxWidth: defaultOption.SIZE.barMaxWidth, } : { symbol, symbolSize: defaultOption.SIZE.symbolSize, connectNulls: true, smooth, smoothMonotone: "x", areaStyle: { color: areaStyleColors[i], }, } ) if (v.data && v.data.length) { v.data.forEach((vv: any) => { if (vv > maxData) { maxData = vv } }) yAxisMax = Math.floor(maxData * 1.2) } if (dataMarkLine) { seriesOption.markLine = dataMarkLine } if (has(chart, "warning")) { const markLine: any = { silent: true, label: markLineLabel, data: [ { yAxis: chart.warning, }, ], } if (markLineStyles && markLineStyles.length) { markLine.lineStyle = markLineStyles[i % markLineStyles.length] } if (chart.warning > maxData) { yAxisMax = Math.floor(chart.warning * 1.1) } seriesOption.markLine = markLine } if (dataMarkArea) { seriesOption.markArea = { data: dataMarkArea, label: markAreaLabel, itemStyle: markAreaStyles, } } const seriesItem = extendDeep(seriesOption, chartOptions.series || {}) series.push(seriesItem) }) if (getOption(chartOptions, "showTotal", false)) { series.push({ name: "总计", type: serieType, stack: "total", markLine: { data: [ { type: "max", name: "最大值" }, { type: "min", name: "最小值" }, ], label: { show: "true", position: "start" }, lineStyle: { color: "rgba(255, 81, 71, 0.85)", }, }, itemStyle: { normal: { color: "rgba(128, 128, 128, 0)", }, }, data: totalArr, }) } const axisLabel = getOption(chartOptions, "axisLabel", {}) const axisLine = getOption(chartOptions, "axisLine", {}) const splitLine = getOption(chartOptions, "splitLine", {}) let xAxis: any = { show: true, type: "category", axisLabel, axisLine, splitLine, splitArea: getOption(chartOptions, "xSplitArea", {}), axisTick: { alignWithLabel: true, }, data: _xAxisData, nameGap: defaultOption.SIZE.nameGap, boundaryGap: serieType != "line", } let yAxis: any = { show: true, name: getOption(chart, "yzTitle", ""), nameTextStyle: { align: "auto" }, type: "value", max: yAxisMax ? yAxisMax : null, axisLabel: { ...axisLabel, formatter: getOption(chartOptions, "yAxisFormat", undefined) }, axisLine, splitLine, splitArea: getOption(chartOptions, "ySplitArea", {}), axisTick: { alignWithLabel: true, }, } //console.log("CHART_title", yAxis.name) const direction = getOption(chartOptions, "direction", false) if (direction) { //互换x,y轴 if (direction == "y") { const tempX = xAxis const tempY = yAxis xAxis = Object.assign({}, tempY) yAxis = Object.assign({}, tempX) } } if (getOption(chartOptions, "inverseX", false)) { xAxis.inverse = true } if (getOption(chartOptions, "inverseY", false)) { yAxis.inverse = true } return { legend, tooltip, xAxis, yAxis, series } } const pieSeriesOption = (chart: any, serieData: Array, serieType: string, chartOptions: any) => { const legend: any = { type: getOption(chartOptions, "legendType", defaultOption.SIZE.legend.type), show: true, icon: getOption(chartOptions, "legendIcon", defaultOption.SIZE.legend.icon), itemGap: defaultOption.SIZE.roseItemGap, data: [], } let showLabel = getOption(chartOptions, "showLabel", false) const showPercent = getOption(chartOptions, "showPercent", true) let labelFormatter = getOption(chartOptions, "labelFormatter", undefined) if (typeof labelFormatter == "function") { showLabel = true } if (!labelFormatter) { labelFormatter = "{b} ({c}) " } const title: any = {} const tooltip = { trigger: "item", formatter: labelFormatter, } const data: any = [] const seriesOption: any = { type: "pie", center: getOption(chartOptions, "pieCenter", defaultOption.SIZE.pieCenter), radius: getOption(chartOptions, "pieRadius", defaultOption.SIZE.pieRadius), markPoint: getOption(chartOptions, "markPoint", {}), markLine: getOption(chartOptions, "markLine", {}), markArea: getOption(chartOptions, "markArea", {}), label: { show: showLabel, position: "outside", formatter: labelFormatter, }, } const total = getTotal(serieData, has(serieData[0], "value") ? "value" : "data") const titleText = getOption(chartOptions, "titleText", getOption(chart, "yzTitle", "") ?? "") const titleUnit = getOption(chartOptions, "titleUnit", "") if (titleText) { if (titleUnit) { title.text = [`{title|${titleText}}`, `{total|${total}}`, `{unit|${titleUnit}}`].join("\n") } else { title.text = `{title|${titleText}}` } } const itemStyle = getOption(chartOptions, "pieItemStyle", undefined) serieData.forEach((v: any) => { const name = showPercent ? `${v.name}:${getPercent(total, v.value)}` : v.name legend.data.push({ name, }) const item: any = { name, value: v.value, } if (itemStyle) { item.itemStyle = itemStyle } data.push(item) }) const series = [extendDeep(seriesOption, chartOptions.series || {}, { data })] return { legend, title, tooltip, series } } /** * * @param data 远程数据 * @param options 为eChart的配置项 同时也可以单独配置 serieType 图表类型 showTotal 显示总数 showLabel 显示数据标签 labelFormatter 数据标签格式化 markLineLabel 标记警告label (远程数据返回 "warning" 字段 生效) markLineStyles 标记警告线样式 (远程数据返回 "warning" 字段 生效) axisLabel 坐标轴label axisLine 坐标轴样式 splitLine 分割线样式 yAxisFormat y轴label格式化 inverseX 反向坐标轴 inverseY 反向坐标轴 direction: 图表方向 'x' | 'y' (bar 类型生效) ... * @param serieType 图表类型 * @returns eChart的配置项 */ const chart = (data: any, options: any, serieType?: string) => { if (data == null) { throw new Error("NO CHART DATA") } const _chart = compatibleData(data) const _series = compatibleSeries(_chart) const chartOptions = extend({}, options) serieType = getOption(chartOptions, "serieType", serieType) //const _xAxisData = has(_chart, "categories") ? _chart.categories : [] //标题默认在图表下居中 ,使用远程数据 yzTitle 字段 const title = { show: true, text: getOption(chartOptions, "titleText", "") || getOption(_chart, "title", "") || getOption(_chart, "yzTitle", ""), } const grid = Object.assign({}, defaultOption.SIZE.grid) let seriesOption = {} switch (serieType) { case "bar": case "line": seriesOption = lineBarSeriesOption(_chart, _series, serieType ?? "bar", chartOptions) break case "pie": seriesOption = pieSeriesOption(_chart, _series, serieType ?? "pie", chartOptions) break } const option = extendDeep({ title, grid }, seriesOption, chartOptions) return option //return Object.assign(true, {}, opt, options || {}) } // const doubleTypes = (data: any, options: any, types = ["bar", "line"]) => { // if (data == null) { // throw new Error("no data") // } // const chartOptions = extendDeep({}, options) // const _charts = compatibleMultChartData(data) // const grid = Object.assign({}, defaultOption.SIZE.grid) // _charts.forEach((_chart: any, i: number) => { // const _series = compatibleSeries(_chart) // //标题默认在图表下居中 ,使用远程数据 title , yzTitle 字段 // const title = { // show: true, // text: getOption(_chart, "title", "") || getOption(_chart, "yzTitle", ""), // } // }) // } export default { bar(data: any, options: any) { return chart(data, options, "bar") }, line(data: any, options: any) { return chart(data, options, "line") }, pie(data: any, options: any) { return chart(data, options, "pie") }, ring(data: any, options: any) { options = Object.assign({ pieRadius: defaultOption.SIZE.ringRadius }, options) return chart(data, options, "pie") }, }