|  | @@ -1,23 +1,11 @@
 | 
	
		
			
				|  |  | -<template>
 | 
	
		
			
				|  |  | -  <div v-loading="loading">
 | 
	
		
			
				|  |  | -    <MetricsCards v-model="metrics" :metric-items="metricsItems" @change="changeMetric"></MetricsCards>
 | 
	
		
			
				|  |  | -    <el-radio-group v-model="statDim" class="chart-button-group" @change="changeStatDim">
 | 
	
		
			
				|  |  | -      <el-radio-button label="day">日</el-radio-button>
 | 
	
		
			
				|  |  | -      <el-radio-button :disabled="!props.fetchLineWeek" label="week">周</el-radio-button>
 | 
	
		
			
				|  |  | -      <el-radio-button :disabled="!props.fetchLineWeek" label="month">月</el-radio-button>
 | 
	
		
			
				|  |  | -    </el-radio-group>
 | 
	
		
			
				|  |  | -    <div ref="chartRef" style="height: 350px"></div>
 | 
	
		
			
				|  |  | -  </div>
 | 
	
		
			
				|  |  | -</template>
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  <script lang="ts" setup>
 | 
	
		
			
				|  |  |  import * as echarts from 'echarts';
 | 
	
		
			
				|  |  | -import { Ref, computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
 | 
	
		
			
				|  |  | -import { dayMetricsEnum, weekMetricsEnum, monthMetricsEnum } from '/src/views/reportManage/dataCenter/utils/enum';
 | 
	
		
			
				|  |  | +import { computed, onBeforeUnmount, onMounted, Ref, ref, watch } from 'vue';
 | 
	
		
			
				|  |  | +import { dayMetricsEnum, initData } from '/src/views/reportManage/dataCenter/utils/enum';
 | 
	
		
			
				|  |  |  import XEUtils from 'xe-utils';
 | 
	
		
			
				|  |  |  import MetricsCards from '../MetricsCards/index.vue';
 | 
	
		
			
				|  |  |  import emitter from '/src/utils/emitter';
 | 
	
		
			
				|  |  | -import { buildChartOpt, parseQueryParams } from '/src/views/reportManage/dataCenter/utils/tools';
 | 
	
		
			
				|  |  | +import { parseQueryParams } from '/src/views/reportManage/dataCenter/utils/tools';
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  defineOptions({
 | 
	
		
			
				|  |  |    name: 'DataTendencyChart',
 | 
	
	
		
			
				|  | @@ -36,16 +24,11 @@ interface Props {
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  const props = withDefaults(defineProps<Props>(), {
 | 
	
		
			
				|  |  | -  initMetric: () => [
 | 
	
		
			
				|  |  | -    {metric: 'sales', color: '#0085ff', label: '销售额'},
 | 
	
		
			
				|  |  | -    {metric: 'ad_sales', color: '#ff9500', label: '广告销售额'},
 | 
	
		
			
				|  |  | -    {metric: 'ad_cost', color: '#3fd4cf', label: '广告花费'},
 | 
	
		
			
				|  |  | -  ],
 | 
	
		
			
				|  |  | +  initMetric: () => initData,
 | 
	
		
			
				|  |  |    metricEnum: () => dayMetricsEnum,
 | 
	
		
			
				|  |  |  });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  const metrics = ref(props.initMetric);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  const metricsItems: Ref<MetricData[]> = ref([]);
 | 
	
		
			
				|  |  |  let chartObj: any;
 | 
	
		
			
				|  |  |  const chartRef = ref();
 | 
	
	
		
			
				|  | @@ -136,7 +119,7 @@ const option: any = {
 | 
	
		
			
				|  |  |        yAxisIndex: 0,
 | 
	
		
			
				|  |  |        itemStyle: {
 | 
	
		
			
				|  |  |          color: '',
 | 
	
		
			
				|  |  | -        borderRadius: 4,
 | 
	
		
			
				|  |  | +        borderRadius: [4, 4, 4, 4],
 | 
	
		
			
				|  |  |        },
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      {
 | 
	
	
		
			
				|  | @@ -147,11 +130,22 @@ const option: any = {
 | 
	
		
			
				|  |  |          x: 'data_datetime',
 | 
	
		
			
				|  |  |          y: '',
 | 
	
		
			
				|  |  |        },
 | 
	
		
			
				|  |  | -      barWidth: '18px',
 | 
	
		
			
				|  |  | -      yAxisIndex: 0,
 | 
	
		
			
				|  |  | +      symbolSize: 6,
 | 
	
		
			
				|  |  | +      symbol: 'circle',
 | 
	
		
			
				|  |  | +      smooth: true,
 | 
	
		
			
				|  |  | +      yAxisIndex: 1,
 | 
	
		
			
				|  |  |        itemStyle: {
 | 
	
		
			
				|  |  | -        color: '',
 | 
	
		
			
				|  |  | -        borderRadius: 4,
 | 
	
		
			
				|  |  | +        // color: '#ff9500',
 | 
	
		
			
				|  |  | +        // borderColor: '#ff9500'
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +      areaStyle: {
 | 
	
		
			
				|  |  | +        // color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
 | 
	
		
			
				|  |  | +        //   { offset: 0, color: '#3fd4cf53' },
 | 
	
		
			
				|  |  | +        //   { offset: 1, color: '#3fd4cf03' },
 | 
	
		
			
				|  |  | +        // ]),
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +      emphasis: {
 | 
	
		
			
				|  |  | +        focus: 'series',
 | 
	
		
			
				|  |  |        },
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      {
 | 
	
	
		
			
				|  | @@ -184,6 +178,7 @@ onMounted(() => {
 | 
	
		
			
				|  |  |      initLine();
 | 
	
		
			
				|  |  |    }, 0);
 | 
	
		
			
				|  |  |  });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  onBeforeUnmount(() => {
 | 
	
		
			
				|  |  |    if (chartObj) {
 | 
	
		
			
				|  |  |      chartObj.dispose();
 | 
	
	
		
			
				|  | @@ -192,67 +187,71 @@ onBeforeUnmount(() => {
 | 
	
		
			
				|  |  |    removeResize();
 | 
	
		
			
				|  |  |  });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -//初始化图表
 | 
	
		
			
				|  |  | -const initLine = async () => {
 | 
	
		
			
				|  |  | +async function initLine() {
 | 
	
		
			
				|  |  |    chartObj = echarts.init(chartRef.value);
 | 
	
		
			
				|  |  | -  const items = await getDataset();
 | 
	
		
			
				|  |  | -  option.dataset.source = items;
 | 
	
		
			
				|  |  | +  option.dataset.source = await getDataset();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  XEUtils.arrayEach(option.series, (info: any, index) => {
 | 
	
		
			
				|  |  | -    const color = metrics.value[index].color;
 | 
	
		
			
				|  |  | -    info.name = metrics.value[index].label;
 | 
	
		
			
				|  |  | -    info.encode.y = metrics.value[index].metric;
 | 
	
		
			
				|  |  | -    if (info.type === 'bar') {
 | 
	
		
			
				|  |  | -      info.itemStyle.color = color;
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      info.itemStyle = {color: color, borderColor: color};
 | 
	
		
			
				|  |  | -      info.areaStyle = {
 | 
	
		
			
				|  |  | -        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
 | 
	
		
			
				|  |  | -          {offset: 0, color: color + '53'},
 | 
	
		
			
				|  |  | -          {offset: 1, color: color + '03'},
 | 
	
		
			
				|  |  | -        ]),
 | 
	
		
			
				|  |  | -      };
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  });
 | 
	
		
			
				|  |  | -  XEUtils.arrayEach(option.yAxis, (info: any, index) => {
 | 
	
		
			
				|  |  | -    info.name = metrics.value[index].label;
 | 
	
		
			
				|  |  | -    info.axisLine.lineStyle.color = metrics.value[index].color;
 | 
	
		
			
				|  |  | -  });
 | 
	
		
			
				|  |  | +  // 初始化系列和 y 轴
 | 
	
		
			
				|  |  | +  option.series = metrics.value.map((metric, index) => ({
 | 
	
		
			
				|  |  | +    id: index,
 | 
	
		
			
				|  |  | +    name: metric.label,
 | 
	
		
			
				|  |  | +    type: index === 0 ? 'bar' : 'line',
 | 
	
		
			
				|  |  | +    encode: {x: 'Name', y: metric.metric},
 | 
	
		
			
				|  |  | +    yAxisIndex: index,
 | 
	
		
			
				|  |  | +    itemStyle: {color: option.series.type == 'bar' ? '#0085ff' : metric.color, borderRadius: [6, 6, 6, 6]},
 | 
	
		
			
				|  |  | +    lineStyle: {color: metric.color},
 | 
	
		
			
				|  |  | +    barWidth: '10%',
 | 
	
		
			
				|  |  | +    areaStyle:
 | 
	
		
			
				|  |  | +        index !== 0
 | 
	
		
			
				|  |  | +            ? {
 | 
	
		
			
				|  |  | +              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
 | 
	
		
			
				|  |  | +                {offset: 0, color: metric.color + '53'},
 | 
	
		
			
				|  |  | +                {offset: 1, color: metric.color + '03'},
 | 
	
		
			
				|  |  | +              ]),
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            : undefined,
 | 
	
		
			
				|  |  | +  }));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  option.yAxis = metrics.value.map((metric, index) => ({
 | 
	
		
			
				|  |  | +    id: index,
 | 
	
		
			
				|  |  | +    name: metric.label,
 | 
	
		
			
				|  |  | +    type: 'value',
 | 
	
		
			
				|  |  | +    position: index === 0 ? 'left' : 'right',
 | 
	
		
			
				|  |  | +    offset: index > 1 ? (index - 1) * 80 : 0,
 | 
	
		
			
				|  |  | +    axisLine: {show: true, lineStyle: {color: metric.color}},
 | 
	
		
			
				|  |  | +    splitLine: {
 | 
	
		
			
				|  |  | +      show: index === 0,
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    show: true,
 | 
	
		
			
				|  |  | +  }));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // 初始化图例
 | 
	
		
			
				|  |  | +  option.legend.selected = props.metricEnum.reduce((acc, metric) => {
 | 
	
		
			
				|  |  | +    acc[metric.label] = metrics.value.some((m) => m.metric === metric.value);
 | 
	
		
			
				|  |  | +    return acc;
 | 
	
		
			
				|  |  | +  }, {});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  XEUtils.arrayEach(props.metricEnum, (info) => {
 | 
	
		
			
				|  |  | -    option.legend.selected[info.label] = false;
 | 
	
		
			
				|  |  | -  });
 | 
	
		
			
				|  |  | -  for (const info of metrics.value) {
 | 
	
		
			
				|  |  | -    option.legend.selected[info.label] = true;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  // console.log(option)
 | 
	
		
			
				|  |  |    chartObj.setOption(option);
 | 
	
		
			
				|  |  |    loading.value = false;
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -//条形图数据
 | 
	
		
			
				|  |  | -const getDataset = async () => {
 | 
	
		
			
				|  |  | -  // console.log('getDataset,line');
 | 
	
		
			
				|  |  | +async function getDataset() {
 | 
	
		
			
				|  |  |    if (statDim.value === 'week') {
 | 
	
		
			
				|  |  |      if (props.fetchLineWeek) {
 | 
	
		
			
				|  |  |        const resp = await props.fetchLineWeek(queryParams.value);
 | 
	
		
			
				|  |  | -      // console.log('week',resp.data);
 | 
	
		
			
				|  |  |        return resp.data;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    } else if (statDim.value === 'month') {
 | 
	
		
			
				|  |  |      if (props.fetchLineMonth) {
 | 
	
		
			
				|  |  |        const resp = await props.fetchLineMonth(queryParams.value);
 | 
	
		
			
				|  |  | -      // console.log('month',resp.data);
 | 
	
		
			
				|  |  |        return resp.data;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    } else {
 | 
	
		
			
				|  |  |      const resp = await props.fetchLine(queryParams.value);
 | 
	
		
			
				|  |  |      return resp.data;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -//卡片数据
 | 
	
		
			
				|  |  |  const getMetricsItems = async () => {
 | 
	
		
			
				|  |  |    let resp;
 | 
	
		
			
				|  |  |    if (statDim.value === 'week' && props.fetchCardWeek) {
 | 
	
	
		
			
				|  | @@ -278,54 +277,114 @@ const getMetricsItems = async () => {
 | 
	
		
			
				|  |  |    });
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -//更新图表的选项
 | 
	
		
			
				|  |  | -const changeMetric = () => {
 | 
	
		
			
				|  |  | -  const opt = buildChartOpt(option, metrics.value);
 | 
	
		
			
				|  |  | -  chartObj.setOption(opt);
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +function changeMetric() {
 | 
	
		
			
				|  |  | +  // 更新图例选中状态
 | 
	
		
			
				|  |  | +  option.legend.selected = props.metricEnum.reduce((acc, metric) => {
 | 
	
		
			
				|  |  | +    acc[metric.label] = metrics.value.some((m) => m.metric === metric.value);
 | 
	
		
			
				|  |  | +    return acc;
 | 
	
		
			
				|  |  | +  }, {});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // 重新创建 series 数组,只包含选中的指标
 | 
	
		
			
				|  |  | +  option.series = metrics.value.map((metric, index) => {
 | 
	
		
			
				|  |  | +    const baseConfig = {
 | 
	
		
			
				|  |  | +      id: index,
 | 
	
		
			
				|  |  | +      name: metric.label,
 | 
	
		
			
				|  |  | +      type: metric.color === '#0085ff' ? 'bar' : 'line',
 | 
	
		
			
				|  |  | +      encode: {x: 'Name', y: metric.metric},
 | 
	
		
			
				|  |  | +      yAxisIndex: index,
 | 
	
		
			
				|  |  | +      itemStyle: {color: metric.color, borderRadius: [6, 6, 6, 6]},
 | 
	
		
			
				|  |  | +      lineStyle: {color: metric.color},
 | 
	
		
			
				|  |  | +      barWidth: '10%',
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (baseConfig != 'bar') {
 | 
	
		
			
				|  |  | +      baseConfig.areaStyle = {
 | 
	
		
			
				|  |  | +        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
 | 
	
		
			
				|  |  | +          {offset: 0, color: metric.color + '53'},
 | 
	
		
			
				|  |  | +          {offset: 1, color: metric.color + '03'},
 | 
	
		
			
				|  |  | +        ]),
 | 
	
		
			
				|  |  | +      };
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return baseConfig;
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // 更新 y 轴显示状态
 | 
	
		
			
				|  |  | +  option.yAxis = metrics.value.map((metric, index) => ({
 | 
	
		
			
				|  |  | +    id: index,
 | 
	
		
			
				|  |  | +    name: metric.label,
 | 
	
		
			
				|  |  | +    type: 'value',
 | 
	
		
			
				|  |  | +    position: index === 0 ? 'left' : 'right',
 | 
	
		
			
				|  |  | +    offset: index > 1 ? (index - 1) * 80 : 0,
 | 
	
		
			
				|  |  | +    axisLine: {
 | 
	
		
			
				|  |  | +      show: true,
 | 
	
		
			
				|  |  | +      lineStyle: {color: metric.color},
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    splitLine: {
 | 
	
		
			
				|  |  | +      show: index === 0,
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    show: true,
 | 
	
		
			
				|  |  | +  }));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  while (option.yAxis.length > metrics.value.length) {
 | 
	
		
			
				|  |  | +    option.yAxis.pop();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  chartObj.setOption(option, true); // 使用 true 作为第二个参数,强制完全刷新
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -//根据日,周,月更新图表
 | 
	
		
			
				|  |  | -const emit = defineEmits(['changeStatDim']);
 | 
	
		
			
				|  |  |  const changeStatDim = async () => {
 | 
	
		
			
				|  |  |    emitter.emit('DateTendency-dateChange', statDim.value);
 | 
	
		
			
				|  |  | -  // loading.value = true;
 | 
	
		
			
				|  |  | -  // // let source = await getDataset();
 | 
	
		
			
				|  |  | -  // // if (source.length > 0) {
 | 
	
		
			
				|  |  | -  // //   chartObj.setOption({dataset: {source: source}});
 | 
	
		
			
				|  |  | -  // // }
 | 
	
		
			
				|  |  | -  // // await getMetricsItems();
 | 
	
		
			
				|  |  | -  // loading.value = false;
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -//监测dataRange的变化
 | 
	
		
			
				|  |  |  let initialized = false;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  watch(props.query, async () => {
 | 
	
		
			
				|  |  |    if (!initialized) {
 | 
	
		
			
				|  |  |      initialized = true;
 | 
	
		
			
				|  |  |      return;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  // emitter.emit('DateTendency-changeStatDim'); // 触发DataTable的loading
 | 
	
		
			
				|  |  |    loading.value = true;
 | 
	
		
			
				|  |  |    await getMetricsItems();
 | 
	
		
			
				|  |  | -  const items = await getDataset();
 | 
	
		
			
				|  |  | -  // emit('changeStatDim', items); // 向父组件传递数据后再传递给DataTable
 | 
	
		
			
				|  |  | -  const opt = {dataset: {source: items}};
 | 
	
		
			
				|  |  | -  chartObj.setOption(opt);
 | 
	
		
			
				|  |  | +  option.dataset.source = await getDataset();
 | 
	
		
			
				|  |  | +  chartObj.setOption(option);
 | 
	
		
			
				|  |  |    loading.value = false;
 | 
	
		
			
				|  |  |  });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +watch(
 | 
	
		
			
				|  |  | +    () => props.initMetric,
 | 
	
		
			
				|  |  | +    (newVal) => {
 | 
	
		
			
				|  |  | +      metrics.value = newVal;
 | 
	
		
			
				|  |  | +      initLine(); // 重新初始化图表数据
 | 
	
		
			
				|  |  | +      //console.log('initMetric', props.initMetric);
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    { immediate: true }
 | 
	
		
			
				|  |  | +);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  const resizeChart = () => {
 | 
	
		
			
				|  |  |    chartObj.resize();
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  const addResize = () => {
 | 
	
		
			
				|  |  |    window.addEventListener('resize', resizeChart);
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  const removeResize = () => {
 | 
	
		
			
				|  |  |    window.removeEventListener('resize', resizeChart);
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  </script>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +<template>
 | 
	
		
			
				|  |  | +  <div v-loading="loading">
 | 
	
		
			
				|  |  | +    <MetricsCards v-model="metrics" :metric-items="metricsItems" @change="changeMetric"></MetricsCards>
 | 
	
		
			
				|  |  | +    <el-radio-group v-model="statDim" class="chart-button-group" @change="changeStatDim">
 | 
	
		
			
				|  |  | +      <el-radio-button label="day">日</el-radio-button>
 | 
	
		
			
				|  |  | +      <el-radio-button :disabled="!props.fetchLineWeek" label="week">周</el-radio-button>
 | 
	
		
			
				|  |  | +      <el-radio-button :disabled="!props.fetchLineWeek" label="month">月</el-radio-button>
 | 
	
		
			
				|  |  | +    </el-radio-group>
 | 
	
		
			
				|  |  | +    <div ref="chartRef" style="height: 350px"></div>
 | 
	
		
			
				|  |  | +  </div>
 | 
	
		
			
				|  |  | +</template>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  <style scoped>
 | 
	
		
			
				|  |  |  .metrics-cards {
 | 
	
		
			
				|  |  |    display: flex;
 |