import { reactive, toRefs, onMounted, ref,watch } from "vue"; import _ from "lodash"; import { IChartType, IDataProps, IParams, ISeasonDataItemProps, } from "@/types"; import Highcharts from "highcharts"; import { defaultOpts, seasonOptions } from "@/utils/chartOptions"; import moment from "moment"; import router from '@/router' // 散点x const scatterXAxis = { tickPosition: "inside", lineColor: "#bfbfbf", tickColor: "#bfbfbf", tickLength: 5, ordinal: false, type: "linear", }; //基础y轴 const basicYAxis = { tickLength: 5, lineWidth: 1, lineColor: "#bfbfbf", tickColor: "#bfbfbf", // offset: 0, visible: true, gridLineWidth: 0, tickPosition: "inside", endOnTick: false, startOnTick: false, showLastLabel: true, }; interface StateProps { chartLimit: { min?: number; max?: number; x_min?:number; x_max?:number; rightMin?:number; rightMax?:number; rightTwoMin?:number; rightTwoMax?:number; } dataList: any[]; chartInfo: any; } /** * useChartRender 处理图表渲染 返回options * @param Data 详情数据 lange中英文 * @param { * Source 1 //ETA图 ChartType: 1曲线图 2季节图 3面积图 4柱状图 5散点图 6组合图 7柱形 10截面散点 11雷达图 * 2 商品价格 * 3 相关性 * 4 滚动相关性 * 5 商品利润 * 6 拟合方程 * 7 统计特征/标准差 * 8 统计特征/百分位 * 9 统计特征/频率 * 10 跨品种分析 * } * */ //依赖数据 const state = reactive({ chartInfo: {}, dataList: [], chartLimit: { min:0, //左轴上下限 max:0, rightMin:0,//右轴上下限 rightMax:0, rightTwoMin:0,//右二轴上下限 rightTwoMax:0, }, }) const language = ref('') const routeQuery = ref({});//路由参 watch(() => router.currentRoute.value.query,(nval) => { routeQuery.value = nval }) export const useChartRender = (Data,lang='zh',) => { state.chartInfo = Data.ChartInfo state.dataList = [1,11].includes(Data.ChartInfo.Source) ? Data.EdbInfoList : [Data.EdbInfoList[0]]; language.value = lang let chartOptions:any; //eta图 if ([1,11].includes(Data.ChartInfo.Source)) { setLimitData(state.dataList) const typeMap = { 1: setDefaultLineOptions, 2: setSeasonOptions, 3: setStackOrCombinChart, 4: setStackOrCombinChart, 5: setScatterChartOptions, 6: setStackOrCombinChart, 7: initBarData, 10: initSectionScatterData, 11: initRadarData }; chartOptions = typeMap[state.chartInfo.ChartType] && typeMap[state.chartInfo.ChartType](Data); }else { //其他source图 const sourceTypeMap = { 2: initCommodityData, 3: initRelevanceChartData, 4: initStatisticChartData, 5: initCommodityData, 6: initFittingEquation, 7: initStatisticChartData, 8: initStatisticChartData, 9: initStatisticChartData, 10: initCrossVarietyChartData, }; chartOptions = sourceTypeMap[Data.ChartInfo.Source] && sourceTypeMap[Data.ChartInfo.Source](Data); } return chartOptions }; /* 曲线图 */ const setDefaultLineOptions = () => { const { dataList,chartInfo } = state; /* 主题样式*/ const chartTheme = chartInfo.ChartThemeStyle ? JSON.parse(chartInfo.ChartThemeStyle) : null; //拼接标题 数据列 let data = [] as any[],ydata = [] as any[],minTimeArr: number[] = [],maxTimeArr: number[] = []; let rightTwoIndex = dataList.findIndex(item => item.IsAxis ===2); // const chartData = _.cloneDeep(dataList); //有右二轴时排个序 按照左 右 右2的顺序 let chartData = dataList.some(_ =>_.IsAxis===2) ? changeEdbOrder(dataList) : _.cloneDeep(dataList); chartData.forEach((item:IDataProps ,index:number) => { //轴位置值相同的下标 let sameSideIndex = chartData.findIndex( (i:IDataProps) => i.IsAxis === item.IsAxis ); //获取对应轴的上下限 //非ETA图库图表也不设置自定义上下限,相关性和统计特征也会用到曲线图 //若chartInfo.Source为1,需在之前调用setLimitData const isETASource = state.chartInfo.Source===1 let minLimit = 0,maxLimit = 0 if(isETASource){ const limitMap = { 0:['rightMin','rightMax'], 1:['min','max'], 2:['rightTwoMin','rightTwoMax'] } if(limitMap[item.IsAxis]){ minLimit = state.chartLimit[`${limitMap[item.IsAxis][0]}`]||0 maxLimit = state.chartLimit[`${limitMap[item.IsAxis][1]}`]||0 } }else{ minLimit = dataList[sameSideIndex].MinData maxLimit = dataList[sameSideIndex].MaxData } //y轴 const textZh = item.ConvertUnit||item.Unit const textEn = textZh?item.ConvertEnUnit||item.UnitEn||item.ConvertUnit||item.Unit:'' let yItem = { ...basicYAxis, labels: { formatter: function (ctx: any) { let val = ctx.value; return sameSideIndex !== index ? '' : val; }, align: 'center', x: [0,2].includes(item.IsAxis) ? 5 : -5, style: { ...chartTheme&&chartTheme.yAxisOptions.style, } }, title: { text:language.value=='zh'?sameSideIndex !== index ? '' : `${textZh}`:textEn, style:{ ...chartTheme&&chartTheme.yAxisOptions.style }, align: 'high', rotation: 0, y: -12, x: (item.IsAxis===0 && rightTwoIndex>-1) ? -chartData[rightTwoIndex].Unit.length*12 : 0, textAlign: item.IsAxis===1 ? 'left' : 'right', reserveSpace: false }, opposite: [0,2].includes(item.IsAxis), reversed: item.IsOrder, min: Number(minLimit), max: Number(maxLimit), tickWidth: 1, visible: sameSideIndex === index, plotBands: setAxisPlotAreas(item.IsAxis), plotLines: setAxisPlotLines(item.IsAxis) } // //拼接标题 判断相同指标名称拼接来源 let dynamic_title = item.EdbName; let dynamic_arr = chartData.filter( (item: IDataProps) => dynamic_title === item.EdbName ); // 拼接配置 IsAxis左轴1 右轴0 IsOrder正序false 逆序true EdbInfoType是否是领先指标 let dynamic_tag = concatDynamicTag(item) // 英文后缀 let dynamic_tag_en = concatDynamicTag(item,'en') //数据列 let nameCh:String = dynamic_arr.length > 1 ? `${item.EdbAliasName||item.EdbName}(${item.SourceName})${dynamic_tag}` : `${item.EdbAliasName||item.EdbName}${dynamic_tag}` let nameEn:String=item.EdbNameEn?`${item.EdbNameEn}${dynamic_tag_en}`:'' let name :String=language.value == 'zh'?nameCh:nameEn //预测指标配置 let predict_params = item.EdbInfoCategoryType === 1 ? getPredictParams(item) : {}; //图表可配置的线条数就10条,第11条用第1条的配置,索引取下模 const lineIndex = chartTheme ? index%chartTheme.lineOptionList.length : index let obj = { data: [] as any[], type: (chartTheme&&chartTheme.lineOptionList[lineIndex].lineType) || 'spline', dashStyle: (chartTheme&&chartTheme.lineOptionList[lineIndex].dashStyle)||'Solid', yAxis: sameSideIndex, name, color: item.ChartColor, lineWidth: Number(item.ChartWidth)||(chartTheme&&chartTheme.lineOptionList[lineIndex].lineWidth)||1, marker:chartTheme && chartTheme.lineOptionList[lineIndex].dataMark && chartTheme.lineOptionList[lineIndex].dataMark!='none'?{ enabled:true, symbol: chartTheme.lineOptionList[lineIndex].markType || 'circle', fillColor:chartTheme.lineOptionList[lineIndex].markColor, radius: chartTheme.lineOptionList[lineIndex].markSize }:{}, ...predict_params }; item.DataList = item.DataList || [] for (let i of item.DataList) { obj.data.push([i.DataTimestamp, i.Value]); } if(item.DataList.length){ minTimeArr.push(item.DataList[0].DataTimestamp) maxTimeArr.push(item.DataList[item.DataList.length-1].DataTimestamp) } data.push(obj); ydata.push(yItem); }) // 范围为1年内 x轴显示为月/日 否则默认年/月 let xAxis:any = {}; const isLessThanOneYear:boolean = xTimeDiffer(); let minTime: number=Math.min(...minTimeArr); let maxTime=Math.max(...maxTimeArr); let step = setXaxisStep(maxTime-minTime); xAxis = { ...defaultOpts.xAxis, tickInterval: screen.value === 'phone' ? step : undefined, labels: { formatter: function (ctx: any) { return isLessThanOneYear ? Highcharts.dateFormat('%m/%d', ctx.value) : Highcharts.dateFormat('%y/%m', ctx.value); }, style: { ...chartTheme&&chartTheme.xAxisOptions.style } }, plotBands: setAxisPlotAreas(3,'datetime'), plotLines: setAxisPlotLines(3,'datetime') } let options:any = {}; options = { series: data, yAxis: ydata, xAxis }; //滚动相关性独立tooltip if(chartInfo.Source === 4) { const relevanceUnitEnMap={ '年': 'Year', '季': 'Season', '月': 'Month', '周': 'Week', '天': 'Day', } const { LeadValue,LeadUnit } = statisticFrequencyRenderData.value; options.tooltip = { formatter: function() { let that:any = this; let str = ''; if(language.value=='zh'){ str = `${Highcharts.dateFormat('%Y/%m/%d',that.x)}

