|
@@ -0,0 +1,1806 @@
|
|
|
|
+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;
|
|
|
|
+ }
|
|
|
|
+ dataList: any[];
|
|
|
|
+ chartInfo: any
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * useChartRender 处理图表渲染 返回options
|
|
|
|
+ * @param Data 详情数据 lange中英文
|
|
|
|
+ * @param {
|
|
|
|
+ * Source 1 //ETA图 ChartType: 1曲线图 2季节图 3面积图 4柱状图 5散点图 6组合图 7柱形 10截面散点
|
|
|
|
+ * 2 商品价格
|
|
|
|
+ * 3 相关性
|
|
|
|
+ * 4 滚动相关性
|
|
|
|
+ * 5 商品利润
|
|
|
|
+ * 6 拟合方程
|
|
|
|
+ * 7 统计特征/标准差
|
|
|
|
+ * 8 统计特征/百分位
|
|
|
|
+ * 9 统计特征/频率
|
|
|
|
+ * 10 跨品种分析
|
|
|
|
+ * }
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+//依赖数据
|
|
|
|
+const state = reactive<StateProps>({
|
|
|
|
+ chartInfo: {},
|
|
|
|
+ dataList: [],
|
|
|
|
+ chartLimit: {},
|
|
|
|
+})
|
|
|
|
+const language = ref('')
|
|
|
|
+const routeQuery = ref<any>({});//路由参
|
|
|
|
+watch(() => router.currentRoute.value.query,(nval) => {
|
|
|
|
+ routeQuery.value = nval
|
|
|
|
+})
|
|
|
|
+export const useChartRender = (Data,lang='ch',) => {
|
|
|
|
+
|
|
|
|
+ state.chartInfo = Data.ChartInfo
|
|
|
|
+ state.dataList = Data.ChartInfo.Source === 1 ? Data.EdbInfoList : [Data.EdbInfoList[0]];
|
|
|
|
+ language.value = lang
|
|
|
|
+
|
|
|
|
+ let chartOptions:any;
|
|
|
|
+
|
|
|
|
+ //eta图
|
|
|
|
+ if (Data.ChartInfo.Source === 1) {
|
|
|
|
+ const typeMap = {
|
|
|
|
+ 1: setDefaultLineOptions,
|
|
|
|
+ 2: setSeasonOptions,
|
|
|
|
+ 3: setStackOrCombinChart,
|
|
|
|
+ 4: setStackOrCombinChart,
|
|
|
|
+ 5: setScatterChartOptions,
|
|
|
|
+ 6: setStackOrCombinChart,
|
|
|
|
+ 7: initBarData,
|
|
|
|
+ 10: initSectionScatterData,
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ 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
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ //y轴
|
|
|
|
+ let textEn = item.Unit?item.UnitEn:''
|
|
|
|
+ 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=='ch'?sameSideIndex !== index ? '' : `${item.Unit}`: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(chartData[sameSideIndex].MinData),
|
|
|
|
+ max: Number(chartData[sameSideIndex].MaxData),
|
|
|
|
+ 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 == 'ch'?nameCh:nameEn
|
|
|
|
+
|
|
|
|
+ //预测指标配置
|
|
|
|
+ let predict_params = item.EdbInfoCategoryType === 1 ? getPredictParams(item) : {};
|
|
|
|
+
|
|
|
|
+ let obj = {
|
|
|
|
+ data: [] as any[],
|
|
|
|
+ type: (chartTheme&&chartTheme.lineOptions.lineType) || 'spline',
|
|
|
|
+ dashStyle: (chartTheme&&chartTheme.lineOptions.dashStyle)||'Solid',
|
|
|
|
+ yAxis: sameSideIndex,
|
|
|
|
+ name,
|
|
|
|
+ color: item.ChartColor,
|
|
|
|
+ lineWidth: Number(item.ChartWidth),
|
|
|
|
+ ...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=='ch'){
|
|
|
|
+ str = `${Highcharts.dateFormat('%Y/%m/%d',that.x)}<br><p>相关性系数:${that.y.toFixed(4)}</p><br><p>领先${LeadValue+LeadUnit}</p>`
|
|
|
|
+ }else{
|
|
|
|
+ str = `${Highcharts.dateFormat('%Y/%m/%d',that.x)}<br><p>Correlation coefficient:${that.y.toFixed(4)}</p><br><p>lead${LeadValue+relevanceUnitEnMap[LeadUnit]}</p>`
|
|
|
|
+ }
|
|
|
|
+ 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[] = [];
|
|
|
|
+
|
|
|
|
+ /* 主题样式*/
|
|
|
|
+ const chartTheme = state.chartInfo.ChartThemeStyle ? JSON.parse(state.chartInfo.ChartThemeStyle) : null;
|
|
|
|
+
|
|
|
|
+ /*处理数据列*/
|
|
|
|
+ for (let j of chartDataHandle) {
|
|
|
|
+ //预测指标配置
|
|
|
|
+ let predict_params = chartData.EdbInfoCategoryType === 1 ? getSeasonPredictParams(j.CuttingDataTimestamp) : {};
|
|
|
|
+
|
|
|
|
+ let serie_item = {
|
|
|
|
+ data: [] as any[],
|
|
|
|
+ type: (chartTheme&&chartTheme.lineOptions.lineType) || chartData.ChartStyle,
|
|
|
|
+ dashStyle: (chartTheme&&chartTheme.lineOptions.dashStyle)||'Solid',
|
|
|
|
+ yAxis: 0,
|
|
|
|
+ name: j.ChartLegend,
|
|
|
|
+ lineWidth: (chartTheme&&chartTheme.lineOptions.lineWidth) || 1,
|
|
|
|
+ ...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轴
|
|
|
|
+ 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=='ch'?`${chartData.Unit}`:chartData.UnitEn,
|
|
|
|
+ align: 'high',
|
|
|
|
+ rotation: 0,
|
|
|
|
+ y: -12,
|
|
|
|
+ x: 0,
|
|
|
|
+ textAlign: 'left',
|
|
|
|
+ reserveSpace: false,
|
|
|
|
+ style:{
|
|
|
|
+ ...chartTheme&&chartTheme.yAxisOptions.style
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ max: Number(chartData.MaxData),
|
|
|
|
+ min: Number(chartData.MinData),
|
|
|
|
+ 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);
|
|
|
|
+
|
|
|
|
+ //y轴
|
|
|
|
+ let textEn = item.Unit?item.UnitEn:''
|
|
|
|
+ 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=='ch'?sameSideIndex !== index ? '' : `${item.Unit}`: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(chartData[sameSideIndex].MinData),
|
|
|
|
+ max: Number(chartData[sameSideIndex].MaxData),
|
|
|
|
+ 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 == 'ch'?nameCh:nameEn
|
|
|
|
+
|
|
|
|
+ //预测指标配置
|
|
|
|
+ let predict_params = item.EdbInfoCategoryType === 1 ? getPredictParams(item,chartStyle) : {};
|
|
|
|
+
|
|
|
|
+ let obj = {
|
|
|
|
+ data: [] as any[],
|
|
|
|
+ type: chartStyle || item.ChartStyle,
|
|
|
|
+ yAxis: serie_yIndex,
|
|
|
|
+ name,
|
|
|
|
+ color: item.ChartColor,
|
|
|
|
+ lineWidth: Number(item.ChartWidth),
|
|
|
|
+ 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=='ch'?
|
|
|
|
+ `<strong>${ 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() }</strong><br>
|
|
|
|
+ ${dataList[0].EdbName}: <span style="font-weight: 600"> ${that.x}</span><br>
|
|
|
|
+ ${dataList[1].EdbName}: <span style="font-weight: 600"> ${that.y}</span>
|
|
|
|
+ `:
|
|
|
|
+ `<strong>${ 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() }</strong><br>
|
|
|
|
+ ${dataList[0].EdbNameEn}: <span style="font-weight: 600"> ${that.x}</span><br>
|
|
|
|
+ ${dataList[1].EdbNameEn}: <span style="font-weight: 600"> ${that.y}</span>
|
|
|
|
+ `
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const { IsOrder,ChartColor } = dataList[0];
|
|
|
|
+ //y轴
|
|
|
|
+ let yAxis = {
|
|
|
|
+ title: {
|
|
|
|
+ text: language.value=='ch'?`${dataList[1].Unit}`:dataList[1].Unit?dataList[1].UnitEn:'',
|
|
|
|
+ // 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(dataList[0].MinData),
|
|
|
|
+ max: Number(dataList[0].MaxData),
|
|
|
|
+ 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 == 'ch'?`${chartInfo.ChartName}${IsOrder ? '(逆序)' : ''}`:`${chartInfo.ChartNameEn}${IsOrder ? '(reserve)' : ''}`,
|
|
|
|
+ color: ChartColor,
|
|
|
|
+ chartType: 'linear',
|
|
|
|
+ marker: {
|
|
|
|
+ radius: (chartTheme&&chartTheme.lineOptions.radius)||5,
|
|
|
|
+ },
|
|
|
|
+ }
|
|
|
|
+ real_data.forEach(_ => {
|
|
|
|
+ series.data.push([_.x,_.y])
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ return {
|
|
|
|
+ title: {
|
|
|
|
+ text:''
|
|
|
|
+ },
|
|
|
|
+ series: [ series ],
|
|
|
|
+ yAxis,
|
|
|
|
+ xAxis: {
|
|
|
|
+ ...scatterXAxis,
|
|
|
|
+ title: {
|
|
|
|
+ text: language.value=='ch'?`${dataList[0].Unit}`:dataList[0].Unit?dataList[0].UnitEn:'',
|
|
|
|
+ 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<any>({})
|
|
|
|
+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==='ch'
|
|
|
|
+ ? 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==='ch' ? chartInfo.Unit : chartInfo.UnitEn,
|
|
|
|
+ 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==='ch' ? (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<any>({});
|
|
|
|
+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' : 'ch';
|
|
|
|
+ }
|
|
|
|
+ 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 === 'ch' ? 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 === 'ch' ? 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 => {
|
|
|
|
+ //数据列
|
|
|
|
+ let series_item = {
|
|
|
|
+ data: [] as any[],
|
|
|
|
+ type: 'scatter',
|
|
|
|
+ name: language.value === 'ch' ? item.Name : item.NameEn,
|
|
|
|
+ color: item.Color,
|
|
|
|
+ lineWidth: 0,
|
|
|
|
+ chartType: 'linear',
|
|
|
|
+ zIndex:1,
|
|
|
|
+ marker: {
|
|
|
|
+ radius: (chartTheme&&chartTheme.lineOptions.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 === 'ch' ? _.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 =`<span>${item.TrendLine}</span><br><span>R²=${item.RSquare}</span>`
|
|
|
|
+ : item.ShowRSquare && !item.ShowFitEquation
|
|
|
|
+ ? tag =`<span>R²=${item.RSquare}</span>`
|
|
|
|
+ : item.ShowFitEquation && !item.ShowRSquare
|
|
|
|
+ ? tag =`<span>${item.TrendLine}</span>`
|
|
|
|
+ : ''
|
|
|
|
+ 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 === 'ch') {
|
|
|
|
+ let series_obj = DataList.find(_ => _.Name === that.series.name);
|
|
|
|
+ let ponit_obj = series_obj.EdbInfoList.find(_ => _.XValue ===that.x && _.YValue===that.y);
|
|
|
|
+
|
|
|
|
+ str=`<b>${ ponit_obj.Name }</b>`;
|
|
|
|
+ str += `<br><span style="color:${that.color}">\u25CF</span>${ponit_obj.XName}: ${that.x} ${ponit_obj.XDate}<br>`;
|
|
|
|
+ str += `<span style="color:${that.color}">\u25CF</span>${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=`<b>${ ponit_obj.NameEn }</b>`;
|
|
|
|
+ str += `<br><span style="color:${that.color}">\u25CF</span>${ponit_obj.XNameEn}: ${that.x} ${ponit_obj.XDate}<br>`;
|
|
|
|
+ str += `<span style="color:${that.color}">\u25CF</span>${ponit_obj.YNameEn}: ${that.y} ${ponit_obj.YDate}`;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ return str
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return {
|
|
|
|
+ title: {
|
|
|
|
+ text:''
|
|
|
|
+ },
|
|
|
|
+ series,
|
|
|
|
+ yAxis: [yAxis],
|
|
|
|
+ xAxis,
|
|
|
|
+ tooltip
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /* 商品价格曲线/利润曲线
|
|
|
|
+
|
|
|
|
+ */
|
|
|
|
+const commodityChartRenderData = ref<any>({})
|
|
|
|
+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 === 'ch' ? _.Name:_.NameEn),
|
|
|
|
+ tickWidth: 1,
|
|
|
|
+ labels: {
|
|
|
|
+ style: {
|
|
|
|
+ ...chartTheme&&chartTheme.xAxisOptions.style
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const { max,min } = chartLimit;
|
|
|
|
+ //y轴
|
|
|
|
+ let yAxis = {
|
|
|
|
+ ...basicYAxis,
|
|
|
|
+ title: {
|
|
|
|
+ text: language.value === 'ch' ? 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[] }) => {
|
|
|
|
+ //处理首或/尾全是无效数据的以null填充
|
|
|
|
+ let filterData = filterInvalidData(item)
|
|
|
|
+
|
|
|
|
+ let serie_item = {
|
|
|
|
+ data: filterData,
|
|
|
|
+ type: (chartTheme&&chartTheme.lineOptions.lineType) || 'spline',
|
|
|
|
+ dashStyle: (chartTheme&&chartTheme.lineOptions.dashStyle)||'Solid',
|
|
|
|
+ yAxis: 0,
|
|
|
|
+ name: language.value === 'ch' ? item.Name : item.NameEn,
|
|
|
|
+ color: item.Color,
|
|
|
|
+ chartType: 'linear',
|
|
|
|
+ lineWidth: (chartTheme&&chartTheme.lineOptions.lineWidth) || 3,
|
|
|
|
+ marker: {
|
|
|
|
+ enabled: false
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ seriesData.push(serie_item)
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ //tooltip
|
|
|
|
+ let tooltip = {
|
|
|
|
+ formatter: function() {
|
|
|
|
+ const ctx: any = this;
|
|
|
|
+ let str: string = '';
|
|
|
|
+
|
|
|
|
+ if(language.value === 'ch') {
|
|
|
|
+
|
|
|
|
+ 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+=`<b>${ edb_name }</b>`
|
|
|
|
+
|
|
|
|
+ if(!obj_item.NoDataEdbList.includes(obj_item.XEdbInfoIdList[index])) {
|
|
|
|
+ str += `<br><span style="color:${item.color}">\u25CF</span>${obj_item.Date}: ${item.y}<br>`
|
|
|
|
+ }else {
|
|
|
|
+ str += `<br><span style="color:${item.color}">\u25CF</span>${obj_item.Date}: 无<br>`
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ }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+=`<b>${ edb_name }</b>`
|
|
|
|
+
|
|
|
|
+ if(!obj_item.NoDataEdbList.includes(obj_item.XEdbInfoIdList[index])) {
|
|
|
|
+ str += `<br><span style="color:${item.color}">\u25CF</span>${obj_item.Date}: ${item.y}<br>`
|
|
|
|
+ }else {
|
|
|
|
+ str += `<br><span style="color:${item.color}">\u25CF</span>${obj_item.Date}: 无<br>`
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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=='ch'?`期数(${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=='ch'?'相关性系数':'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=>{
|
|
|
|
+ let serie_item = {
|
|
|
|
+ data: item.Value,
|
|
|
|
+ type: (chartTheme&&chartTheme.lineOptions.lineType) || 'spline',
|
|
|
|
+ dashStyle: (chartTheme&&chartTheme.lineOptions.dashStyle)||'Solid',
|
|
|
|
+ yAxis: 0,
|
|
|
|
+ name: language.value=='ch'?data.ChartInfo.ChartName:data.ChartInfo.ChartNameEn,
|
|
|
|
+ color: item.Color,
|
|
|
|
+ chartType: 'linear',
|
|
|
|
+ lineWidth: (chartTheme&&chartTheme.lineOptions.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=='ch'){
|
|
|
|
+ str = `<p>相关性系数:${that.y.toFixed(4)}</p><br><p>领先${ data.ChartInfo.Source===3 ?that.x+'期' : LeadValue+LeadUnit}</p>`
|
|
|
|
+ }else{
|
|
|
|
+ str = `<p>Correlation coefficient:${that.y.toFixed(4)}</p><br><p>lead${ data.ChartInfo.Source===3 ? that.x+'stage' : LeadValue+relevanceUnitEnMap[LeadUnit]}</p>`
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return str
|
|
|
|
+ },
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return {
|
|
|
|
+ isRelevanceChart:data.ChartInfo.Source===3,
|
|
|
|
+ title: {
|
|
|
|
+ text:'',
|
|
|
|
+ },
|
|
|
|
+ series: seriesData,
|
|
|
|
+ yAxis: [yAxis] ,
|
|
|
|
+ xAxis:xAxis,
|
|
|
|
+ tooltip
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* 统计特征
|
|
|
|
+ 标准差
|
|
|
|
+ 百分比
|
|
|
|
+ 频率
|
|
|
|
+滚动相关性*/
|
|
|
|
+const statisticFrequencyRenderData = ref<any>({});
|
|
|
|
+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 === 'ch' ? 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,
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let series_item = {
|
|
|
|
+ data: item.Value.map(_ =>[_.X,_.Y]),
|
|
|
|
+ dashStyle: (chartTheme&&chartTheme.lineOptions.dashStyle)||'Solid',
|
|
|
|
+ type: (chartTheme&&chartTheme.lineOptions.lineType) || 'spline',
|
|
|
|
+ yAxis: index,
|
|
|
|
+ name: language.value === 'ch' ? item.Name : item.NameEn,
|
|
|
|
+ color: item.Color,
|
|
|
|
+ lineWidth: (chartTheme&&chartTheme.lineOptions.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=`<b>${ data_interval }</b>`;
|
|
|
|
+ that.points.forEach(item => {
|
|
|
|
+ str += `<br><span style="color:${item.color}">\u25CF</span>${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<any>({})
|
|
|
|
+const initCrossVarietyChartData = (data:{ DataResp:any,ChartInfo:any,EdbInfoList:any }) => {
|
|
|
|
+ state.dataList = data.EdbInfoList;
|
|
|
|
+ crossVarietyChartRenderData.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,YNameEn,DataList } = data.DataResp;
|
|
|
|
+
|
|
|
|
+ let isAllEn = true;
|
|
|
|
+ let haveOneNoEn = DataList.some(_ => !_.NameEn);
|
|
|
|
+ if(!XNameEn || !YNameEn || haveOneNoEn) isAllEn = false;
|
|
|
|
+ language.value = isAllEn ? 'en' : 'ch';
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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 === 'ch' ? 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 === 'ch' ? 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[] = [];
|
|
|
|
+ DataList.forEach(item => {
|
|
|
|
+ //数据列
|
|
|
|
+ let series_item = {
|
|
|
|
+ data: [] as any[],
|
|
|
|
+ type: 'scatter',
|
|
|
|
+ name: language.value === 'ch' ? item.Name : item.NameEn,
|
|
|
|
+ color: item.Color,
|
|
|
|
+ lineWidth: 0,
|
|
|
|
+ chartType: 'linear',
|
|
|
|
+ zIndex:1
|
|
|
|
+ }
|
|
|
|
+ item.CoordinatePointData.forEach(_ => {
|
|
|
|
+ series_item.data.push({x: _.X,y: _.Y,})
|
|
|
|
+ })
|
|
|
|
+ series.push(series_item);
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ let tooltip = {
|
|
|
|
+ formatter: function() {
|
|
|
|
+
|
|
|
|
+ let that:any = this;
|
|
|
|
+ let str = '';
|
|
|
|
+ if(language.value === 'ch') {
|
|
|
|
+ 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=`<b>${ that.series.name }</b>`;
|
|
|
|
+ str += `<br><span style="color:${that.color}">\u25CF</span>${xEdbInfo.EdbName}: ${that.x} ${ponit_obj.XDate}<br>`;
|
|
|
|
+ str += `<span style="color:${that.color}">\u25CF</span>${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=`<b>${ that.series.name }</b>`;
|
|
|
|
+ str += `<br><span style="color:${that.color}">\u25CF</span>${xEdbInfo.EdbNameEn}: ${that.x} ${ponit_obj.XDate}<br>`;
|
|
|
|
+ str += `<span style="color:${that.color}">\u25CF</span>${yEdbInfo.EdbNameEn}: ${that.y} ${ponit_obj.YDate}`;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return str
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return {
|
|
|
|
+ title: {
|
|
|
|
+ text:''
|
|
|
|
+ },
|
|
|
|
+ series,
|
|
|
|
+ yAxis: [yAxis],
|
|
|
|
+ xAxis,
|
|
|
|
+ tooltip
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// 曲线图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 = "ch"
|
|
|
|
+): string => {
|
|
|
|
+ // IsAxis左轴1 右轴0 2右2轴
|
|
|
|
+ //IsOrder正序false 逆序true
|
|
|
|
+ //EdbInfoType是否是领先指标
|
|
|
|
+ const axisLabelMap: any =
|
|
|
|
+ lang == "ch"
|
|
|
|
+ ? {
|
|
|
|
+ 0: "右轴",
|
|
|
|
+ 2: "右2轴",
|
|
|
|
+ }
|
|
|
|
+ : {
|
|
|
|
+ 0: "RHS",
|
|
|
|
+ 2: "2-RHS",
|
|
|
|
+ };
|
|
|
|
+ const orderLabelMap: any =
|
|
|
|
+ lang == "ch"
|
|
|
|
+ ? {
|
|
|
|
+ 1: "逆序",
|
|
|
|
+ }
|
|
|
|
+ : {
|
|
|
|
+ 1: "REV",
|
|
|
|
+ };
|
|
|
|
+ const edbInfoMap: any =
|
|
|
|
+ lang == "ch"
|
|
|
|
+ ? {
|
|
|
|
+ 0: "领先",
|
|
|
|
+ }
|
|
|
|
+ : {
|
|
|
|
+ 0: "Lead",
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const leadUnitEnMap: any = {
|
|
|
|
+ 年: "Y",
|
|
|
|
+ 季: "Q",
|
|
|
|
+ 月: "M",
|
|
|
|
+ 周: "W",
|
|
|
|
+ 天: "D",
|
|
|
|
+ };
|
|
|
|
+ //英文领先单位转换
|
|
|
|
+ const edbLeadUnit = lang == "ch" ? 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;
|
|
|
|
+};
|