Jelajahi Sumber

Merge branch 'xinyan' into test

xinyan 8 bulan lalu
induk
melakukan
115f16d913
1 mengubah file dengan 186 tambahan dan 126 penghapusan
  1. 186 126
      src/views/adManage/portfolios/chartComponents/dataTendency.vue

+ 186 - 126
src/views/adManage/portfolios/chartComponents/dataTendency.vue

@@ -1,40 +1,23 @@
-<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 label="week" :disabled="!props.fetchLineWeek">周</el-radio-button>
-      <el-radio-button label="month" :disabled="!props.fetchLineWeek">月</el-radio-button>
-    </el-radio-group>
-    <div style="height: 350px" ref="chartRef"></div>
-  </div>
-</template>
-
 <script lang="ts" setup>
-import { computed, onBeforeUnmount, onMounted, Ref, ref, watch } from 'vue'
-import * as echarts from 'echarts'
-import { spCampaignMetricsEnum } from '/@/views/adManage/utils/enum.js'
-import MetricsCards from '/@/components/MetricsCards/index.vue'
-import XEUtils from 'xe-utils'
-import { buildChartOpt, parseQueryParams } from '/@/views/adManage/utils/tools.js'
-import emitter from '/@/utils/emitter'
-
-// import { useShopInfo } from '/@/stores/shopInfo'
-// import { usePublicData } from '/@/stores/publicData'
-// import { storeToRefs } from 'pinia'
+import { computed, onBeforeUnmount, onMounted, Ref, ref, watch } from 'vue';
+import * as echarts from 'echarts';
+import { spCampaignMetricsEnum } from '/@/views/adManage/utils/enum.js';
+import MetricsCards from '/@/components/MetricsCards/index.vue';
+import XEUtils from 'xe-utils';
+import { parseQueryParams } from '/@/views/adManage/utils/tools.js';
 
 defineOptions({
   name: 'DataTendencyChart',
-})
+});
 
 interface Props {
-  fetchCard: Function
-  fetchLine: Function
-  fetchLineMonth?: Function
-  fetchLineWeek?: Function
-  query: { [key: string]: any }
-  initMetric?: ShowMetric[]
-  metricEnum?: { [key: string]: string }[]
+  fetchCard: Function;
+  fetchLine: Function;
+  fetchLineMonth?: Function;
+  fetchLineWeek?: Function;
+  query: { [key: string]: any };
+  initMetric?: ShowMetric[];
+  metricEnum?: { [key: string]: string }[];
 }
 
 const props = withDefaults(defineProps<Props>(), {
@@ -44,16 +27,13 @@ const props = withDefaults(defineProps<Props>(), {
     { metric: 'Spend', color: '#ff9500', label: '花费' },
   ],
   metricEnum: () => spCampaignMetricsEnum,
-})
+});
 
-const metrics = ref(props.initMetric)
-// const shopInfo = useShopInfo()
-// const publicData = usePublicData()
-// const { dateRange } = storeToRefs(publicData)
-const metricsItems: Ref<MetricData[]> = ref([])
-let chartObj: any
-const chartRef = ref()
-const statDim = ref('day')
+const metrics = ref(props.initMetric);
+const metricsItems: Ref<MetricData[]> = ref([]);
+let chartObj: any;
+const chartRef = ref();
+const statDim = ref('day');
 const option: any = {
   dataset: {
     source: [],
@@ -136,7 +116,7 @@ const option: any = {
         x: 'Name',
         y: '',
       },
-      barWidth: '18px',
+      barWidth: '16px',
       yAxisIndex: 0,
       itemStyle: {
         color: '',
@@ -188,82 +168,58 @@ const option: any = {
       },
     },
   ],
-}
-const loading = ref(true)
-const queryParams = computed(() => parseQueryParams(props.query))
+};
+const loading = ref(true);
+const queryParams = computed(() => parseQueryParams(props.query));
 
 onMounted(() => {
-  getMetricsItems()
-  addResize()
+  getMetricsItems();
+  addResize();
   // initLine()
   setTimeout(() => {
-    initLine()
-  }, 0)
-})
+    initLine();
+  }, 0);
+});
+
 onBeforeUnmount(() => {
   if (chartObj) {
-    chartObj.dispose()
-    chartObj = null
+    chartObj.dispose();
+    chartObj = null;
   }
-  removeResize()
-})
-
-const initLine = async () => {
-  chartObj = echarts.init(chartRef.value)
-  const items = await getDataset()
-  option.dataset.source = items
+  removeResize();
+});
 
