Переглянути джерело

✨ feat: 搜索词-分析页热力图优化

WanGxC 9 місяців тому
батько
коміт
e01dd38721

+ 5 - 0
src/views/searchTerm/analysisPage/IndicatorFunnel.vue

@@ -50,6 +50,11 @@ async function fetchFunnelData() {
     report_range: filter.value.reportType,
     layer_type: funnelFilter.layerSelect,
   };
+  // 根据条件有选择性地添加 [filter.value.layerType.split('_')[0]]: filter.value.variable
+  if (funnelFilter.layerSelect === 'asin_view' || funnelFilter.layerSelect === 'brand_view') {
+    query[filter.value.layerType.split('_')[0]] = filter.value.variable;
+  }
+
   try {
     const response = await api.getFunnelData(query);
     if (!response.data || Object.keys(response.data).length === 0) {

+ 104 - 50
src/views/searchTerm/analysisPage/IndicatorHeatmap.vue

@@ -55,36 +55,63 @@ async function fetchHeatmapData() {
       return;
     }
     hasData.value = true;
-    const days = responseData.data.map((item) => item.Reporting_Date); // y轴数据
-    const keywords = Object.keys(responseData.data[0]).filter((key) => key !== 'Reporting_Date').slice(0, 10); // x轴数据
+    const days = [...new Set(responseData.data.map(item => item.Reporting_Date))]; // x轴数据
+    const keywords = [...new Set(responseData.data.flatMap(item =>
+        Object.keys(item).filter(key => key !== 'Reporting_Date')
+    ))]; // y轴数据
     const data = [];
 
     // 找出所有数值的最大值,用于设置 visualMap 的 max 值
     const maxValue = Math.max(
-      ...responseData.data.flatMap((item) =>
-        Object.entries(item)
-          .filter(([key]) => key !== 'Reporting_Date')
-          .map(([, value]) => value as number)
-      )
+        ...responseData.data.flatMap(item =>
+            Object.values(item).filter(value => typeof value === 'number') as number[]
+        )
     );
 
-    responseData.data.forEach((item, yIndex) => {
-      keywords.forEach((keyword, xIndex) => {
+    responseData.data.forEach(item => {
+      const dayIndex = days.indexOf(item.Reporting_Date);
+      keywords.forEach((keyword: any, keywordIndex) => {
         if (item[keyword] !== undefined && item[keyword] !== null) {
-          data.push([xIndex, yIndex, item[keyword]]); // 只添加非空值
+          data.push([dayIndex, keywordIndex, item[keyword]]); // [x, y, value]
         }
       });
     });
 
     const option = {
+      title: {
+        text: '搜索词时间段对比热力图',
+      },
+      position: 'top',
       tooltip: {
         position: 'top',
+        formatter: function(params) {
+          const keyword = keywords[params.value[1]]; // 获取当前悬浮的关键词
+          return `${keyword}<br>${params.seriesName}: ${params.value[2]}`;
+        }
       },
       grid: {
-        height: '65%',
-        top: '15%',
+        // height: '65%',
+        top: '10%',
+        bottom: '15%',
+        left: '15%',
+        right: '5%',
       },
-      position: 'top',
+      series: [
+        {
+          name: keywords,
+          type: 'heatmap',
+          data: data,
+          label: {
+            show: true,
+          },
+          emphasis: {
+            itemStyle: {
+              shadowBlur: 10,
+              shadowColor: 'rgba(0, 0, 0, 0.5)',
+            },
+          },
+        },
+      ],
       xAxis: {
         type: 'category',
         name: '日期',
@@ -103,7 +130,7 @@ async function fetchHeatmapData() {
             return value;
           },
         },
-        data: keywords,
+        data: days,
         splitArea: {
           show: true,
         },
@@ -117,39 +144,45 @@ async function fetchHeatmapData() {
           fontSize: 14,
           color: '#333',
         },
-        // nameTruncate: {
-        //   maxWidth: 20,
-        //   ellipsis: '...'
-        // },
-        data: days,
+        data: keywords,
         splitArea: {
           show: true,
         },
+        axisLabel: {
+          interval: 0,  // 显示所有标签
+          width: 280,  // 增加标签区域的宽度以适应文本
+          rotate: 15,  // 标签旋转角度
+        },
       },
+      dataZoom: [
+        {
+          type: 'slider',  // 纵向滚动条类型
+          yAxisIndex: 0,  // 指定作用于 Y 轴
+          // start: 100,  // 初始位置,百分比形式,数值越小,滚动条越靠下
+          // end: 30,
+          startValue: 0,  // 初始位置,数值形式,数值越小,滚动条越靠上
+          endValue: 20,
+          zoomLock: true,  // 锁定缩放,避免同时缩放两个滚动条
+          showDetail: false,  // 显示缩放的细节
+          brushSelect: false,
+        },
+        {
+          type: 'inside',  // 内置型数据区域缩放组件(使用鼠标滚轮和拖拽)
+          yAxisIndex: 0,
+          zoomOnMouseWheel: false,
+          moveOnMouseMove: true,
+          moveOnMouseWheel: true,
+        }
+      ],
       visualMap: {
         min: 0,
         max: maxValue, // 使用计算出的最大值
+        itemHeight: 500,
         calculable: true,
         orient: 'horizontal',
         left: 'center',
         bottom: '2%',
       },
-      series: [
-        {
-          name: 'Punch Card',
-          type: 'heatmap',
-          data: data,
-          label: {
-            show: true,
-          },
-          emphasis: {
-            itemStyle: {
-              shadowBlur: 10,
-              shadowColor: 'rgba(0, 0, 0, 0.5)',
-            },
-          },
-        },
-      ],
     };
 
     if (!chart) {
@@ -175,25 +208,46 @@ async function fetchHeatmapData() {
 
 <template>
   <el-card shadow="never" v-loading="heatmapLoading" class="flex flex-col" body-class="w-full">
-    <div class="font-bold text-xl mb-4 text-center" style="color: #464646">搜索词时间段对比热力图</div>
-    <div class="text-center">
-      <el-radio-group v-model="filter.metric" @change="fetchHeatmapData">
-        <el-radio-button label="Search_Query_Score" value="Search_Query_Score" />
-        <el-radio-button label="Search_Query_Volume" value="Search_Query_Volume" />
-        <el-radio-button label="Impressions_Total_Count" value="Impressions_Total_Count" />
-        <el-radio-button label="Impressions_A_B_Count" value="Impressions_A_B_Count" />
-        <el-radio-button label="Clicks_Total_Count" value="Clicks_Total_Count" />
-        <el-radio-button label="Clicks_Price_Median" value="Clicks_Price_Median" />
-        <el-radio-button label="Cart_Adds_Total_Count" value="Cart_Adds_Total_Count" />
-        <el-radio-button label="Purchases_Total_Count" value="Purchases_Total_Count" />
-      </el-radio-group>
+
+    <div class="text-center flex justify-center items-center">
+      <span class="font-medium mr-1.5">指标 </span>
+      <el-select v-model="filter.metric" @change="fetchHeatmapData" style="width: 200px">
+        <el-option label="搜索查询分数" value="Search_Query_Score"></el-option>
+        <el-option label="搜索查询数量" value="Search_Query_Volume"></el-option>
+
+        <el-option label="展示量-总数" value="Impressions_Total_Count"></el-option>
+        <el-option v-if="filter.layerType === 'asin_view'" label="展示量-ASIN数量" value="Impressions_A_B_Count"></el-option>
+        <el-option v-if="filter.layerType === 'brand_view'" label="展示量-品牌数量" value="Impressions_A_B_Count"></el-option>
+
+        <el-option label="点击量-总数" value="Clicks_Total_Count"></el-option>
+        <el-option v-if="filter.layerType === 'asin_view'" label="点击量-ASIN数量" value="Clicks_A_B_Count"></el-option>
+        <el-option v-if="filter.layerType === 'brand_view'" label="点击量-品牌数量" value="Clicks_A_B_Count"></el-option>
+        <el-option label="点击量-价格中位数" value="Clicks_Price_Median"></el-option>
+        <el-option v-if="filter.layerType === 'asin_view'" label="点击量-ASIN价格中位数" value="Clicks_A_B_Price_Median"></el-option>
+        <el-option v-if="filter.layerType === 'brand_view'" label="点击量-品牌价格中位数" value="Clicks_A_B_Price_Median"></el-option>
+
+        <el-option label="加购-总数" value="Cart_Adds_Total_Count"></el-option>
+        <el-option v-if="filter.layerType === 'asin_view'" label="加购-ASIN数量" value="Cart_Adds_A_B_Count"></el-option>
+        <el-option v-if="filter.layerType === 'brand_view'" label="加购-品牌数量" value="Cart_Adds_A_B_Count"></el-option>
+        <el-option label="加购-价格中位数" value="Cart_Adds_Price_Median"></el-option>
+        <el-option v-if="filter.layerType === 'asin_view'" label="加购-ASIN价格中位数" value="Cart_Adds_A_B_Price_Median"></el-option>
+        <el-option v-if="filter.layerType === 'brand_view'" label="加购-品牌价格中位数" value="Cart_Adds_A_B_Price_Median"></el-option>
+
+
+        <el-option label="购买-总数" value="Purchases_Total_Count"></el-option>
+        <el-option v-if="filter.layerType === 'asin_view'" label="购买-ASIN数量" value="Purchases_A_B_Count"></el-option>
+        <el-option v-if="filter.layerType === 'brand_view'" label="购买-品牌数量" value="Purchases_A_B_Count"></el-option>
+        <el-option label="购买-价格中位数" value="Purchases_Price_Median"></el-option>
+        <el-option v-if="filter.layerType === 'asin_view'" label="购买-ASIN价格中位数" value="Purchases_A_B_Price_Median"></el-option>
+        <el-option v-if="filter.layerType === 'brand_view'" label="购买-品牌价格中位数" value="Purchases_A_B_Price_Median"></el-option>
+      </el-select>
     </div>
 
     <div class="w-full">
-      <div v-show="!heatmapLoading && !hasData" style="min-height: 500px">
-        <el-empty :image-size="300" />
+      <div v-if="!heatmapLoading && !hasData" style="min-height: 800px" class="flex justify-center items-center">
+        <el-empty :image-size="300"  />
       </div>
-      <div v-show="hasData" ref="chartRef" style="width: 100%; height: 500px"></div>
+      <div v-show="hasData" ref="chartRef" style="width: 100%; height: 800px"></div>
     </div>
   </el-card>
 </template>

+ 1 - 1
src/views/searchTerm/analysisPage/index.vue

@@ -17,7 +17,7 @@ const filter = ref({
   searchTerm: '',
   reportType: 'MONTHLY',
   reportDate: [dayjs().format('YYYY-MM-DD'), dayjs().subtract(1, 'month').format('YYYY-MM-DD')],
-  variable: 'B00TEST01',
+  variable: 'B00TEST0001',
   metric: 'Search_Query_Score',
 });
 provide('filter', filter);