相关性系数:${that.y.toFixed(4)}


领先${LeadValue+LeadUnit}

` }else{ str = `${Highcharts.dateFormat('%Y/%m/%d',that.x)}

Correlation coefficient:${that.y.toFixed(4)}


lead${LeadValue+relevanceUnitEnMap[LeadUnit]}

` } return str }, } } return options; } /* 季节图 */ const screen = ref(document.body.clientWidth < 1200 ? 'phone' : 'pc'); const setSeasonOptions = () => { const chartData = state.dataList[0]; // 农历数据需要去除第一项 在ETA1.0.5之后,除了这里 农历和公历处理逻辑一样 if(!chartData.DataList){ return } const chartDataHandle=state.chartInfo.Calendar === '农历'? chartData.DataList.filter((item:ISeasonDataItemProps, index:number) => index > 0): chartData.DataList let seasonYdata:any[] = [], seasonData:any[] = []; //获取对应轴的上下限 let minLimit = 0,maxLimit = 0 minLimit = state.chartLimit.min||0 maxLimit = state.chartLimit.max||0 /* 主题样式*/ const chartTheme = state.chartInfo.ChartThemeStyle ? JSON.parse(state.chartInfo.ChartThemeStyle) : null; // 跟颜色对应 chartTheme && (chartTheme.lineOptionList=chartTheme.lineOptionList.reverse().slice(-chartDataHandle.length)) /*处理数据列*/ for (let index in chartDataHandle) { console.log(index,'index'); let j = chartDataHandle[index] //预测指标配置 let predict_params = chartData.EdbInfoCategoryType === 1 ? getSeasonPredictParams(j.CuttingDataTimestamp) : {}; // 图表可配置的线条数就10条,第11条用第1条的配置,索引取下模 const lineIndex = chartTheme ? Number(index)%chartTheme.lineOptionList.length : index let serie_item = { data: [] as any[], type: (chartTheme&&chartTheme.lineOptionList[lineIndex].lineType) || chartData.ChartStyle, dashStyle: (chartTheme&&chartTheme.lineOptionList[lineIndex].dashStyle)||'Solid', yAxis: 0, name: j.ChartLegend, lineWidth: (chartTheme&&chartTheme.lineOptionList[lineIndex].lineWidth) || 1, marker:chartTheme && chartTheme.lineOptionList[lineIndex].dataMark && chartTheme.lineOptionList[lineIndex].dataMark!='none'?{ enabled:true, symbol: chartTheme.lineOptionList[lineIndex].markType || 'circle', fillColor:chartTheme.lineOptionList[lineIndex].markColor, radius: chartTheme.lineOptionList[lineIndex].markSize }:{}, ...predict_params }; const data_array = _.cloneDeep(j.DataList); data_array && data_array.forEach((item: IParams) => { serie_item.data.push([item.DataTimestamp, item.Value]); }); seasonData.push(serie_item); } //y轴 const textZh = chartData.ConvertUnit||chartData.Unit const textEn = textZh?chartData.ConvertEnUnit||chartData.UnitEn||chartData.ConvertUnit||chartData.Unit:'' seasonYdata = [{ ...defaultOpts.yAxis, labels: { formatter: function (ctx: any) { let val = ctx.value; return val; }, align: 'center', style: { ...chartTheme&&chartTheme.yAxisOptions.style } }, title: { text:language.value=='zh'?`${textZh}`:textEn, align: 'high', rotation: 0, y: -12, x: 0, textAlign: 'left', reserveSpace: false, style:{ ...chartTheme&&chartTheme.yAxisOptions.style }, }, max: Number(maxLimit), min: Number(minLimit), plotBands: setAxisPlotAreas(1), plotLines: setAxisPlotLines(1) }]; // 季节图x轴显示月/日 const xAxis = { ...defaultOpts.xAxis, tickInterval: screen.value === 'phone' ? 24 * 3600 * 1000 * 60 : undefined, labels: { formatter: function (ctx: any) { return Highcharts.dateFormat('%m/%d', ctx.value); }, style: { ...chartTheme&&chartTheme.xAxisOptions.style } }, plotBands: setAxisPlotAreas(3,'datetime'), plotLines: setAxisPlotLines(3,'datetime') }; const tooltip = { ...defaultOpts.tooltip, dateTimeLabelFormats: { // 时间格式化字符 day: '%m/%d', week: '%m/%d', month: '%m/%d', year: '%m/%d', }, xDateFormat: '%m/%d', } let colors = chartTheme&&chartTheme.colorsOptions.reverse(); return { colors:colors.slice(-chartDataHandle.length), series: seasonData, yAxis: seasonYdata, xAxis, tooltip }; } /* 堆叠图/组合图设置 本来和曲线图逻辑基本一致兼容下即可 为了以后便于维护和阅读还是拆开写吧 */ const setStackOrCombinChart = () => { const { dataList,chartInfo } = state; /* 主题样式*/ const chartTheme = state.chartInfo.ChartThemeStyle ? JSON.parse(state.chartInfo.ChartThemeStyle) : null; //拼接标题 数据列 let data = [] as any[],ydata = [] as any[],minTimeArr: number[] = [],maxTimeArr: number[] = []; // const chartData = _.cloneDeep(dataList); //有右二轴时排个序 按照左 右 右2的顺序 let chartData = dataList.some(_ =>_.IsAxis===2) ? changeEdbOrder(dataList) : _.cloneDeep(dataList); //支持的图表类型 const chartTypeMap: IChartType = { 3: 'areaspline', 4: 'column', 6: '' }; let chartStyle = chartTypeMap[chartInfo.ChartType]; chartData.forEach((item:IDataProps ,index:number) => { //轴位置值相同的下标 let sameSideIndex = chartData.findIndex( (i:IDataProps) => i.IsAxis === item.IsAxis ); //堆叠图的yAxis必须一致 数据列所对应的y轴 let serie_yIndex = index; if([3,4].includes(chartInfo.ChartType)) { // 类型为堆叠图时公用第一个指标y轴 serie_yIndex = 0; } else if(chartInfo.ChartType ===6 && ['areaspline','column'].includes(item.ChartStyle)) { // 组合图找第一个堆叠柱状或面积的作为公用 serie_yIndex = chartData.findIndex((i:IDataProps) => i.ChartStyle === item.ChartStyle); } //数据对应的y轴是公用轴则配置也共享 item.IsAxis = serie_yIndex === index ? item.IsAxis : chartData[serie_yIndex].IsAxis; item.IsOrder = serie_yIndex === index ? item.IsOrder : chartData[serie_yIndex].IsOrder; // 右2轴下标 let rightTwoIndex = [3,4].includes(chartInfo.ChartType) ? -1 : dataList.findIndex(item => item.IsAxis===2); //获取对应轴的上下限 let minLimit = 0,maxLimit = 0 const limitMap = { 0:['rightMin','rightMax'], 1:['min','max'], 2:['rightTwoMin','rightTwoMax'] } if(limitMap[item.IsAxis]){ minLimit = state.chartLimit[`${limitMap[item.IsAxis][0]}`]||0 maxLimit = state.chartLimit[`${limitMap[item.IsAxis][1]}`]||0 } //y轴 const textZh = item.ConvertUnit||item.Unit const textEn = textZh?item.ConvertEnUnit||item.UnitEn||item.ConvertUnit||item.Unit:'' let yItem = { ...basicYAxis, labels: { formatter: function (ctx: any) { let val = ctx.value; return sameSideIndex !== index ? '' : val; }, align: 'center', x: [0,2].includes(item.IsAxis) ? 5 : -5, style:{ ...chartTheme&&chartTheme.yAxisOptions.style }, }, title: { text:language.value=='zh'?sameSideIndex !== index ? '' : `${textZh}`:textEn, // text: null, align: 'high', rotation: 0, y: -12, x: (item.IsAxis===0 && rightTwoIndex>-1) ? -chartData[rightTwoIndex].Unit.length*12 : 0, textAlign: item.IsAxis===1 ? 'left' : 'right', reserveSpace: false, style:{ ...chartTheme&&chartTheme.yAxisOptions.style }, }, opposite: [0,2].includes(item.IsAxis), reversed: item.IsOrder, min: Number(minLimit), max: Number(maxLimit), tickWidth: sameSideIndex !== index ? 0 : 1, visible: serie_yIndex === index && sameSideIndex ===index, plotBands: setAxisPlotAreas(item.IsAxis), plotLines: setAxisPlotLines(item.IsAxis) } // //拼接标题 判断相同指标名称拼接来源 let dynamic_title = item.EdbName; let dynamic_arr = chartData.filter( (item: IDataProps) => dynamic_title === item.EdbName ); // 拼接配置 IsAxis左轴1 右轴0 IsOrder正序false 逆序true EdbInfoType是否是领先指标 let dynamic_tag = concatDynamicTag(item) // 英文后缀 let dynamic_tag_en = concatDynamicTag(item,'en') //数据列 let nameCh:String = dynamic_arr.length > 1 ? `${item.EdbAliasName||item.EdbName}(${item.SourceName})${dynamic_tag}` : `${item.EdbAliasName||item.EdbName}${dynamic_tag}` let nameEn:String=item.EdbNameEn?`${item.EdbNameEn}${dynamic_tag_en}`:'' let name :String=language.value == 'zh'?nameCh:nameEn //预测指标配置 let predict_params = item.EdbInfoCategoryType === 1 ? getPredictParams(item,chartStyle) : {}; //图表可配置的线条数就10条,第11条用第1条的配置,索引取下模 const lineIndex = chartTheme ? index%chartTheme.lineOptionList.length : index let obj = { data: [] as any[], type: chartStyle || item.ChartStyle, yAxis: serie_yIndex, name, color: item.ChartColor, lineWidth: Number(item.ChartWidth)||(chartTheme&&chartTheme.lineOptionList[lineIndex].lineWidth) || 1, fillColor: (chartInfo.ChartType === 3 || (chartInfo.ChartType === 6 && item.ChartStyle === 'areaspline')) ? item.ChartColor : undefined, zIndex: (chartInfo.ChartType === 6 && ['line','spline'].includes(item.ChartStyle)) ? 1 : 0, //防止组合图曲线被遮住 borderWidth: 1, borderColor: item.ChartColor, ...predict_params }; item.DataList = item.DataList || [] for (let i of item.DataList) { obj.data.push([i.DataTimestamp, i.Value]); } if(item.DataList.length){ minTimeArr.push(item.DataList[0].DataTimestamp) maxTimeArr.push(item.DataList[item.DataList.length-1].DataTimestamp) } data.push(obj); ydata.push(yItem); }) // 范围为1年内 x轴显示为月/日 否则默认年/月 let xAxis:any = {}; const isLessThanOneYear:boolean = xTimeDiffer(); let minTime: number=Math.min(...minTimeArr); let maxTime=Math.max(...maxTimeArr); let step = setXaxisStep(maxTime-minTime); xAxis = { ...defaultOpts.xAxis, tickInterval: screen.value === 'phone' ? step : undefined, labels: { formatter: function (ctx: any) { return isLessThanOneYear ? Highcharts.dateFormat('%m/%d', ctx.value) : Highcharts.dateFormat('%y/%m', ctx.value); }, style: { ...chartTheme&&chartTheme.xAxisOptions.style } }, plotBands: setAxisPlotAreas(3,'datetime'), plotLines: setAxisPlotLines(3,'datetime') } return { series: data, yAxis: ydata, xAxis }; } /* 散点图 第一个指标值为x轴 第二个指标为y轴*/ const setScatterChartOptions = () => { const { dataList,chartInfo } = state; /* 主题样式*/ const chartTheme = state.chartInfo.ChartThemeStyle ? JSON.parse(state.chartInfo.ChartThemeStyle) : null; // 取2个指标中日期相同的数据 const real_data: any[] = []; let tmpData_date: any = {};//用来取点对应的日期 let data1 = _.cloneDeep(dataList)[0].DataList || []; let data2 = _.cloneDeep(dataList)[1].DataList || []; data1.forEach((_item: IParams) => { data2.forEach((_item2: IParams) => { if(_item.DataTimestamp === _item2.DataTimestamp) { //日期 let itemIndex =_item.Value + "_" +_item2.Value if(tmpData_date[itemIndex]) { tmpData_date[itemIndex].push( moment(_item.DataTimestamp).format('YYYY/MM/DD')) } else { tmpData_date[itemIndex] = [moment(_item.DataTimestamp).format('YYYY/MM/DD')] } //值 real_data.push({ x: _item.Value, y: _item2.Value }) } }) }) real_data.sort((x,y) => x-y); //悬浮窗 拼接日期 原始指标名称 let tooltip = { formatter: function() { const that: any = this; return language.value=='zh'? `${ tmpData_date[that.x+'_'+that.y].length > 4 ? tmpData_date[that.x+'_'+that.y].slice(0,4).join()+'...' : tmpData_date[that.x+'_'+that.y].join() }
${dataList[0].EdbName}: ${that.x}
${dataList[1].EdbName}: ${that.y} `: `${ tmpData_date[that.x+'_'+that.y].length > 4 ? tmpData_date[that.x+'_'+that.y].slice(0,4).join()+'...' : tmpData_date[that.x+'_'+that.y].join() }
${dataList[0].EdbNameEn}: ${that.x}
${dataList[1].EdbNameEn}: ${that.y} ` } } const { IsOrder,ChartColor } = dataList[0]; const textYZh = dataList[1].ConvertUnit||dataList[1].Unit const textYEn = textYZh?dataList[1].ConvertEnUnit||dataList[1].UnitEn||dataList[1].ConvertUnit||dataList[1].Unit:'' const textX = dataList[0].ConvertUnit||dataList[0].Unit const textXEn = textX?dataList[0].ConvertEnUnit||dataList[0].UnitEn||dataList[0].ConvertUnit||dataList[0].Unit:'' //获取对应轴的上下限 let minLimit = 0,maxLimit = 0 minLimit = state.chartLimit.min||0 maxLimit = state.chartLimit.max||0 //y轴 let yAxis = { title: { text: language.value=='zh'?`${textYZh}`:textYEn, // text: null, align: 'high', rotation: 0, y: -5, x:0, textAlign: 'left', reserveSpace: false, style:{ ...chartTheme&&chartTheme.yAxisOptions.style }, }, labels: { formatter: function (ctx: any) { return ctx.value; }, align: 'center', style:{ ...chartTheme&&chartTheme.yAxisOptions.style }, }, opposite: false, reversed: IsOrder, min: Number(minLimit), max: Number(maxLimit), tickWidth: 1, tickLength: 5, lineWidth: 1, lineColor: '#bfbfbf', tickColor: '#bfbfbf', offset: 0, visible: true, gridLineWidth: 0, tickPosition: 'inside', endOnTick: false, startOnTick: false, showLastLabel: true, tickPixelInterval: 50, plotBands: setAxisPlotAreas(1), plotLines: setAxisPlotLines(1) } //数据列 let series: any = { data: [], type: 'scatter', name: language.value == 'zh'?`${chartInfo.ChartName}${IsOrder ? '(逆序)' : ''}`:`${chartInfo.ChartNameEn}${IsOrder ? '(reserve)' : ''}`, color: ChartColor, chartType: 'linear', marker: { radius: (chartTheme&&chartTheme.lineOptionList[0].radius)||5, }, } real_data.forEach(_ => { series.data.push([_.x,_.y]) }) return { title: { text:'' }, series: [ series ], yAxis, xAxis: { ...scatterXAxis, title: { text: language.value=='zh'?`${textX}`:textXEn, align: 'high', rotation: 0, x: 0, offset: 20, style: { ...chartTheme&&chartTheme.xAxisOptions.style }, }, labels: { style: { ...chartTheme&&chartTheme.xAxisOptions.style } }, plotBands: setAxisPlotAreas(3), plotLines: setAxisPlotLines(3) }, tooltip } } /* 奇怪柱状图 获取图表详情后赋值奇怪柱状图数据 */ const barChartRenderData = ref({}) const initBarData = (data: { XEdbIdValue: number[]; YDataList: any; EdbInfoList: any; ChartInfo: any; }) => { const { XEdbIdValue,YDataList,EdbInfoList,ChartInfo } = data; barChartRenderData.value = { barDateList: YDataList, barXIdData: XEdbIdValue, barEdbData: EdbInfoList, chartInfo: ChartInfo } state.chartLimit = { min: Number(ChartInfo.LeftMin), max: Number(ChartInfo.LeftMax), } return setBarChart() } const setBarChart = () => { const {barDateList,barXIdData,barEdbData,chartInfo} = barChartRenderData.value; const { chartLimit } = state; let seriesData: { data: any; type: string; yAxis: number; name: any; color: any; chartType: string; }[] = []; const data = _.cloneDeep(barDateList); /* 主题样式*/ const chartTheme = chartInfo.ChartThemeStyle ? JSON.parse(chartInfo.ChartThemeStyle) : null; let categories = language.value==='zh' ? barXIdData.map((_:number) => barEdbData.find((edb: { EdbInfoId: number; }) => edb.EdbInfoId===_).EdbAliasName) : barXIdData.map((_:number) => barEdbData.find((edb: { EdbInfoId: number; }) => edb.EdbInfoId===_).EdbNameEn) //x轴 let xAxis = { ...scatterXAxis, categories, tickWidth: 1, labels: { style: { ...chartTheme&&chartTheme.xAxisOptions.style } } } const { max,min } = chartLimit; console.log(max,min) //y轴 let yAxis = { ...basicYAxis, title: { text: language.value==='zh' ? chartInfo.Unit : (chartInfo.UnitEn||chartInfo.Unit), align: 'high', rotation: 0, y: -12, x:0, textAlign: 'left', reserveSpace: false, style:{ ...chartTheme&&chartTheme.yAxisOptions.style }, }, labels: { formatter: function (ctx:any) { let val = ctx.value; return val; }, align: 'center', style:{ ...chartTheme&&chartTheme.yAxisOptions.style }, }, min: Number(min), max: Number(max), opposite: false, tickWidth: 1, plotBands: setAxisPlotAreas(1), plotLines: setAxisPlotLines(1) } //数据列 data.forEach((item: { Value: number; Name: string; Date: string; Color: string; }) => { let serie_item = { data: item.Value, type: 'column', yAxis: 0, name: language.value==='zh' ? (item.Name || item.Date) : item.Date, color: item.Color, chartType: 'linear' }; seriesData.push(serie_item) }) return { title: { text:'' }, plotOptions: { column:{ stacking: null, }, }, series: seriesData, yAxis: [ yAxis ], xAxis } } /* 散点截面图 */ const sectionScatterRenderData = ref({}); const initSectionScatterData = (data: { DataResp:any,EdbInfoList:any[] }) => { sectionScatterRenderData.value = data.DataResp; state.chartLimit.min=Number(data.DataResp.YMinValue) state.chartLimit.max=Number(data.DataResp.YMaxValue) state.chartLimit.x_min=Number(data.DataResp.XMinValue) state.chartLimit.x_max=Number(data.DataResp.XMaxValue) //校验英文名称是否完整 // if(routeQuery.value.fromPage === 'en' && language.value==='en') { // const { XNameEn,XUnitNameEn,YNameEn,YUnitNameEn } = data.DataResp; // // DataList可能是null // const DataList = data.DataResp.DataList||[] // let isAllEn = true; // if(!XNameEn || !XUnitNameEn || !YNameEn || !YUnitNameEn) isAllEn = false; // if(DataList.some(_ => !_.NameEn)) isAllEn = false; // console.log(isAllEn) // language.value = isAllEn ? 'en' : 'zh'; // } return setSectionScatterChart() } /* 截面散点图设置 sectionScatterData */ const setSectionScatterChart = () => { const { chartLimit } = state; const { DataList,XName,XNameEn,YName,YNameEn } = sectionScatterRenderData.value; /* 主题样式*/ const chartTheme = state.chartInfo.ChartThemeStyle ? JSON.parse(state.chartInfo.ChartThemeStyle) : null; if(!DataList){ return } const { min,max,x_min,x_max } = chartLimit; //y轴 let yAxis = { ...basicYAxis, title: { text: language.value === 'zh' ? YName : YNameEn, align: 'middle', style:{ ...chartTheme&&chartTheme.yAxisOptions.style }, }, labels: { style:{ ...chartTheme&&chartTheme.yAxisOptions.style }, }, opposite: false, reversed: false, min: Number(min), max: Number(max), tickWidth: 1, plotBands: setAxisPlotAreas(1), plotLines: setAxisPlotLines(1) } //x轴 let xAxis = { ...scatterXAxis, title: { text: language.value === 'zh' ? XName : XNameEn, align: 'middle', style: { ...chartTheme&&chartTheme.xAxisOptions.style }, }, labels: { style:{ ...chartTheme&&chartTheme.xAxisOptions.style }, }, min: Number(x_min), max: Number(x_max), plotBands: setAxisPlotAreas(3), plotLines: setAxisPlotLines(3) } //数据列 let series: any[] = []; DataList.forEach((item:any,index:number) => { // 图表可配置的线条数就10条,第11条用第1条的配置,索引取下模 const lineIndex = chartTheme ? index%chartTheme.lineOptionList.length : index //数据列 let series_item = { data: [] as any[], type: 'scatter', name: language.value === 'zh' ? item.Name : item.NameEn, color: item.Color, lineWidth: 0, chartType: 'linear', zIndex:1, marker: { radius: (chartTheme&&chartTheme.lineOptionList[lineIndex].radius)||5, }, visible: true } item.EdbInfoList.forEach(_ => { series_item.data.push({ x: _.XValue, y: _.YValue, dataLabels: { enabled: _.IsShow, allowOverlap: true, align: 'left', format: language.value === 'zh' ? _.Name : _.NameEn } }) }) series.push(series_item); //趋势线 if(item.ShowTrendLine) { let trend_data = item.TrendLimitData.map((_,_index) => ( _index === item.TrendLimitData.length-1 ? { x: _.X, y: _.Y, dataLabels: { enabled: item.ShowRSquare || item.ShowFitEquation, align: 'left', color: '#666', x: 20, y: 30, zIndex: 9, allowOverlap: true, formatter: function(){ let tag = ''; item.ShowRSquare && item.ShowFitEquation ? tag =`${item.TrendLine}
R²=${item.RSquare}` : item.ShowRSquare && !item.ShowFitEquation ? tag =`R²=${item.RSquare}` : item.ShowFitEquation && !item.ShowRSquare ? tag =`${item.TrendLine}` : '' return tag } } } : { x: _.X, y: _.Y, } )) let trend_item = { data: trend_data, type: 'spline', linkedTo: ':previous', color: item.Color, lineWidth: 1, chartType: 'linear', enableMouseTracking: false, dashStyle:'Dash', zIndex: 2, visible: true, marker: { enabled: false } } series.push(trend_item) } }) let tooltip = { formatter: function() { const that:any = this; let str = ''; if(language.value === 'zh') { let series_obj = DataList.find(_ => _.Name === that.series.name); let ponit_obj = series_obj.EdbInfoList.find(_ => _.XValue ===that.x && _.YValue===that.y); str=`${ ponit_obj.Name }`; str += `
\u25CF${ponit_obj.XName}: ${that.x} ${ponit_obj.XDate}
`; str += `\u25CF${ponit_obj.YName}: ${that.y} ${ponit_obj.YDate}`; }else { let series_obj = DataList.find(_ => _.NameEn === that.series.name); let ponit_obj = series_obj.EdbInfoList.find(_ => _.XValue ===that.x && _.YValue===that.y); str=`${ ponit_obj.NameEn }`; str += `
\u25CF${ponit_obj.XNameEn}: ${that.x} ${ponit_obj.XDate}
`; str += `\u25CF${ponit_obj.YNameEn}: ${that.y} ${ponit_obj.YDate}`; } return str } } return { title: { text:'' }, series, yAxis: [yAxis], xAxis, tooltip } } /* 商品价格曲线/利润曲线 */ const commodityChartRenderData = ref({}) const initCommodityData = (data: { XDataList: any[]; YDataList: any; EdbInfoList: any; ChartInfo: any; DataResp?:any }) => { const { XDataList,YDataList,EdbInfoList,ChartInfo,DataResp } = data; commodityChartRenderData.value = { commodityEdbList: EdbInfoList, commodityChartData: ChartInfo.Source===5?DataResp.YDataList:YDataList, commodityXData: ChartInfo.Source===5?DataResp.XDataList:XDataList } if(ChartInfo.Source===5) { state.chartInfo = { ...state.chartInfo, ProfitName: DataResp.ProfitName, ProfitNameEn: DataResp.ProfitNameEn } } state.chartLimit = { min: Number(ChartInfo.LeftMin), max: Number(ChartInfo.LeftMax) } return setCommodityChart(); } /* 商品价格曲线设置 绘图逻辑同奇怪柱形图*/ const setCommodityChart = () => { const {chartLimit,chartInfo} = state; const { commodityChartData,commodityXData,commodityEdbList } = commodityChartRenderData.value; let seriesData:any[] = []; const data = _.cloneDeep(commodityChartData); /* 主题样式*/ const chartTheme = chartInfo.ChartThemeStyle ? JSON.parse(chartInfo.ChartThemeStyle) : null; //x轴 let xAxis = { ...scatterXAxis, categories: commodityXData.map(_ => language.value === 'zh' ? _.Name:_.NameEn), tickWidth: 1, labels: { style: { ...chartTheme&&chartTheme.xAxisOptions.style } } } const { max,min } = chartLimit; //y轴 let yAxis = { ...basicYAxis, title: { text: language.value === 'zh' ? commodityEdbList[0].Unit : commodityEdbList[0].UnitEn, align: 'high', rotation: 0, y: -12, textAlign: 'left', reserveSpace: false, style:{ ...chartTheme&&chartTheme.yAxisOptions.style }, }, labels: { formatter: function (ctx:any) { let val = ctx.value; return val; }, align: 'center', style:{ ...chartTheme&&chartTheme.yAxisOptions.style }, }, min: Number(min), max: Number(max), opposite: false, tickWidth: 1, } //数据列 data.forEach((item: { Value: number[]; Name: string; Date: string; Color: string;NameEn: string,XEdbInfoIdList: number[],NoDataEdbList: number[] },index:number) => { //处理首或/尾全是无效数据的以null填充 let filterData = filterInvalidData(item) // 图表可配置的线条数就10条,第11条用第1条的配置,索引取下模 const lineIndex = chartTheme ? index%chartTheme.lineOptionList.length : index let serie_item = { data: filterData, type: (chartTheme&&chartTheme.lineOptionList[lineIndex].lineType) || 'spline', dashStyle: (chartTheme&&chartTheme.lineOptionList[lineIndex].dashStyle)||'Solid', yAxis: 0, name: language.value === 'zh' ? item.Name : item.NameEn, color: item.Color, chartType: 'linear', lineWidth: (chartTheme&&chartTheme.lineOptionList[lineIndex].lineWidth) || 3, marker: { enabled: false } }; seriesData.push(serie_item) }) //tooltip let tooltip = { formatter: function() { const ctx: any = this; let str: string = ''; if(language.value === 'zh') { ctx.points.forEach((item: { series:{name: string},y: number,color: string }) => { let obj_item = data.find((_:any) => _.Name === item.series.name); let index = commodityXData.findIndex(_ => _.Name === ctx.x); //合约显示 let haveContract = obj_item.XEdbInfoIdList[index]; if(haveContract) { // 利润曲线指标名 let edb_name = chartInfo.Source === 5 ? (index === 0 ? obj_item.NameList[index] : `${chartInfo.ProfitName}(${obj_item.NameList[index]})`) : commodityEdbList.find(_ => _.EdbInfoId === obj_item.XEdbInfoIdList[index]).EdbName; str+=`${ edb_name }` if(!obj_item.NoDataEdbList.includes(obj_item.XEdbInfoIdList[index])) { str += `
\u25CF${obj_item.Date}: ${item.y}
` }else { str += `
\u25CF${obj_item.Date}: 无
` } } }) }else { ctx.points.forEach((item: { series:{name: string},y: number,color: string }) => { let obj_item = data.find((_: any) => _.NameEn === item.series.name); let index = commodityXData.findIndex(_ => _.NameEn === ctx.x); let haveContract = obj_item.XEdbInfoIdList[index]; if(haveContract) { let edb_name = chartInfo.Source === 5 ? (index === 0 ? obj_item.NameList[index] : `${chartInfo.ProfitNameEn}(${obj_item.NameList[index]})`) : commodityEdbList.find(_ => _.EdbInfoId === obj_item.XEdbInfoIdList[index]).EdbNameEn; str+=`${ edb_name }` if(!obj_item.NoDataEdbList.includes(obj_item.XEdbInfoIdList[index])) { str += `
\u25CF${obj_item.Date}: ${item.y}
` }else { str += `
\u25CF${obj_item.Date}: 无
` } } }) } return str || '无合约' }, shared: true } return { title: { text:'' }, series: seriesData, yAxis: [ yAxis ], xAxis, tooltip } }; /* 处理无效数据为null */ const filterInvalidData = (item:{ Value: number[]; Name: string; Date: string; Color: string;NameEn: string,XEdbInfoIdList: number[],NoDataEdbList: number[] })=> { let validateArr = item.XEdbInfoIdList.filter(_ => _&&!item.NoDataEdbList.includes(_)); let first_index = item.XEdbInfoIdList.findIndex(_ => _ === validateArr[0]); let last_index = item.XEdbInfoIdList.findIndex(_ => _ === validateArr[validateArr.length-1]); console.log('first_index',first_index) console.log('last_index',last_index) let arr = item.Value.map((item,index:number) => { if(index < first_index || index > last_index) { return null }else { return item } }) return arr; } /* 相关性图表 */ const initRelevanceChartData=(data)=>{ /* 主题样式*/ const chartTheme = state.chartInfo.ChartThemeStyle ? JSON.parse(state.chartInfo.ChartThemeStyle) : null; const relevanceUnitEnMap={ '年': 'Year', '季': 'Season', '月': 'Month', '周': 'Week', '天': 'Day', } // 处理X轴 let xAxis={ categories: data.ChartInfo.Source===3 ? data.XEdbIdValue : data.DataResp.XDateTimeValue, tickWidth: 1, title: { text: data.ChartInfo.Source===3 ? (language.value=='zh'?`期数(${data.CorrelationChartInfo.LeadUnit})`:`stage(${relevanceUnitEnMap[data.CorrelationChartInfo.LeadUnit]})`):null, align: 'high', rotation: 0, style: { ...chartTheme&&chartTheme.xAxisOptions.style } }, labels: { style: { ...chartTheme&&chartTheme.xAxisOptions.style } }, tickInterval: 1, offset:0, tickmarkPlacement:'on' } // 处理Y轴 let yAxis={ ...basicYAxis, title: { text: language.value=='zh'?'相关性系数':'Correlation coefficient', align: 'high', rotation: 0, y: -12, textAlign: 'left', reserveSpace: false, style:{ ...chartTheme&&chartTheme.yAxisOptions.style }, }, labels: { formatter: function (ctx) { let val = ctx.value; return val; }, align: 'center', style:{ ...chartTheme&&chartTheme.yAxisOptions.style }, }, opposite: false, tickWidth: 1, } //处理series let seriesData:any[]=[] data.YDataList.forEach((item:any,index:number)=>{ // 图表可配置的线条数就10条,第11条用第1条的配置,索引取下模 const lineIndex = chartTheme ? index%chartTheme.lineOptionList.length : index let serie_item = { data: item.Value, type: (chartTheme&&chartTheme.lineOptionList[lineIndex].lineType) || 'spline', dashStyle: (chartTheme&&chartTheme.lineOptionList[lineIndex].dashStyle)||'Solid', yAxis: 0, name: language.value=='zh'?data.ChartInfo.ChartName:data.ChartInfo.ChartNameEn, color: item.Color, chartType: 'linear', lineWidth: (chartTheme&&chartTheme.lineOptionList[lineIndex].lineWidth) || 3, marker: { enabled: false } }; seriesData.push(serie_item) }) const { LeadValue,LeadUnit } = data.CorrelationChartInfo; let tooltip = { formatter: function() { const that:any = this; let str=''; if(language.value=='zh'){ str = `