-  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
-  })
+watch(props.query, async () => {
+  // console.log("------watch-----queryParams", props.query)
+  loading.value = true;
+  await getMetricsItems();
+  const items = await getDataset();
+  const opt = { dataset: { source: items } };
+  chartObj.setOption(opt);
+  loading.value = false;
+});
 
-  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 () => {
+async function getDataset() {
   if (statDim.value === 'week') {
     if (props.fetchLineWeek) {
-      const resp = await props.fetchLineWeek(queryParams.value)
-      return resp.data
+      const resp = await props.fetchLineWeek(queryParams.value);
+      return resp.data;
     }
   } else if (statDim.value === 'month') {
     if (props.fetchLineMonth) {
-      const resp = await props.fetchLineMonth(queryParams.value)
-      return resp.data
+      const resp = await props.fetchLineMonth(queryParams.value);
+      return resp.data;
     }
   } else {
-    const resp = await props.fetchLine(queryParams.value)
-    return resp.data
+    const resp = await props.fetchLine(queryParams.value);
+    return resp.data;
   }
 }
-const getMetricsItems = async () => {
-  const resp = await props.fetchCard(queryParams.value)
-  const data = resp.data
-  metricsItems.value.length = 0
+
+async function getMetricsItems() {
+  const resp = await props.fetchCard(queryParams.value);
+  const data = resp.data;
+  metricsItems.value.length = 0;
   XEUtils.arrayEach(props.metricEnum, (info) => {
     const tmp: MetricData = {
       label: info.label,
@@ -271,46 +227,150 @@ const getMetricsItems = async () => {
       metricVal: data[info.value],
       gapVal: data[`gap${info.value}`],
       preVal: data[`prev${info.value}`],
-    }
-    metricsItems.value.push(tmp)
-  })
+    };
+    metricsItems.value.push(tmp);
+  });
 }
 
-const changeMetric = () => {
-  const opt = buildChartOpt(option, metrics.value)
-  chartObj.setOption(opt)
+async function initLine() {
+  chartObj = echarts.init(chartRef.value);
+  option.dataset.source = await getDataset();
+
+  // 初始化系列和 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 },
+    barMaxWidth: '16px',
+    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: any, metric) => {
+    acc[metric.label] = metrics.value.some((m) => m.metric === metric.value);
+    return acc;
+  }, {});
+
+  chartObj.setOption(option);
+  loading.value = false;
 }
 
-const changeStatDim = async () => {
-  loading.value = true
-  let source = await getDataset()
-  if (source.length > 0) {
-    chartObj.setOption({ dataset: { source: source } })
+function changeMetric() {
+  // 更新图例选中状态
+  option.legend.selected = props.metricEnum.reduce((acc: any, metric) => {
+    acc[metric.label] = metrics.value.some((m) => m.metric === metric.value);
+    return acc;
+  }, {});
+
+  // 重新创建 series 数组,只包含选中的指标
+  option.series = metrics.value.map((metric, index) => {
+    const baseConfig: any = {
+      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 },
+      barMaxWidth: '16px',
+    };
+
+    if (baseConfig.type != '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,
+  }));
+
+  // 确保只有必要的 y 轴
+  while (option.yAxis.length > metrics.value.length) {
+    option.yAxis.pop();
   }
-  loading.value = false
+
+  chartObj.setOption(option, true); // 使用 true 作为第二个参数,强制完全刷新
 }
 
-watch(props.query, async () => {
-  // console.log("------watch-----queryParams", props.query)
-  loading.value = true
-  await getMetricsItems()
-  const items = await getDataset()
-  const opt = { dataset: { source: items } }
-  chartObj.setOption(opt)
-  loading.value = false
-})
+async function changeStatDim() {
+  loading.value = true;
+  let source = await getDataset();
+  if (source.length > 0) {
+    chartObj.setOption({ dataset: { source: source } });
+  }
+  loading.value = false;
+}
 
-const resizeChart = () => {
-  chartObj.resize()
+function resizeChart() {
+  chartObj.resize();
 }
-const addResize = () => {
-  window.addEventListener('resize', resizeChart)
+
+function addResize() {
+  window.addEventListener('resize', resizeChart);
 }
-const removeResize = () => {
-  window.removeEventListener('resize', resizeChart)
+
+function 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 label="week" :disabled="!props.fetchLineWeek">周</el-radio-button>
+      <el-radio-button label="month" :disabled="!props.fetchLineWeek">月</el-radio-button>
+    </el-radio-group>
+    <div style="height: 350px" ref="chartRef"></div>
+  </div>
+</template>
+
 <style scoped>
 .metrics-cards {
   display: flex;