相关性系数:${that.y.toFixed(4)}


领先${ data.ChartInfo.Source===3 ?that.x+'期' : LeadValue+LeadUnit}

` }else{ str = `

Correlation coefficient:${that.y.toFixed(4)}


lead${ data.ChartInfo.Source===3 ? that.x+'stage' : LeadValue+relevanceUnitEnMap[LeadUnit]}

` } return str }, } return { isRelevanceChart:data.ChartInfo.Source===3, title: { text:'', }, series: seriesData, yAxis: [yAxis] , xAxis:xAxis, tooltip } } /* 统计特征 标准差 百分比 频率 滚动相关性*/ const statisticFrequencyRenderData = ref({}); const initStatisticChartData = (data: { DataResp:any,ChartInfo:any,CorrelationChartInfo:any }) => { if(data.ChartInfo.Source === 9) { //频率图 statisticFrequencyRenderData.value = data.DataResp; return setStatisticFrequency(); }else { state.dataList = [data.DataResp]; if(data.ChartInfo.Source === 4) statisticFrequencyRenderData.value = data.CorrelationChartInfo; return setDefaultLineOptions(); } } /* 统计频率图 */ const setStatisticFrequency = () => { const { DataList,LeftMaxValue,LeftMinValue,RightMaxValue,RightMinValue } = statisticFrequencyRenderData.value if(!DataList){ return } /* 主题样式*/ const chartTheme = state.chartInfo.ChartThemeStyle ? JSON.parse(state.chartInfo.ChartThemeStyle) : null; let xAxis = { ...scatterXAxis, tickWidth: 1, labels: { style: { ...chartTheme&&chartTheme.xAxisOptions.style } } } //y和系列 let yAxis:any[] = [],series:any[] = []; DataList.forEach((item,index) => { let y_item = { ...basicYAxis, title: { text: language.value === 'zh' ? item.Unit : item.UnitEn, align: 'high', rotation: 0, y: -12, reserveSpace: false, style:{ ...chartTheme&&chartTheme.yAxisOptions.style }, }, labels: { style:{ ...chartTheme&&chartTheme.yAxisOptions.style }, }, opposite: item.IsAxis===1?false:true, min: index===0? Number(LeftMinValue):Number(RightMinValue), max: index===0? Number(LeftMaxValue):Number(RightMaxValue), tickWidth: 1, } // 图表可配置的线条数就10条,第11条用第1条的配置,索引取下模 const lineIndex = chartTheme ? index%chartTheme.lineOptionList.length : index let series_item = { data: item.Value.map(_ =>[_.X,_.Y]), dashStyle: (chartTheme&&chartTheme.lineOptionList[lineIndex].dashStyle)||'Solid', type: (chartTheme&&chartTheme.lineOptionList[lineIndex].lineType) || 'spline', yAxis: index, name: language.value === 'zh' ? item.Name : item.NameEn, color: item.Color, lineWidth: (chartTheme&&chartTheme.lineOptionList[lineIndex].lineWidth)||3, chartType: 'linear', zIndex:1 } series.push(series_item); yAxis.push(y_item) }) let tooltip = { formatter: function() { let that:any = this; let xList = DataList[0].Value.map(_ =>_.X); let step = xList[1]-xList[0]; let data_interval = `[${Number(that.x).toFixed(2)},${Number(that.x+step).toFixed(2)}]`; let str=`${ data_interval }`; that.points.forEach(item => { str += `
\u25CF${item.series.name}: ${item.y}%` }) return str }, shared: true } return { title: { text:'' }, tooltip, series, yAxis, xAxis } } /* 拟合方程曲线 曲线图绘图逻辑*/ const initFittingEquation = (data) => { state.dataList = [data.DataResp]; return setDefaultLineOptions(); } /* 跨品种分析 */ const crossVarietyChartRenderData = ref({}) const initCrossVarietyChartData = (data:{ DataResp:any,ChartInfo:any,EdbInfoList:any }) => { state.dataList = data.EdbInfoList; crossVarietyChartRenderData.value = data.DataResp; /* 历史数据chartInfo里全是空 兼容下历史数据不崩 */ state.chartLimit.min=data.ChartInfo.LeftMin?Number(data.ChartInfo.LeftMin):Number(data.DataResp.YMinValue) state.chartLimit.max=data.ChartInfo.LeftMax?Number(data.ChartInfo.LeftMax):Number(data.DataResp.YMaxValue) state.chartLimit.x_min=data.ChartInfo.XMin?Number(data.ChartInfo.XMin):Number(data.DataResp.XMinValue) state.chartLimit.x_max=data.ChartInfo.XMax?Number(data.ChartInfo.XMax):Number(data.DataResp.XMaxValue) //二次校验英文配置是否完整 // if(routeQuery.value.fromPage === 'en' && language.value==='en') { // const { XNameEn,YNameEn,DataList } = data.DataResp; // let isAllEn = true; // let haveOneNoEn = DataList.some(_ => !_.NameEn); // if(!XNameEn || !YNameEn || haveOneNoEn) isAllEn = false; // language.value = isAllEn ? 'en' : 'zh'; // } return setCrossVarietyChart() } /* 跨品种分析 */ const setCrossVarietyChart = () => { const { chartLimit,dataList,chartInfo } = state; /* 主题样式*/ const chartTheme = chartInfo.ChartThemeStyle ? JSON.parse(chartInfo.ChartThemeStyle) : null; const { min,max,x_min,x_max } = chartLimit; const { DataList,XName,YName,XNameEn,YNameEn } = crossVarietyChartRenderData.value; //y轴 let yAxis = { ...basicYAxis, title: { text: language.value === 'zh' ? YName : YNameEn, align: 'middle', style: { ...chartTheme&&chartTheme.xAxisOptions.style } }, labels: { style: { ...chartTheme&&chartTheme.xAxisOptions.style } }, opposite: false, reversed: false, min: Number(min), max: Number(max), tickWidth: 1, } // x轴 let xAxis = { ...scatterXAxis, title: { text: language.value === 'zh' ? XName : XNameEn, align: 'middle', style: { ...chartTheme&&chartTheme.xAxisOptions.style } }, labels: { style: { ...chartTheme&&chartTheme.xAxisOptions.style } }, min: Number(x_min), max: Number(x_max), } //数据列 let series:any[] = []; const tagMap = { //标签对应文字 1: '最新', 3: 'Fix' } DataList.forEach(item => { //数据列 let series_item = { data: [] as any[], type: 'scatter', name: language.value === 'zh' ? item.Name : item.NameEn, color: item.Color, lineWidth: 0, chartType: 'linear', zIndex:1 } item.CoordinatePointData.forEach(_ => { series_item.data.push({ x: _.X, y: _.Y, dataLabels: { enabled: _.ShowTips===1, allowOverlap: true, align: 'left', format: tagMap[_.DateType] || `-${_.DaysAgo}D`, } }) }) series.push(series_item); }) let tooltip = { formatter: function() { let that:any = this; let str = ''; if(language.value === 'zh') { let series_obj = DataList.find(_ => _.Name === that.series.name); let ponit_obj = series_obj.CoordinatePointData.find(_ => _.X ===that.x && _.Y===that.y); let xEdbInfo = dataList.find(_ => _.EdbInfoId===ponit_obj.XEdbInfoId); let yEdbInfo = dataList.find(_ => _.EdbInfoId===ponit_obj.YEdbInfoId); str=`${ that.series.name }`; str += `
\u25CF${xEdbInfo.EdbName}: ${that.x} ${ponit_obj.XDate}
`; str += `\u25CF${yEdbInfo.EdbName}: ${that.y} ${ponit_obj.YDate}`; }else { let series_obj = DataList.find(_ => _.NameEn === that.series.name); let ponit_obj = series_obj.CoordinatePointData.find(_ => _.X ===that.x && _.Y===that.y); let xEdbInfo = dataList.find(_ => _.EdbInfoId===ponit_obj.XEdbInfoId); let yEdbInfo = dataList.find(_ => _.EdbInfoId===ponit_obj.YEdbInfoId); str=`${ that.series.name }`; str += `
\u25CF${xEdbInfo.EdbNameEn}: ${that.x} ${ponit_obj.XDate}
`; str += `\u25CF${yEdbInfo.EdbNameEn}: ${that.y} ${ponit_obj.YDate}`; } return str } } return { title: { text:'' }, series, yAxis: [yAxis], xAxis, tooltip } } /* 雷达图 */ const radarChartRenderData = ref({}) const initRadarData = (data) => { const { DataResp,EdbInfoList,ChartInfo } = data; radarChartRenderData.value = { YDataList: DataResp.YDataList, XDataList: EdbInfoList.filter(_ => DataResp.XEdbIdValue.includes(_.EdbInfoId)) } state.chartLimit.min=Number(ChartInfo.LeftMin) state.chartLimit.max=Number(ChartInfo.LeftMax) return setRadarChart(); } const setRadarChart = () => { const { YDataList,XDataList } = radarChartRenderData.value; const { chartLimit,dataList,chartInfo } = state; /* 主题样式*/ const chartTheme = chartInfo.ChartThemeStyle ? JSON.parse(chartInfo.ChartThemeStyle) : null; let categories = language.value==='zh' ? XDataList.map(_ => _.EdbAliasName||_.EdbName) : XDataList.map(_ => _.EdbNameEn) //x轴 let xAxis = { lineWidth: 0, tickLength: 0, tickmarkPlacement: 'on', categories, labels: { allowOverlap: true, // align:'center', distance: 20, style: { ...chartTheme&&chartTheme.xAxisOptions.style } } } //y轴 const { max,min } = chartLimit; let yAxis = [{ gridLineInterpolation: 'polygon', gridLineWidth: 1, lineWidth: 0, endOnTick: false, startOnTick: false, showLastLabel: true, // tickAmount:4, title: { text: language.value==='zh' ? chartInfo.Unit : chartInfo.UnitEn, align: 'high', rotation: 0, y: 5, x:10, textAlign: 'left', reserveSpace: false, style:{ ...chartTheme&&chartTheme.yAxisOptions.style }, }, labels: { allowOverlap: true, style:{ ...chartTheme&&chartTheme.yAxisOptions.style } }, min: Number(min), max: Number(max), }] //系列 let series:any[] = []; YDataList.forEach((item,index) => { // 图表可配置的线条数就10条,第11条用第1条的配置,索引取下模 const lineIndex = chartTheme ? index%chartTheme.lineOptionList.length : index let serie_item = { data: item.Value, pointPlacement: 'on', type: (chartTheme&&chartTheme.lineOptionList[lineIndex].lineType) || 'line', dashStyle: (chartTheme&&chartTheme.lineOptionList[lineIndex].dashStyle)||'Solid', yAxis: 0, name: language.value==='zh' ? (item.Name || item.Date) : item.Date, color: item.Color, lineWidth: (chartTheme&&chartTheme.lineOptionList[lineIndex].lineWidth) || 1, chartType: 'linear' }; series.push(serie_item) }) return { chart: { ...defaultOpts.chart, ...chartTheme.drawOption, spacing: [2,10,2,10], polar:true, }, title: { text:'' }, pane: { size: '80%' }, series, yAxis, xAxis } } // 曲线图x轴显示计算年限差 >1年 显示年/月 <=1 显示月/日 const xTimeDiffer = () => { const end_date = state.chartInfo.DateType === 5 ? state.chartInfo.EndDate : state.chartInfo.DateType === 6 ? new Date() : ''; //年限差 const year_differ = moment(end_date).diff( moment(state.chartInfo.StartDate), 'years', true ); // console.log(year_differ) if ([5, 6].includes(state.chartInfo.DateType) && year_differ <= 1) { return true } else { return false } } /* 设置x轴步长 刻度数量多一点 */ const setXaxisStep = (timestamp: number) => { return timestamp / 6; } /* 指标顺序调整 IsAxis: 0右轴 1左轴 2右2*/ const changeEdbOrder = (data: any[]) => { // 左轴指标 let left_edbs = data.filter(_ => _.IsAxis===1); //右轴指标 let right_edbs = data.filter(_ => !_.IsAxis); // 右2轴指标 let right_two_edbs = data.filter(_ => _.IsAxis === 2); // 按 左 右 右2顺序排列 return [left_edbs,right_edbs,right_two_edbs].flat(Infinity); } /* 拼接动态的指标名称小标签 */ const concatDynamicTag = ( { IsAxis, IsOrder, EdbInfoType, LeadValue, LeadUnit }: IDataProps, lang: String = 'zh' ): string => { // IsAxis左轴1 右轴0 2右2轴 //IsOrder正序false 逆序true //EdbInfoType是否是领先指标 const axisLabelMap: any = lang == 'zh' ? { 0: "右轴", 2: "右2轴", } : { 0: "RHS", 2: "2-RHS", }; const orderLabelMap: any = lang == 'zh' ? { 1: "逆序", } : { 1: "REV", }; const edbInfoMap: any = lang == 'zh' ? { 0: "领先", } : { 0: "Lead", }; const leadUnitEnMap: any = { 年: "Y", 季: "Q", 月: "M", 周: "W", 天: "D", }; //英文领先单位转换 const edbLeadUnit = lang == 'zh' ? LeadUnit : leadUnitEnMap[LeadUnit]; let axis_tag = axisLabelMap[IsAxis] || ""; //逆序拼接 let order_tag = orderLabelMap[Number(IsOrder)] ? `${axis_tag ? "," : ""}${orderLabelMap[Number(IsOrder)]}` : ""; //领先拼接 let edb_tag = edbInfoMap[EdbInfoType] ? `${axis_tag || order_tag ? "," : ""}${ edbInfoMap[EdbInfoType] } ${LeadValue}${edbLeadUnit}` : ""; return axis_tag || order_tag || edb_tag ? `(${axis_tag}${order_tag}${edb_tag})` : ""; }; /* 预测配置 分区 */ const getPredictParams = ( { LatestDate, MoveLatestDate, PredictChartColor, ChartStyle }: IDataProps, chartStyle = "" ) => { return { zoneAxis: "x", zones: [ { value: new Date(MoveLatestDate || LatestDate).getTime() + 1, }, { dashStyle: "ShortDot", color: ChartStyle === "column" || chartStyle === "column" ? "transparent" : PredictChartColor, }, ], }; }; /* 季节图预测数据 年份=分割点年份做分割 年份>分割点年份全为预测 */ const getSeasonPredictParams = (timestamp: number) => { return timestamp ? { zoneAxis: "x", zones: [ { value: new Date(timestamp).getTime() + 1, }, { dashStyle: "ShortDot", }, ], } : {}; }; /* 处理轴的标识线结构 在指定轴位置上拼接标识线 0:右轴 1:左轴 2:右2轴 x轴固定3 axisType表示x轴类型 处理时间轴的值 datetime/null */ const setAxisPlotLines = (axis: number, axisType: any = null) => { const { MarkersLines, ChartType } = state.chartInfo; if (!MarkersLines) return []; let markerLines = JSON.parse(MarkersLines); let arr = markerLines.filter((_) => _.isShow && _.axis === axis); let plotLines = arr.map((_) => { //是否是x时间轴 let isXDateAxis = axis === 3 && axisType === "datetime"; let markerValue:number; if (isXDateAxis) { //季节图x轴额外拼个年份 let nowYear = ChartType===2 ? new Date(state.dataList[0].DataList[1].DataList [0].DataTimestamp).getFullYear() : ''; markerValue = ChartType === 2 ? new Date(`${nowYear}-${_.value}`).getTime() : new Date(_.value).getTime(); } else { markerValue = Number(_.value); } return { value: markerValue, dashStyle: _.dashStyle, width: Number(_.lineWidth), color: _.color, label: { text: _.text || "", verticalAlign: _.textPosition, style: { color: _.textColor, fontSize: _.textFontSize, }, }, }; }); return plotLines; }; /* 处理标识区拼接 axisType表示x轴类型处理时间轴的值 datetime/null */ const setAxisPlotAreas = (axis: number, axisType: any = null) => { const { MarkersAreas, ChartType } = state.chartInfo; if (!MarkersAreas) return []; let markerAreas = JSON.parse(MarkersAreas); let arr = markerAreas.filter((_) => _.isShow && _.axis === axis); let plotBands = arr.map((_) => { //是否是x时间轴 let isXDateAxis = axis === 3 && axisType === "datetime"; let fromMarkerValue:number, toMarkerValue:number; if (isXDateAxis) { //季节图x轴额外拼个年份 let nowYear = ChartType===2 ? new Date(state.dataList[0].DataList[1].DataList [0].DataTimestamp).getFullYear() : ''; fromMarkerValue = ChartType === 2 ? new Date(`${nowYear}-${_.fromValue}`).getTime() : new Date(_.fromValue).getTime(); toMarkerValue = ChartType === 2 ? new Date(`${nowYear}-${_.toValue}`).getTime() : new Date(_.toValue).getTime(); } else { fromMarkerValue = Number(_.fromValue); toMarkerValue = Number(_.toValue); } //默认label有些偏移 重新归正下 let positionMapValue = { 'top': 12, 'middle': 0, 'bottom': -10 } return { from: fromMarkerValue, to: toMarkerValue, color: _.color, label: { text: _.text || "", verticalAlign: _.textPosition, y: positionMapValue[_.textPosition], style: { color: _.textColor, fontSize: _.textFontSize, }, }, }; }); return plotBands; }; /* ----自定义上下限相关--- */ const setLimitData = (tableData:any=[])=>{ const { //左右轴极值字段 LeftMin=0,LeftMax=0, RightMin=0,RightMax=0, Right2Min=0,Right2Max=0, MinMaxSave } = state.chartInfo if(MinMaxSave){ state.chartLimit.min = Number(LeftMin) state.chartLimit.max = Number(LeftMax) state.chartLimit.rightMin = Number(RightMin) state.chartLimit.rightMax = Number(RightMax) state.chartLimit.rightTwoMin = Number(Right2Min) state.chartLimit.rightTwoMax = Number(Right2Max) //若用户修改过,则检测轴的上下限是否为空,若为空,则需要计算对应轴的上下限 checkLimit(tableData) }else{ calcYAxislimit(tableData) } } const checkLimit = (tableData:any=[])=>{ //散点图单独处理 if(state.chartInfo.ChartType===5){ if(tableData[1]){ if(Number(state.chartLimit.min)===0&&Number(state.chartLimit.max)===0){ state.chartLimit.min = tableData[1].MinData state.chartLimit.max = tableData[1].MaxData } } return } //若轴的上下限均为0,则不管用户有没有修改过,都重新赋值 if(Number(state.chartLimit.min)===0&&Number(state.chartLimit.max)===0){ const leftData = tableData.filter((i:IDataProps)=>i.IsAxis===1).map((i:IDataProps)=>[Number(i.MinData),Number(i.MaxData)]) if(leftData.length){ const {Max,Min} = calcLimit(leftData.flat()) state.chartLimit.min = Min state.chartLimit.max = Max }else{ state.chartLimit.min = 0 state.chartLimit.max = 0 } } if(Number(state.chartLimit.rightMin)===0&&Number(state.chartLimit.rightMax)===0){ const rightData = tableData.filter((i:IDataProps) => !i.IsAxis).map((i:IDataProps)=>[Number(i.MinData),Number(i.MaxData)]) if(rightData.length){ const {Max,Min} = calcLimit(rightData.flat()) state.chartLimit.rightMin = Min state.chartLimit.rightMax = Max }else{ state.chartLimit.rightMin = 0 state.chartLimit.rightMax = 0 } } if(Number(state.chartLimit.rightTwoMin)===0&&Number(state.chartLimit.rightTwoMax)===0){ const rightTwoData = tableData.filter((i:IDataProps)=>i.IsAxis===2).map((i:IDataProps)=>[Number(i.MinData),Number(i.MaxData)]) if(rightTwoData.length){ const {Max,Min} = calcLimit(rightTwoData.flat()) state.chartLimit.rightTwoMin = Min state.chartLimit.rightTwoMax = Max }else{ state.chartLimit.rightTwoMin = 0 state.chartLimit.rightTwoMax = 0 } } } /* 计算y轴上下限 */ const calcYAxislimit = (tableData:any=[])=>{ //散点图单独处理 if(state.chartInfo.ChartType===5){ if(tableData[1]){ state.chartLimit.min = tableData[1].MinData state.chartLimit.max = tableData[1].MaxData } return } //分组 const leftData = tableData.filter((i:IDataProps) => i.IsAxis === 1).map((i:IDataProps) => [Number(i.MinData), Number(i.MaxData)]) const rightData = tableData.filter((i:IDataProps) => !i.IsAxis).map((i:IDataProps) => [Number(i.MinData), Number(i.MaxData)]) const rightTwoData = tableData.filter((i:IDataProps) => i.IsAxis === 2).map((i:IDataProps) => [Number(i.MinData), Number(i.MaxData)]) //计算最大最小值 if (leftData.length) { const { Max, Min } = calcLimit(leftData.flat()) state.chartLimit.min = Min state.chartLimit.max = Max } else { state.chartLimit.min = 0 state.chartLimit.max = 0 } if (rightData.length) { const { Max, Min } = calcLimit(rightData.flat()) state.chartLimit.rightMin = Min state.chartLimit.rightMax = Max } else { state.chartLimit.rightMin = 0 state.chartLimit.rightMax = 0 } if (rightTwoData.length) { const { Max, Min } = calcLimit(rightTwoData.flat()) state.chartLimit.rightTwoMin = Min state.chartLimit.rightTwoMax = Max } else { state.chartLimit.rightTwoMin = 0 state.chartLimit.rightTwoMax = 0 } } const calcLimit = (arr:any)=>{ return { Max: Math.max(...arr), Min: Math.min(...arr) } } /*-------------------- */