Pārlūkot izejas kodu

Merge branch 'refs/heads/wang' into test

WanGxC 10 mēneši atpakaļ
vecāks
revīzija
6ad84e264d

+ 1 - 1
src/layout/routerView/parent.vue

@@ -3,7 +3,7 @@
 		<router-view v-slot="{ Component }">
 			<transition :name="setTransitionName" mode="out-in">
 				<keep-alive :include="getKeepAliveNames" v-if="showView">
-					<div>
+					<div class="flex-1"> <!-- flex-1 避免 layout main 的内容不能全屏 -->
 						<component :is="Component" :key="state.refreshRouterViewKey" class="w100" v-show="!isIframePage" />
 					</div>
 				</keep-alive>

+ 44 - 68
src/views/adManage/sb/chartComponents/dataTendency.vue

@@ -181,6 +181,16 @@ onMounted(() => {
   }, 0);
 });
 
+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;
+});
+
 onBeforeUnmount(() => {
   if (chartObj) {
     chartObj.dispose();
@@ -189,15 +199,37 @@ onBeforeUnmount(() => {
   removeResize();
 });
 
-const metricColors = {
-  Impression: '#0085ff',
-  Click: '#3fd4cf',
-  Spend: '#ff9500',
-  // ... 其他指标的颜色
-};
+async function getDataset() {
+  if (statDim.value === 'week') {
+    if (props.fetchLineWeek) {
+      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;
+    }
+  } else {
+    const resp = await props.fetchLine(queryParams.value);
+    return resp.data;
+  }
+}
 
-function getColorForMetric(metric: string): string {
-  return metricColors[metric] || '#999999'; // 如果没有预定义的颜色,返回一个默认颜色
+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,
+      value: info.value,
+      metricVal: data[info.value],
+      gapVal: data[`gap${info.value}`],
+      preVal: data[`prev${info.value}`],
+    };
+    metricsItems.value.push(tmp);
+  });
 }
 
 async function initLine() {
@@ -239,7 +271,7 @@ async function initLine() {
   }));
 
   // 初始化图例
-  option.legend.selected = props.metricEnum.reduce((acc, metric) => {
+  option.legend.selected = props.metricEnum.reduce((acc: any, metric) => {
     acc[metric.label] = metrics.value.some((m) => m.metric === metric.value);
     return acc;
   }, {});
@@ -248,54 +280,16 @@ async function initLine() {
   loading.value = false;
 }
 
-async function getDataset() {
-  if (statDim.value === 'week') {
-    if (props.fetchLineWeek) {
-      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;
-    }
-  } else {
-    const resp = await props.fetchLine(queryParams.value);
-    return resp.data;
-  }
-}
-
-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,
-      value: info.value,
-      metricVal: data[info.value],
-      gapVal: data[`gap${info.value}`],
-      preVal: data[`prev${info.value}`],
-    };
-    metricsItems.value.push(tmp);
-  });
-}
-
-// const changeMetric = () => {
-//   const opt = buildChartOpt(option, metrics.value)
-//   chartObj.setOption(opt)
-// }
-
 function changeMetric() {
   // 更新图例选中状态
-  option.legend.selected = props.metricEnum.reduce((acc, metric) => {
+  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 = {
+    const baseConfig: any = {
       id: index,
       name: metric.label,
       type: metric.color === '#0085ff' ? 'bar' : 'line',
@@ -306,7 +300,7 @@ function changeMetric() {
       barWidth: '10%',
     };
 
-    if (baseConfig != 'bar') {
+    if (baseConfig.type != 'bar') {
       baseConfig.areaStyle = {
         color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
           { offset: 0, color: metric.color + '53' },
@@ -343,14 +337,6 @@ function changeMetric() {
   chartObj.setOption(option, true); // 使用 true 作为第二个参数,强制完全刷新
 }
 
-watch(
-  metrics,
-  () => {
-    changeMetric();
-  },
-  { deep: true }
-);
-
 async function changeStatDim() {
   loading.value = true;
   let source = await getDataset();
@@ -360,16 +346,6 @@ async function changeStatDim() {
   loading.value = false;
 }
 
-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;
-});
-
 function resizeChart() {
   chartObj.resize();
 }

+ 49 - 72
src/views/adManage/sd/chartComponents/dataTendency.vue

@@ -116,7 +116,7 @@ const option: any = {
         x: 'Name',
         y: '',
       },
-      barWidth: '18px',
+      barWidth: '16px',
       yAxisIndex: 0,
       itemStyle: {
         color: '',
@@ -189,15 +189,47 @@ onBeforeUnmount(() => {
   removeResize();
 });
 
-const metricColors = {
-  Impression: '#0085ff',
-  Click: '#3fd4cf',
-  Spend: '#ff9500',
-  // ... 其他指标的颜色
-};
+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 getDataset() {
+  if (statDim.value === 'week') {
+    if (props.fetchLineWeek) {
+      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;
+    }
+  } else {
+    const resp = await props.fetchLine(queryParams.value);
+    return resp.data;
+  }
+}
 
-function getColorForMetric(metric: string): string {
-  return metricColors[metric] || '#999999'; // 如果没有预定义的颜色,返回一个默认颜色
+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,
+      value: info.value,
+      metricVal: data[info.value],
+      gapVal: data[`gap${info.value}`],
+      preVal: data[`prev${info.value}`],
+    };
+    metricsItems.value.push(tmp);
+  });
 }
 
 async function initLine() {
@@ -213,7 +245,7 @@ async function initLine() {
     yAxisIndex: index,
     itemStyle: { color: option.series.type == 'bar' ? '#0085ff' : metric.color, borderRadius: [6, 6, 6, 6] },
     lineStyle: { color: metric.color },
-    barWidth: '10%',
+    barWidth: '16px',
     areaStyle:
       index !== 0
         ? {
@@ -239,7 +271,7 @@ async function initLine() {
   }));
 
   // 初始化图例
-  option.legend.selected = props.metricEnum.reduce((acc, metric) => {
+  option.legend.selected = props.metricEnum.reduce((acc: any, metric) => {
     acc[metric.label] = metrics.value.some((m) => m.metric === metric.value);
     return acc;
   }, {});
@@ -248,54 +280,16 @@ async function initLine() {
   loading.value = false;
 }
 
-async function getDataset() {
-  if (statDim.value === 'week') {
-    if (props.fetchLineWeek) {
-      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;
-    }
-  } else {
-    const resp = await props.fetchLine(queryParams.value);
-    return resp.data;
-  }
-}
-
-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,
-      value: info.value,
-      metricVal: data[info.value],
-      gapVal: data[`gap${info.value}`],
-      preVal: data[`prev${info.value}`],
-    };
-    metricsItems.value.push(tmp);
-  });
-}
-
-// const changeMetric = () => {
-//   const opt = buildChartOpt(option, metrics.value)
-//   chartObj.setOption(opt)
-// }
-
 function changeMetric() {
   // 更新图例选中状态
-  option.legend.selected = props.metricEnum.reduce((acc, metric) => {
+  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 = {
+    const baseConfig: any = {
       id: index,
       name: metric.label,
       type: metric.color === '#0085ff' ? 'bar' : 'line',
@@ -303,10 +297,10 @@ function changeMetric() {
       yAxisIndex: index,
       itemStyle: { color: metric.color, borderRadius: [6, 6, 6, 6] },
       lineStyle: { color: metric.color },
-      barWidth: '10%',
+      barWidth: '16px',
     };
 
-    if (baseConfig != 'bar') {
+    if (baseConfig.type != 'bar') {
       baseConfig.areaStyle = {
         color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
           { offset: 0, color: metric.color + '53' },
@@ -343,31 +337,14 @@ function changeMetric() {
   chartObj.setOption(option, true); // 使用 true 作为第二个参数,强制完全刷新
 }
 
-watch(
-  metrics,
-  () => {
-    changeMetric();
-  },
-  { deep: true }
-);
-const changeStatDim = async () => {
+async function changeStatDim() {
   loading.value = true;
   let source = await getDataset();
   if (source.length > 0) {
     chartObj.setOption({ dataset: { source: source } });
   }
   loading.value = false;
-};
-
-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;
-});
+}
 
 function resizeChart() {
   chartObj.resize();

+ 47 - 71
src/views/adManage/sp/chartComponents/dataTendency.vue

@@ -116,7 +116,7 @@ const option: any = {
         x: 'Name',
         y: '',
       },
-      barWidth: '18px',
+      barWidth: '186px',
       yAxisIndex: 0,
       itemStyle: {
         color: '',
@@ -189,15 +189,47 @@ onBeforeUnmount(() => {
   removeResize();
 });
 
-const metricColors = {
-  Impression: '#0085ff',
-  Click: '#3fd4cf',
-  Spend: '#ff9500',
-  // ... 其他指标的颜色
-};
+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 getDataset() {
+  if (statDim.value === 'week') {
+    if (props.fetchLineWeek) {
+      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;
+    }
+  } else {
+    const resp = await props.fetchLine(queryParams.value);
+    return resp.data;
+  }
+}
 
-function getColorForMetric(metric: string): string {
-  return metricColors[metric] || '#999999'; // 如果没有预定义的颜色,返回一个默认颜色
+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,
+      value: info.value,
+      metricVal: data[info.value],
+      gapVal: data[`gap${info.value}`],
+      preVal: data[`prev${info.value}`],
+    };
+    metricsItems.value.push(tmp);
+  });
 }
 
 async function initLine() {
@@ -213,7 +245,7 @@ async function initLine() {
     yAxisIndex: index,
     itemStyle: { color: option.series.type == 'bar' ? '#0085ff' : metric.color, borderRadius: [6, 6, 6, 6] },
     lineStyle: { color: metric.color },
-    barWidth: '10%',
+    barWidth: '16px',
     areaStyle:
       index !== 0
         ? {
@@ -239,7 +271,7 @@ async function initLine() {
   }));
 
   // 初始化图例
-  option.legend.selected = props.metricEnum.reduce((acc, metric) => {
+  option.legend.selected = props.metricEnum.reduce((acc: any, metric) => {
     acc[metric.label] = metrics.value.some((m) => m.metric === metric.value);
     return acc;
   }, {});
@@ -248,54 +280,16 @@ async function initLine() {
   loading.value = false;
 }
 
-async function getDataset() {
-  if (statDim.value === 'week') {
-    if (props.fetchLineWeek) {
-      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;
-    }
-  } else {
-    const resp = await props.fetchLine(queryParams.value);
-    return resp.data;
-  }
-}
-
-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,
-      value: info.value,
-      metricVal: data[info.value],
-      gapVal: data[`gap${info.value}`],
-      preVal: data[`prev${info.value}`],
-    };
-    metricsItems.value.push(tmp);
-  });
-}
-
-// const changeMetric = () => {
-//   const opt = buildChartOpt(option, metrics.value)
-//   chartObj.setOption(opt)
-// }
-
 function changeMetric() {
   // 更新图例选中状态
-  option.legend.selected = props.metricEnum.reduce((acc, metric) => {
+  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 = {
+    const baseConfig: any = {
       id: index,
       name: metric.label,
       type: metric.color === '#0085ff' ? 'bar' : 'line',
@@ -303,10 +297,10 @@ function changeMetric() {
       yAxisIndex: index,
       itemStyle: { color: metric.color, borderRadius: [6, 6, 6, 6] },
       lineStyle: { color: metric.color },
-      barWidth: '10%',
+      barWidth: '16px',
     };
 
-    if (baseConfig != 'bar') {
+    if (baseConfig.type != 'bar') {
       baseConfig.areaStyle = {
         color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
           { offset: 0, color: metric.color + '53' },
@@ -343,14 +337,6 @@ function changeMetric() {
   chartObj.setOption(option, true); // 使用 true 作为第二个参数,强制完全刷新
 }
 
-watch(
-  metrics,
-  () => {
-    changeMetric();
-  },
-  { deep: true }
-);
-
 async function changeStatDim() {
   loading.value = true;
   let source = await getDataset();
@@ -360,16 +346,6 @@ async function changeStatDim() {
   loading.value = false;
 }
 
-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;
-});
-
 function resizeChart() {
   chartObj.resize();
 }

+ 22 - 30
src/views/productCenter/productAnalysis/components/DateTendency/index.vue

@@ -12,13 +12,12 @@
 
 <script lang="ts" setup>
 import * as echarts from 'echarts';
-import { Ref, computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
+import { computed, onBeforeUnmount, onMounted, Ref, ref, watch } from 'vue';
 import { productListMetricsEnum } from '/@/views/productCenter/productList/utils/enum';
-// import MetricsCards from '/@/components/MetricsCards/index.vue'
 import XEUtils from 'xe-utils';
 import MetricsCards from '../MetricsCards/index.vue';
 import emitter from '/@/utils/emitter';
-import { buildChartOpt, parseQueryParams } from '/@/views/adManage/utils/tools.js';
+import { parseQueryParams } from '/@/views/adManage/utils/tools.js';
 
 defineOptions({
   name: 'DataTendencyChart',
@@ -153,7 +152,7 @@ const option: any = {
         x: 'Name',
         y: '',
       },
-      barWidth: '18px',
+      barWidth: '16px',
       yAxisIndex: 0,
       itemStyle: {
         color: '',
@@ -238,6 +237,7 @@ onMounted(() => {
     initLine();
   }, 0);
 });
+
 onBeforeUnmount(() => {
   if (chartObj) {
     chartObj.dispose();
@@ -246,6 +246,18 @@ onBeforeUnmount(() => {
   removeResize();
 });
 
+watch(props.query, async () => {
+  // console.log("------watch-----queryParams", props.query)
+  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);
+  loading.value = false;
+});
+
 // const initLine = async () => {
 //   chartObj = echarts.init(chartRef.value)
 //   const items = await getDataset()
@@ -299,7 +311,7 @@ async function initLine() {
     yAxisIndex: index,
     itemStyle: { color: index === 0 ? '#0085ff' : metric.color, borderRadius: [6, 6, 6, 6] },
     lineStyle: { color: metric.color },
-    barWidth: '10%',
+    barWidth: '16px',
     areaStyle:
       index !== 0
         ? {
@@ -328,7 +340,7 @@ async function initLine() {
   }));
 
   // 初始化图例
-  option.legend.selected = props.metricEnum.reduce((acc, metric) => {
+  option.legend.selected = props.metricEnum.reduce((acc: any, metric) => {
     acc[metric.label] = limitedMetrics.some((m) => m.metric === metric.value);
     return acc;
   }, {});
@@ -341,14 +353,14 @@ async function initLine() {
 function changeMetric() {
   const limitedMetrics = metrics.value.slice(0, 3);
   // 更新图例选中状态
-  option.legend.selected = props.metricEnum.reduce((acc, metric) => {
+  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 = limitedMetrics.map((metric, index) => {
-    const baseConfig = {
+    const baseConfig: any = {
       id: index,
       name: metric.label,
       type: metric.color === '#0085ff' ? 'bar' : 'line',
@@ -356,10 +368,10 @@ function changeMetric() {
       yAxisIndex: index,
       itemStyle: { color: metric.color, borderRadius: [6, 6, 6, 6] },
       lineStyle: { color: metric.color },
-      barWidth: '10%',
+      barWidth: '16px',
     };
 
-    if (baseConfig != 'bar') {
+    if (baseConfig.type != 'bar') {
       baseConfig.areaStyle = {
         color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
           { offset: 0, color: metric.color + '53' },
@@ -429,14 +441,6 @@ async function getMetricsItems() {
   });
 }
 
-watch(
-    metrics,
-    () => {
-      changeMetric();
-    },
-    { deep: true }
-);
-
 const emit = defineEmits(['changeStatDim']);
 
 async function changeStatDim() {
@@ -450,18 +454,6 @@ async function changeStatDim() {
   emit('changeStatDim', source); // 向父组件传递数据后再传递给DataTable
 }
 
-watch(props.query, async () => {
-  // console.log("------watch-----queryParams", props.query)
-  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);
-  loading.value = false;
-});
-
 function resizeChart() {
   chartObj.resize();
 }

+ 28 - 55
src/views/productCenter/productList/components/DateTendency/index.vue

@@ -11,16 +11,13 @@
 </template>
 
 <script lang="ts" setup>
-import { ref, onMounted, onBeforeUnmount, Ref, unref, watch, computed } from 'vue'
-import * as echarts from 'echarts'
-import { productListMetricsEnum } from '/@/views/productCenter/productList/utils/enum'
-// import MetricsCards from '/@/components/MetricsCards/index.vue'
-import MetricsCards from '../MetricsCards/index.vue'
-import XEUtils from 'xe-utils'
-import { buildChartOpt, parseQueryParams } from '/@/views/adManage/utils/tools.js'
+import { computed, onBeforeUnmount, onMounted, Ref, ref, watch } from 'vue';
+import * as echarts from 'echarts';
+import MetricsCards from '../MetricsCards/index.vue';
+import XEUtils from 'xe-utils';
+import { parseQueryParams } from '/@/views/adManage/utils/tools.js';
 import { spCampaignMetricsEnum } from '/@/views/adManage/utils/enum';
 
-
 defineOptions({
   name: 'DataTendencyChart',
 });
@@ -131,7 +128,7 @@ const option: any = {
         x: 'Name',
         y: '',
       },
-      barWidth: '18px',
+      barWidth: '16px',
       yAxisIndex: 0,
       itemStyle: {
         color: '',
@@ -204,16 +201,15 @@ onBeforeUnmount(() => {
   removeResize();
 });
 
-const metricColors = {
-  Impression: '#0085ff',
-  Click: '#3fd4cf',
-  Spend: '#ff9500',
-  // ... 其他指标的颜色
-};
-
-function getColorForMetric(metric: string): string {
-  return metricColors[metric] || '#999999'; // 如果没有预定义的颜色,返回一个默认颜色
-}
+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 initLine() {
   chartObj = echarts.init(chartRef.value);
@@ -228,16 +224,16 @@ async function initLine() {
     yAxisIndex: index,
     itemStyle: { color: option.series.type == 'bar' ? '#0085ff' : metric.color, borderRadius: [6, 6, 6, 6] },
     lineStyle: { color: metric.color },
-    barWidth: '10%',
+    barWidth: '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,
+      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) => ({
@@ -254,7 +250,7 @@ async function initLine() {
   }));
 
   // 初始化图例
-  option.legend.selected = props.metricEnum.reduce((acc, metric) => {
+  option.legend.selected = props.metricEnum.reduce((acc: any, metric) => {
     acc[metric.label] = metrics.value.some((m) => m.metric === metric.value);
     return acc;
   }, {});
@@ -296,21 +292,16 @@ async function getMetricsItems() {
   });
 }
 
-// const changeMetric = () => {
-//   const opt = buildChartOpt(option, metrics.value)
-//   chartObj.setOption(opt)
-// }
-
 function changeMetric() {
   // 更新图例选中状态
-  option.legend.selected = props.metricEnum.reduce((acc, metric) => {
+  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 = {
+    const baseConfig: any = {
       id: index,
       name: metric.label,
       type: metric.color === '#0085ff' ? 'bar' : 'line',
@@ -318,7 +309,7 @@ function changeMetric() {
       yAxisIndex: index,
       itemStyle: { color: metric.color, borderRadius: [6, 6, 6, 6] },
       lineStyle: { color: metric.color },
-      barWidth: '10%',
+      barWidth: '16px',
     };
 
     if (baseConfig != 'bar') {
@@ -358,14 +349,6 @@ function changeMetric() {
   chartObj.setOption(option, true); // 使用 true 作为第二个参数,强制完全刷新
 }
 
-watch(
-    metrics,
-    () => {
-      changeMetric();
-    },
-    { deep: true }
-);
-
 async function changeStatDim() {
   loading.value = true;
   let source = await getDataset();
@@ -375,16 +358,6 @@ async function changeStatDim() {
   loading.value = false;
 }
 
-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;
-});
-
 function resizeChart() {
   chartObj.resize();
 }

+ 24 - 0
src/views/searchTerm/asinView/api.ts

@@ -0,0 +1,24 @@
+import { request } from '/@/utils/service';
+
+const apiPrefix = '/api/searchterm/';
+
+
+export function getTableData(query: any) {
+  return request({
+    url: apiPrefix + 'asinview/',
+    method: 'GET',
+    params: query,
+  });
+}
+
+export function postDownload(body: any) {
+  return request({
+    url: apiPrefix + 'topsearchtermTable/',
+    method: 'POST',
+    params: body,
+    responseType: 'blob',
+    headers: {
+      'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' // 指定接受 Excel 文件
+    }
+  });
+}

+ 295 - 0
src/views/searchTerm/asinView/index.vue

@@ -0,0 +1,295 @@
+<script setup lang="ts">
+/**
+ * @Name: index.vue
+ * @Description: asinView
+ * @Author: Cheney
+ */
+
+import { Download, Refresh, Search } from '@element-plus/icons-vue';
+import { nextTick, onBeforeMount, reactive, ref, watch } from 'vue';
+import dayjs from 'dayjs';
+import { getTableData, postDownload } from './api';
+import { ElMessage } from 'element-plus';
+import { asinColumns } from './useColumns';
+import { brandColumns } from '/@/views/searchTerm/brandView/useColumns';
+
+const reportTypeSelect = ref('monthly');
+const searchTermInp = ref('');
+const asinInp = ref('B0');
+const tableLoading = ref(false);
+const downloadLoading = ref(false);
+const date = ref(calculateLastWeek());
+
+const gridOptions: any = reactive({
+  height: 'auto',
+  border: false,
+  round: true,
+  columnConfig: {
+    resizable: true,
+  },
+  toolbarConfig: {
+    custom: true,
+    slots: {
+      buttons: 'toolbar_buttons',
+    },
+  },
+  columns: asinColumns,
+  data: [],
+});
+
+const tablePage = reactive({
+  total: 0,
+  currentPage: 1,
+  pageSize: 20,
+});
+
+onBeforeMount(() => {
+  fetchTableData();
+});
+
+watch(date, () => {
+  fetchTableData();
+});
+
+async function handlePageChange({ currentPage, pageSize }) {
+  tablePage.currentPage = currentPage;
+  tablePage.pageSize = pageSize;
+  await fetchTableData();
+}
+
+async function handleSelectChange() {
+  if (!asinInp.value) {
+    ElMessage.warning({ message: '请输入ASIN', plain: true });
+    return;
+  } else {
+    await fetchTableData();
+  }
+
+}
+
+async function refreshTable() {
+  tablePage.currentPage = 1;
+  tablePage.pageSize = 20;
+  asinInp.value = '';
+  searchTermInp.value = '';
+  reportTypeSelect.value = 'weekly';
+  // marketplaceSelect.value = marketplaceIdEnum[0].value;
+  await fetchTableData();
+}
+
+async function fetchTableData() {
+  tableLoading.value = true;
+  const query = {
+    page: tablePage.currentPage,
+    limit: tablePage.pageSize,
+    asin: asinInp.value,
+    search_term: searchTermInp.value,
+    report_type: reportTypeSelect.value,
+    // marketplace_Ids: marketplaceSelect.value,
+    date_start: date.value[0],
+    date_end: date.value[1],
+  };
+  try {
+    const response = await getTableData(query);
+    tablePage.total = response.total;
+    gridOptions.data = response.data;
+  } catch (error) {
+    console.error('==Error==:', error);
+  } finally {
+    tableLoading.value = false;
+  }
+}
+
+/**
+ * 输入框按下回车后触发
+ */
+async function handleQueryChange() {
+  if (!validateSearchTermInput(searchTermInp.value)) {
+    if (searchTermInp.value.length == 0) {
+      return;
+    } else {
+      ElMessage.warning({ message: '搜索词只能输入数字和英文字母', plain: true });
+      return;
+    }
+  }
+  if (asinInp.value.length > 0 && !validateAsinInput(asinInp.value)) {
+    ElMessage.warning({ message: '不符合匹配规范', plain: true });
+    return;
+  }
+  await fetchTableData();
+}
+
+/**
+ * 校验SearchTerm输入是否合法
+ * @param input 输入的字符串
+ */
+function validateSearchTermInput(input: string) {
+  const regex = /^[a-zA-Z0-9\s]*$/;
+  return regex.test(input);
+}
+
+/**
+ * 校验Asin输入是否合法
+ * @param input 输入的字符串
+ */
+function validateAsinInput(input: string) {
+  const regex = /^[Bb]0[A-Za-z0-9\s]*$/i;
+  return regex.test(input);
+}
+
+/**
+ * 计算上周的周日到周六的日期范围
+ */
+function calculateLastWeek() {
+  const today = dayjs();
+  const lastDay = today.subtract(1, 'day'); // 昨天
+  const firstDay = lastDay.subtract(6, 'day'); // 一周前
+  return [firstDay.format('YYYY-MM-DD'), lastDay.format('YYYY-MM-DD')];
+}
+
+// async function handleDownload() {
+//   downloadLoading.value = true;
+//   try {
+//     const body = {
+//       asin: asinInp.value,
+//       date_start: date.value[0],
+//       date_end: date.value[1],
+//       search_term: searchTermInp.value,
+//       marketplace_Ids: marketplaceSelect.value,
+//       report_type: reportTypeSelect.value,
+//     };
+//
+//     const response = await postDownload(body);
+//
+//     const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
+//
+//     // 创建一个临时 URL
+//     const url = window.URL.createObjectURL(blob);
+//
+//     // 创建一个临时的 <a> 元素并触发下载
+//     const link = document.createElement('a');
+//     link.href = url;
+//
+//     // 设置文件名
+//     const currentTime = dayjs().format('YYYY-MM-DD_HH_mm_ss');
+//     const filename = `TopSearchTerm_${currentTime}.xlsx`;
+//
+//     link.setAttribute('download', filename);
+//
+//     // 添加到 body, 触发点击, 然后移除
+//     document.body.appendChild(link);
+//     link.click();
+//     document.body.removeChild(link);
+//
+//     // 释放 URL 对象
+//     window.URL.revokeObjectURL(url);
+//
+//     ElMessage.success('文件下载成功');
+//   } catch (error) {
+//     console.error('==Error==:', error);
+//     ElMessage.error('文件下载失败,请重试');
+//   } finally {
+//     downloadLoading.value = false;
+//   }
+// }
+</script>
+
+<template>
+  <div class="py-2 px-2.5" style="background-color: #f7f7f7">
+    <el-card shadow="hover" class="mb-2.5" style="border: none; margin-bottom: 10px">
+      <div class="flex justify-between">
+        <div class="flex gap-5 flex-wrap">
+          <div>
+            <span class="font-medium mr-0.5">报告类型 </span>
+            <el-select v-model="reportTypeSelect" @change="handleSelectChange" style="width: 90px">
+              <el-option label="周度" value="weekly" />
+              <el-option label="月度" value="monthly" />
+            </el-select>
+          </div>
+          <div>
+            <span class="font-medium mr-0.5">搜索词 </span>
+            <el-input
+              v-model="searchTermInp"
+              @keyup.enter="handleQueryChange"
+              :prefix-icon="Search"
+              placeholder="输入后回车查询"
+              clearable
+              @clear="handleSelectChange"
+              style="width: 240px" />
+          </div>
+          <div>
+            <span class="font-medium mr-0.5">ASIN </span>
+            <el-input
+              v-model="asinInp"
+              @keyup.enter="handleQueryChange"
+              :prefix-icon="Search"
+              placeholder="输入后回车查询"
+              clearable
+              @clear="handleSelectChange"
+              style="width: 180px" />
+          </div>
+          <div>
+            <span class="font-medium mr-0.5">报告日期 </span>
+            <el-date-picker
+              v-model="date"
+              type="daterange"
+              value-format="YYYY-MM-DD"
+              range-separator="To"
+              :disabled-date="(time: Date) => time > new Date()"
+              :popper-options="{ placement: 'bottom-end' }"
+              :clearable="false" />
+          </div>
+        </div>
+        <div class="flex">
+          <!--<el-button-->
+          <!--  type="success"-->
+          <!--  plain-->
+          <!--  @click="handleDownload"-->
+          <!--  :icon="Download"-->
+          <!--  round-->
+          <!--  :loading="downloadLoading"-->
+          <!--  :disabled="!tableData.length"-->
+          <!--  >下载表格-->
+          <!--</el-button>-->
+          <el-button @click="refreshTable" :icon="Refresh" circle></el-button>
+        </div>
+      </div>
+    </el-card>
+    <el-card shadow="hover" style="border: none">
+      <div style="overflow: hidden; width: 100%; height: 950px" v-loading="tableLoading">
+        <vxe-grid v-bind="gridOptions">
+          <template #toolbar_buttons></template>
+          <template v-for="col in asinColumns" #[`${col.field}_default`]="{ row }">
+            <div v-if="col.field === 'clickedItemName'">
+              <el-tooltip effect="dark" :content="row.clickedItemName" placement="top" :show-after="300" >
+                <div class="line-text font-medium">
+                  {{ row.clickedItemName }}
+                </div>
+              </el-tooltip>
+            </div>
+            <div v-else class="font-medium">
+              {{ row[col.field] ? row[col.field] : '-' }}
+            </div>
+          </template>
+          <template #pager>
+            <vxe-pager
+              :layouts="['Sizes', 'PrevJump', 'PrevPage', 'Number', 'NextPage', 'NextJump', 'FullJump', 'Total']"
+              v-model:current-page="tablePage.currentPage"
+              v-model:page-size="tablePage.pageSize"
+              :total="tablePage.total"
+              @page-change="handlePageChange">
+            </vxe-pager>
+          </template>
+        </vxe-grid>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<style scoped>
+.line-text {
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+</style>

+ 307 - 0
src/views/searchTerm/asinView/useColumns.ts

@@ -0,0 +1,307 @@
+export const asinColumns = [
+  {
+    field: 'Search_Query',
+    title: '搜索词',
+    minWidth: 180,
+    sortable: true,
+    slots: { default: 'Search_Query_default' }
+  },
+  {
+    field: 'Search_Query_Score',
+    title: '综合得分',
+    minWidth: 120,
+    sortable: true,
+    slots: { default: 'Search_Query_Score_default' }
+  },
+  {
+    field: 'Search_Query_Volume',
+    title: '查询量',
+    minWidth: 120,
+    sortable: true,
+    slots: { default: 'Search_Query_Volume_default' }
+  },
+  {
+    field: 'Impressions_Total_Count',
+    title: '关键词曝光量',
+    minWidth: 150,
+    sortable: true,
+    slots: { default: 'Impressions_Total_Count_default' }
+  },
+  {
+    field: 'Impressions_ASIN_Count',
+    title: '该asin的关键词曝光量',
+    minWidth: 200,
+    sortable: true,
+    slots: { default: 'Impressions_ASIN_Count_default' }
+  },
+  {
+    field: 'Impressions_ASIN_Share',
+    title: '该asin曝光占比',
+    minWidth: 150,
+    sortable: true,
+    slots: { default: 'Impressions_ASIN_Share_default' }
+  },
+  {
+    field: 'Clicks_Total_Count',
+    title: '点击总数',
+    minWidth: 120,
+    sortable: true,
+    slots: { default: 'Clicks_Total_Count_default' }
+  },
+  {
+    field: 'Clicks_Click_Rate',
+    title: '点击率',
+    minWidth: 120,
+    sortable: true,
+    slots: { default: 'Clicks_Click_Rate_default' }
+  },
+  {
+    field: 'Clicks_ASIN_Count',
+    title: '该asin此关键词的点击量',
+    minWidth: 200,
+    sortable: true,
+    slots: { default: 'Clicks_ASIN_Count_default' }
+  },
+  {
+    field: 'Clicks_ASIN_Share',
+    title: '该asin点击占比',
+    minWidth: 150,
+    sortable: true,
+    slots: { default: 'Clicks_ASIN_Share_default' }
+  },
+  {
+    field: 'Clicks_Price_Median',
+    title: '点击价格中位数',
+    minWidth: 200,
+    sortable: true,
+    slots: { default: 'Clicks_Price_Median_default' }
+  },
+  {
+    field: 'Clicks_ASIN_Price_Median',
+    title: '该asin点击价格中位数',
+    minWidth: 200,
+    sortable: true,
+    slots: { default: 'Clicks_ASIN_Price_Median_default' }
+  },
+  {
+    field: 'Clicks_Same_Day_Shipping_Speed',
+    title: '点击同日发货速度',
+    minWidth: 180,
+    sortable: true,
+    slots: { default: 'Clicks_Same_Day_Shipping_Speed_default' }
+  },
+  {
+    field: 'Clicks_1D_Shipping_Speed',
+    title: '点击隔日发货速度',
+    minWidth: 180,
+    sortable: true,
+    slots: { default: 'Clicks_1D_Shipping_Speed_default' }
+  },
+  {
+    field: 'Clicks_2D_Shipping_Speed',
+    title: '点击两日内发货速度',
+    minWidth: 180,
+    sortable: true,
+    slots: { default: 'Clicks_2D_Shipping_Speed_default' }
+  },
+  {
+    field: 'Cart_Adds_Total_Count',
+    title: '加入购物车总数',
+    minWidth: 180,
+    sortable: true,
+    slots: { default: 'Cart_Adds_Total_Count_default' }
+  },
+  {
+    field: 'Cart_Adds_Cart_Add_Rate',
+    title: '加购率',
+    minWidth: 120,
+    sortable: true,
+    slots: { default: 'Cart_Adds_Cart_Add_Rate_default' }
+  },
+  {
+    field: 'Cart_Adds_ASIN_Count',
+    title: '此asin该关键词的加购数量',
+    minWidth: 230,
+    sortable: true,
+    slots: { default: 'Cart_Adds_ASIN_Count_default' }
+  },
+  {
+    field: 'Cart_Adds_ASIN_Share',
+    title: '此asin的关键词加购数站总加购的百分比',
+    minWidth: 300,
+    sortable: true,
+    slots: { default: 'Cart_Adds_ASIN_Share_default' }
+  },
+  {
+    field: 'Cart_Adds_Price_Median',
+    title: '加入购物车价格中位数',
+    minWidth: 200,
+    sortable: true,
+    slots: { default: 'Cart_Adds_Price_Median_default' }
+  },
+  {
+    field: 'Cart_Adds_ASIN_Price_Median',
+    title: '此asin下关键词的产品平均价格',
+    minWidth: 240,
+    sortable: true,
+    slots: { default: 'Cart_Adds_ASIN_Price_Median_default' }
+  },
+  {
+    field: 'Cart_Adds_Same_Day_Shipping_Speed',
+    title: '同日发货速度',
+    minWidth: 150,
+    sortable: true,
+    slots: { default: 'Cart_Adds_Same_Day_Shipping_Speed_default' }
+  },
+  {
+    field: 'Cart_Adds_1D_Shipping_Speed',
+    title: '隔日发货速度',
+    minWidth: 150,
+    sortable: true,
+    slots: { default: 'Cart_Adds_1D_Shipping_Speed_default' }
+  },
+  {
+    field: 'Cart_Adds_2D_Shipping_Speed',
+    title: '两天内发货速度',
+    minWidth: 150,
+    sortable: true,
+    slots: { default: 'Cart_Adds_2D_Shipping_Speed_default' }
+  },
+  {
+    field: 'Purchases_Total_Count',
+    title: '购买总数',
+    minWidth: 100,
+    sortable: true,
+    slots: { default: 'Purchases_Total_Count_default' }
+  },
+  {
+    field: 'Purchases_Purchase_Rate',
+    title: '购买率',
+    minWidth: 100,
+    sortable: true,
+    slots: { default: 'Purchases_Purchase_Rate_default' }
+  },
+  {
+    field: 'Purchases_ASIN_Count',
+    title: '此asin下关键词的购买数',
+    minWidth: 200,
+    sortable: true,
+    slots: { default: 'Purchases_ASIN_Count_default' }
+  },
+  {
+    field: 'Purchases_ASIN_Share',
+    title: '此asin该关键词的购买占比',
+    minWidth: 230,
+    sortable: true,
+    slots: { default: 'Purchases_ASIN_Share_default' }
+  },
+  {
+    field: 'Purchases_Price_Median',
+    title: '购买价格中位数',
+    minWidth: 150,
+    sortable: true,
+    slots: { default: 'Purchases_Price_Median_default' }
+  },
+  {
+    field: 'Purchases_ASIN_Price_Median',
+    title: '此asin购买价格中位数',
+    minWidth: 200,
+    sortable: true,
+    slots: { default: 'Purchases_ASIN_Price_Median_default' }
+  },
+  {
+    field: 'Purchases_Same_Day_Shipping_Speed',
+    title: '同日配送速度购买数',
+    minWidth: 180,
+    sortable: true,
+    slots: { default: 'Purchases_Same_Day_Shipping_Speed_default' }
+  },
+  {
+    field: 'Purchases_1D_Shipping_Speed',
+    title: '隔日发货速度',
+    minWidth: 150,
+    sortable: true,
+    slots: { default: 'Purchases_1D_Shipping_Speed_default' }
+  },
+  {
+    field: 'Purchases_2D_Shipping_Speed',
+    title: '2天内发货速度',
+    minWidth: 150,
+    sortable: true,
+    slots: { default: 'Purchases_2D_Shipping_Speed_default' }
+  },
+  {
+    field: 'Reporting_Date',
+    title: '报告日期',
+    minWidth: 180,
+    sortable: true,
+    slots: { default: 'Reporting_Date_default' }
+  },
+  {
+    field: 'ASIN',
+    title: 'ASIN',
+    minWidth: 120,
+    slots: { default: 'ASIN_default' }
+  },
+  {
+    field: 'departmentName',
+    title: '站点名',
+    minWidth: 150,
+    slots: { default: 'departmentName_default' }
+  },
+  {
+    field: 'searchTerm',
+    title: '搜索词',
+    minWidth: 120,
+    slots: { default: 'searchTerm_default' }
+  },
+  {
+    field: 'searchFrequencyRank',
+    title: '搜索频率排名',
+    minWidth: 150,
+    sortable: true,
+    slots: { default: 'searchFrequencyRank_default' }
+  },
+  {
+    field: 'clickedAsin',
+    title: '点击ASIN',
+    minWidth: 120,
+    sortable: true,
+    slots: { default: 'clickedAsin_default' }
+  },
+  {
+    field: 'clickedItemName',
+    title: '点击商品名称',
+    minWidth: 150,
+    sortable: true,
+    slots: { default: 'clickedItemName_default' }
+  },
+  {
+    field: 'clickShareRank',
+    title: '点击分享排名',
+    minWidth: 150,
+    sortable: true,
+    slots: { default: 'clickShareRank_default' }
+  },
+  {
+    field: 'clickShare',
+    title: '点击分享率',
+    minWidth: 150,
+    sortable: true,
+    slots: { default: 'clickShare_default' }
+  },
+  {
+    field: 'conversionShare',
+    title: '转化分享率',
+    minWidth: 150,
+    sortable: true,
+    slots: { default: 'conversionShare_default' }
+  },
+
+  {
+    field: 'marketplaceIds',
+    title: '市场ID',
+    minWidth: 180,
+    slots: { default: 'marketplaceIds_default' }
+  }
+];

+ 24 - 0
src/views/searchTerm/brandView/api.ts

@@ -0,0 +1,24 @@
+import { request } from '/@/utils/service';
+
+const apiPrefix = '/api/searchterm/';
+
+
+export function getTableData(query: any) {
+  return request({
+    url: apiPrefix + 'brandview/',
+    method: 'GET',
+    params: query,
+  });
+}
+
+export function postDownload(body: any) {
+  return request({
+    url: apiPrefix + 'topsearchtermTable/',
+    method: 'POST',
+    params: body,
+    responseType: 'blob',
+    headers: {
+      'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' // 指定接受 Excel 文件
+    }
+  });
+}

+ 289 - 0
src/views/searchTerm/brandView/index.vue

@@ -0,0 +1,289 @@
+<script setup lang="ts">
+/**
+ * @Name: index.vue
+ * @Description: brandView
+ * @Author: Cheney
+ */
+
+import { Download, Refresh, Search } from '@element-plus/icons-vue';
+import { nextTick, onBeforeMount, reactive, ref, watch } from 'vue';
+import dayjs from 'dayjs';
+import { getTableData, postDownload } from './api';
+import { ElMessage } from 'element-plus';
+import { brandColumns } from './useColumns';
+import { universalColumns } from '/@/views/productCenter/productAnalysis/utils/columns';
+
+const reportTypeSelect = ref('weekly');
+const searchTermInp = ref('zosi');
+// const asinInp = ref('B0');
+const tableLoading = ref(false);
+const downloadLoading = ref(false);
+const date = ref(calculateLastWeek());
+
+const gridOptions: any = reactive({
+  height: 'auto',
+  border: false,
+  round: true,
+  columnConfig: {
+    resizable: true,
+  },
+  toolbarConfig: {
+    custom: true,
+    slots: {
+      buttons: 'toolbar_buttons',
+    },
+  },
+  columns: brandColumns,
+  data: [],
+});
+
+const tablePage = reactive({
+  total: 0,
+  currentPage: 1,
+  pageSize: 20,
+});
+
+onBeforeMount(() => {
+  fetchTableData();
+});
+
+watch(date, () => {
+  fetchTableData();
+});
+
+async function handlePageChange({ currentPage, pageSize }) {
+  tablePage.currentPage = currentPage;
+  tablePage.pageSize = pageSize;
+  await fetchTableData();
+}
+
+async function handleSelectChange() {
+  await fetchTableData();
+}
+
+async function refreshTable() {
+  tablePage.currentPage = 1;
+  tablePage.pageSize = 20;
+  // asinInp.value = '';
+  searchTermInp.value = '';
+  reportTypeSelect.value = 'weekly';
+  // marketplaceSelect.value = marketplaceIdEnum[0].value;
+  await fetchTableData();
+}
+
+async function fetchTableData() {
+  tableLoading.value = true;
+  const query = {
+    page: tablePage.currentPage,
+    limit: tablePage.pageSize,
+    // asin: asinInp.value,
+    search_term: searchTermInp.value,
+    report_type: reportTypeSelect.value,
+    // marketplace_Ids: marketplaceSelect.value,
+    date_start: date.value[0],
+    date_end: date.value[1],
+  };
+  try {
+    const response = await getTableData(query);
+    tablePage.total = response.total;
+    gridOptions.data = response.data;
+  } catch (error) {
+    console.error('==Error==:', error);
+  } finally {
+    tableLoading.value = false;
+  }
+}
+
+/**
+ * 输入框按下回车后触发
+ */
+async function handleQueryChange() {
+  if (!validateSearchTermInput(searchTermInp.value)) {
+    if (searchTermInp.value.length == 0) {
+      return;
+    } else {
+      ElMessage.warning({ message: '搜索词只能输入数字和英文字母', plain: true });
+      return;
+    }
+  }
+  // if (asinInp.value.length > 0 && !validateAsinInput(asinInp.value)) {
+  //   ElMessage.warning({ message: '不符合匹配规范', plain: true });
+  //   return;
+  // }
+  await fetchTableData();
+}
+
+/**
+ * 校验SearchTerm输入是否合法
+ * @param input 输入的字符串
+ */
+function validateSearchTermInput(input: string) {
+  const regex = /^[a-zA-Z0-9\s]*$/;
+  return regex.test(input);
+}
+
+/**
+ * 校验Asin输入是否合法
+ * @param input 输入的字符串
+ */
+function validateAsinInput(input: string) {
+  const regex = /^[Bb]0[A-Za-z0-9\s]*$/i;
+  return regex.test(input);
+}
+
+/**
+ * 计算上周的周日到周六的日期范围
+ */
+function calculateLastWeek() {
+  const today = dayjs();
+  const lastDay = today.subtract(1, 'day'); // 昨天
+  const firstDay = lastDay.subtract(6, 'day'); // 一周前
+  return [firstDay.format('YYYY-MM-DD'), lastDay.format('YYYY-MM-DD')];
+}
+
+// async function handleDownload() {
+//   downloadLoading.value = true;
+//   try {
+//     const body = {
+//       asin: asinInp.value,
+//       date_start: date.value[0],
+//       date_end: date.value[1],
+//       search_term: searchTermInp.value,
+//       marketplace_Ids: marketplaceSelect.value,
+//       report_type: reportTypeSelect.value,
+//     };
+//
+//     const response = await postDownload(body);
+//
+//     const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
+//
+//     // 创建一个临时 URL
+//     const url = window.URL.createObjectURL(blob);
+//
+//     // 创建一个临时的 <a> 元素并触发下载
+//     const link = document.createElement('a');
+//     link.href = url;
+//
+//     // 设置文件名
+//     const currentTime = dayjs().format('YYYY-MM-DD_HH_mm_ss');
+//     const filename = `TopSearchTerm_${currentTime}.xlsx`;
+//
+//     link.setAttribute('download', filename);
+//
+//     // 添加到 body, 触发点击, 然后移除
+//     document.body.appendChild(link);
+//     link.click();
+//     document.body.removeChild(link);
+//
+//     // 释放 URL 对象
+//     window.URL.revokeObjectURL(url);
+//
+//     ElMessage.success('文件下载成功');
+//   } catch (error) {
+//     console.error('==Error==:', error);
+//     ElMessage.error('文件下载失败,请重试');
+//   } finally {
+//     downloadLoading.value = false;
+//   }
+// }
+</script>
+
+<template>
+  <div class="py-2 px-2.5" style="background-color: #f7f7f7">
+    <el-card shadow="hover" class="mb-2.5" style="border: none; margin-bottom: 10px">
+      <div class="flex justify-between">
+        <div class="flex gap-5 flex-wrap">
+          <div>
+            <span class="font-medium mr-0.5">报告类型 </span>
+            <el-select v-model="reportTypeSelect" @change="handleSelectChange" style="width: 90px">
+              <el-option label="周度" value="weekly" />
+              <el-option label="月度" value="monthly" />
+            </el-select>
+          </div>
+          <div>
+            <span class="font-medium mr-0.5">搜索词 </span>
+            <el-input
+              v-model="searchTermInp"
+              @keyup.enter="handleQueryChange"
+              :prefix-icon="Search"
+              placeholder="输入后回车查询"
+              clearable
+              @clear="handleSelectChange"
+              style="width: 240px" />
+          </div>
+          <!--<div>-->
+          <!--  <span class="font-medium mr-0.5">ASIN </span>-->
+          <!--  <el-input-->
+          <!--      v-model="asinInp"-->
+          <!--      @keyup.enter="handleQueryChange"-->
+          <!--      :prefix-icon="Search"-->
+          <!--      placeholder="输入后回车查询"-->
+          <!--      clearable-->
+          <!--      @clear="handleSelectChange"-->
+          <!--      style="width: 180px" />-->
+          <!--</div>-->
+          <div>
+            <span class="font-medium mr-0.5">报告日期 </span>
+            <el-date-picker
+              v-model="date"
+              type="daterange"
+              value-format="YYYY-MM-DD"
+              range-separator="To"
+              :disabled-date="(time: Date) => time > new Date()"
+              :popper-options="{ placement: 'bottom-end' }"
+              :clearable="false" />
+          </div>
+        </div>
+        <div class="flex">
+          <!--<el-button-->
+          <!--  type="success"-->
+          <!--  plain-->
+          <!--  @click="handleDownload"-->
+          <!--  :icon="Download"-->
+          <!--  round-->
+          <!--  :loading="downloadLoading"-->
+          <!--  :disabled="!tableData.length"-->
+          <!--  >下载表格-->
+          <!--</el-button>-->
+          <el-button @click="refreshTable" :icon="Refresh" circle></el-button>
+        </div>
+      </div>
+    </el-card>
+    <el-card shadow="hover" style="border: none">
+      <div style="overflow: hidden; width: 100%; height: 950px" v-loading="tableLoading">
+        <vxe-grid v-bind="gridOptions">
+          <template #toolbar_buttons></template>
+          <template v-for="col in brandColumns" #[`${col.field}_default`]="{ row }">
+            <div v-if="col.field === 'clickedItemName'">
+              <el-tooltip effect="dark" :content="row.clickedItemName" placement="top" :show-after="300" >
+                <div class="line-text font-medium">
+                  {{ row.clickedItemName }}
+                </div>
+              </el-tooltip>
+            </div>
+            <div v-else class="font-medium">
+              {{ row[col.field] ? row[col.field] : '-' }}
+            </div>
+          </template>
+          <template #pager>
+            <vxe-pager
+              :layouts="['Sizes', 'PrevJump', 'PrevPage', 'Number', 'NextPage', 'NextJump', 'FullJump', 'Total']"
+              v-model:current-page="tablePage.currentPage"
+              v-model:page-size="tablePage.pageSize"
+              :total="tablePage.total"
+              @page-change="handlePageChange">
+            </vxe-pager>
+          </template>
+        </vxe-grid>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<style scoped>
+.line-text {
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+</style>

+ 260 - 0
src/views/searchTerm/brandView/useColumns.ts

@@ -0,0 +1,260 @@
+export const brandColumns = [
+  {
+    field: 'Search_Query',
+    title: '搜索词',
+    minWidth: 130,
+    slots: { default: 'Search_Query_default' },
+  },
+  {
+    field: 'Search_Query_Score',
+    title: '综合得分',
+    minWidth: 130,
+    slots: { default: 'Search_Query_Score_default' },
+  },
+  {
+    field: 'Search_Query_Volume',
+    title: '查询量',
+    minWidth: 130,
+    slots: { default: 'Search_Query_Volume_default' },
+  },
+  {
+    field: 'Impressions_Total_Count',
+    title: '关键词曝光量',
+    minWidth: 130,
+    slots: { default: 'Impressions_Total_Count_default' },
+  },
+  {
+    field: 'Impressions_ASIN_Count',
+    title: '该asin的关键词曝光量',
+    minWidth: 180,
+    slots: { default: 'Impressions_ASIN_Count_default' },
+  },
+  {
+    field: 'Impressions_ASIN_Share',
+    title: '该asin曝光占比',
+    minWidth: 130,
+    slots: { default: 'Impressions_ASIN_Share_default' },
+  },
+  {
+    field: 'Clicks_Total_Count',
+    title: '此关键词的点击量',
+    minWidth: 150,
+    slots: { default: 'Clicks_Total_Count_default' },
+  },
+  {
+    field: 'Clicks_Click_Rate',
+    title: '点击率',
+    minWidth: 100,
+    slots: { default: 'Clicks_Click_Rate_default' },
+  },
+  {
+    field: 'Clicks_ASIN_Count',
+    title: '该asin此关键词的点击量',
+    minWidth: 180,
+    slots: { default: 'Clicks_ASIN_Count_default' },
+  },
+  {
+    field: 'Clicks_ASIN_Share',
+    title: '该asin点击占比',
+    minWidth: 130,
+    slots: { default: 'Clicks_ASIN_Share_default' },
+  },
+  {
+    field: 'Clicks_Price_Median',
+    title: '该关键词的平均价格',
+    minWidth: 150,
+    slots: { default: 'Clicks_Price_Median_default' },
+  },
+  {
+    field: 'Clicks_ASIN_Price_Median',
+    title: '该asin此关键词的价格',
+    minWidth: 170,
+    slots: { default: 'Clicks_ASIN_Price_Median_default' },
+  },
+  {
+    field: 'Clicks_Same_Day_Shipping_Speed',
+    title: '点击同一天发货速度',
+    minWidth: 150,
+    slots: { default: 'Clicks_Same_Day_Shipping_Speed_default' },
+  },
+  {
+    field: 'Clicks_1D_Shipping_Speed',
+    title: '点击隔日发货速度',
+    minWidth: 150,
+    slots: { default: 'Clicks_1D_Shipping_Speed_default' },
+  },
+  {
+    field: 'Clicks_2D_Shipping_Speed',
+    title: '点击2天内发货速度',
+    minWidth: 150,
+    slots: { default: 'Clicks_2D_Shipping_Speed_default' },
+  },
+  {
+    field: 'Cart_Adds_Total_Count',
+    title: '该关键词加购数量',
+    minWidth: 150,
+    slots: { default: 'Cart_Adds_Total_Count_default' },
+  },
+  {
+    field: 'Cart_Adds_Cart_Add_Rate',
+    title: '加购率',
+    minWidth: 100,
+    slots: { default: 'Cart_Adds_Cart_Add_Rate_default' },
+  },
+  {
+    field: 'Cart_Adds_ASIN_Count',
+    title: '此asin该关键词的加购数量',
+    minWidth: 200,
+    slots: { default: 'Cart_Adds_ASIN_Count_default' },
+  },
+  {
+    field: 'Cart_Adds_ASIN_Share',
+    title: '此asin的关键词加购数站总加购的百分比',
+    minWidth: 280,
+    slots: { default: 'Cart_Adds_ASIN_Share_default' },
+  },
+  {
+    field: 'Cart_Adds_Price_Median',
+    title: '此关键词的产品价格',
+    minWidth: 160,
+    slots: { default: 'Cart_Adds_Price_Median_default' },
+  },
+  {
+    field: 'Cart_Adds_ASIN_Price_Median',
+    title: '此asin下关键词的产品平均价格',
+    minWidth: 230,
+    slots: { default: 'Cart_Adds_ASIN_Price_Median_default' },
+  },
+  {
+    field: 'Cart_Adds_Same_Day_Shipping_Speed',
+    title: '同一天发货速度',
+    minWidth: 130,
+    slots: { default: 'Cart_Adds_Same_Day_Shipping_Speed_default' },
+  },
+  {
+    field: 'Cart_Adds_1D_Shipping_Speed',
+    title: '隔日发货速度',
+    minWidth: 130,
+    slots: { default: 'Cart_Adds_1D_Shipping_Speed_default' },
+  },
+  {
+    field: 'Cart_Adds_2D_Shipping_Speed',
+    title: '两天内发货速度',
+    minWidth: 130,
+    slots: { default: 'Cart_Adds_2D_Shipping_Speed_default' },
+  },
+  {
+    field: 'Purchases_Total_Count',
+    title: '该关键词下的购买数',
+    minWidth: 150,
+    slots: { default: 'Purchases_Total_Count_default' },
+  },
+  {
+    field: 'Purchases_Purchase_Rate',
+    title: '该关键词购买率',
+    minWidth: 130,
+    slots: { default: 'Purchases_Purchase_Rate_default' },
+  },
+  {
+    field: 'Purchases_ASIN_Count',
+    title: '此asin下关键词的购买数',
+    minWidth: 180,
+    slots: { default: 'Purchases_ASIN_Count_default' },
+  },
+  {
+    field: 'Purchases_ASIN_Share',
+    title: '此asin该关键词的购买占比',
+    minWidth: 200,
+    slots: { default: 'Purchases_ASIN_Share_default' },
+  },
+  {
+    field: 'Purchases_Price_Median',
+    title: '关键词的购买平均价格',
+    minWidth: 180,
+    slots: { default: 'Purchases_Price_Median_default' },
+  },
+  {
+    field: 'Purchases_ASIN_Price_Median',
+    title: '此asin下关键词的购买价格',
+    minWidth: 200,
+    slots: { default: 'Purchases_ASIN_Price_Median_default' },
+  },
+  {
+    field: 'Purchases_Same_Day_Shipping_Speed',
+    title: '同一天发货速度',
+    minWidth: 130,
+    slots: { default: 'Purchases_Same_Day_Shipping_Speed_default' },
+  },
+  {
+    field: 'Purchases_1D_Shipping_Speed',
+    title: '隔日发货速度',
+    minWidth: 130,
+    slots: { default: 'Purchases_1D_Shipping_Speed_default' },
+  },
+  {
+    field: 'Purchases_2D_Shipping_Speed',
+    title: '2天内发货速度',
+    minWidth: 130,
+    slots: { default: 'Purchases_2D_Shipping_Speed_default' },
+  },
+  {
+    field: 'Reporting_Date',
+    title: '报告日期',
+    minWidth: 100,
+    slots: { default: 'Reporting_Date_default' },
+  },
+  {
+    field: 'departmentName',
+    title: '站点名',
+    minWidth: 150,
+    slots: { default: 'departmentName_default' },
+  },
+  {
+    field: 'searchTerm',
+    title: '关键词',
+    minWidth: 100,
+    slots: { default: 'searchTerm_default' },
+  },
+  {
+    field: 'searchFrequencyRank',
+    title: '关键词搜索排名',
+    minWidth: 130,
+    slots: { default: 'searchFrequencyRank_default' },
+  },
+  {
+    field: 'clickedAsin',
+    title: 'ASIN',
+    minWidth: 150,
+    slots: { default: 'clickedAsin_default' },
+  },
+  {
+    field: 'clickedItemName',
+    title: '标题',
+    minWidth: 150,
+    slots: { default: 'clickedItemName_default' },
+  },
+  {
+    field: 'clickShareRank',
+    title: '点击分享排名',
+    minWidth: 130,
+    slots: { default: 'clickShareRank_default' },
+  },
+  {
+    field: 'clickShare',
+    title: '点击分享率',
+    minWidth: 130,
+    slots: { default: 'clickShare_default' },
+  },
+  {
+    field: 'conversionShare',
+    title: '转化分享率',
+    minWidth: 130,
+    slots: { default: 'conversionShare_default' },
+  },
+  {
+    field: 'marketplaceIds',
+    title: '站点id',
+    minWidth: 180,
+    slots: { default: 'marketplaceIds_default' },
+  },
+];

+ 124 - 125
src/views/searchTerm/rootWordManage/components/root-word-manage-table.vue

@@ -5,7 +5,7 @@
  * @Author: Cheney
  */
 
-import { nextTick, onBeforeMount, onMounted, reactive, ref } from 'vue';
+import { nextTick, onMounted, reactive, ref } from 'vue';
 import { Plus, Search, Upload } from '@element-plus/icons-vue';
 import * as api from '../api';
 import type { UploadInstance, UploadRawFile } from 'element-plus';
@@ -311,135 +311,134 @@ function handleResponse(response: any) {
 </script>
 
 <template>
-  <div>
-    <el-card v-loading="tableLoading" body-style="background-color: #f7f7f7;">
-      <!-- 筛选 -->
-      <el-card body-class="flex justify-between gap-3.5" shadow="hover" style="border: none; margin-bottom: 10px">
-        <div class="flex gap-7">
-          <div>
-            <span class="font-bold mr-2" style="color: #303133">词根:</span>
-            <el-input
-              v-model="searchTermFilter"
-              placeholder="请输入词根"
-              clearable
-              @change="fetchSearchTermList"
-              :prefix-icon="Search"
-              style="width: 240px"></el-input>
-          </div>
-          <div>
-            <span class="font-bold mr-2" style="color: #303133">词根类型:</span>
-            <el-select v-model="searchTermTypeFilter" style="width: 200px" @change="fetchSearchTermList">
-              <el-option label="全部" value="all" />
-              <el-option label="positive" value="positive" />
-              <el-option label="negative" value="negative" />
-              <el-option label="无词根类型" value="typeless" />
-            </el-select>
-          </div>
+  <div class="p-2.5" v-loading="tableLoading" style="background-color: #f7f7f7">
+    <!-- 筛选 -->
+    <el-card body-class="flex justify-between gap-3.5" shadow="hover" style="border: none; margin-bottom: 10px">
+      <div class="flex gap-7">
+        <div>
+          <span class="font-bold mr-2" style="color: #303133">词根:</span>
+          <el-input
+            v-model="searchTermFilter"
+            placeholder="请输入词根"
+            clearable
+            @change="fetchSearchTermList"
+            :prefix-icon="Search"
+            style="width: 240px"></el-input>
         </div>
-        <div class="flex gap-3.5">
-          <el-button plain type="primary" @click="handleDialogVisible">
-            <el-icon>
-              <Plus />
-            </el-icon>
-            添加词根
-          </el-button>
-          <!-- 想要不页面不跳动可以加72的高度 -->
-          <div>
-            <el-upload
-              ref="upload"
-              action="#"
-              :limit="1"
-              :auto-upload="true"
-              :on-exceed="handleExceed"
-              :http-request="handleCustomUpload">
-              <template #trigger>
-                <el-button plain round type="warning" :icon="Upload">批量词根上传</el-button>
-              </template>
-            </el-upload>
-          </div>
+        <div>
+          <span class="font-bold mr-2" style="color: #303133">词根类型:</span>
+          <el-select v-model="searchTermTypeFilter" style="width: 200px" @change="fetchSearchTermList">
+            <el-option label="全部" value="all" />
+            <el-option label="positive" value="positive" />
+            <el-option label="negative" value="negative" />
+            <el-option label="无词根类型" value="typeless" />
+          </el-select>
         </div>
-      </el-card>
-      <!-- 表格 -->
-      <el-card shadow="hover" style="border: none">
-        <div style="height: 800px;">
-          <el-table :data="tableData" height="800" stripe style="width: 100%">
-            <el-table-column fixed prop="add_date" label="添加日期" width="180" sortable>
-              <template #header>
-                <el-icon style="top: 2px; margin-right: 3px">
-                  <Calendar />
-                </el-icon>
-                <span>添加日期</span>
-              </template>
-              <template #default="{ row }">
-                <span class="font-medium">{{ row.add_date }}</span>
-              </template>
-            </el-table-column>
-            <el-table-column prop="searchTerm" label="词根" sortable>
-              <template #header>
-                <el-icon style="top: 2px; right: 2px">
-                  <Key />
-                </el-icon>
-                <span>词根</span>
-              </template>
-              <template #default="{ row }">
-                <el-input ref="searchTermInpRef" v-if="row.isEditing" v-model="row.searchTerm" @change="updateSearchTerm(row)" />
-                <span class="font-semibold" v-else>{{ row.searchTerm }}</span>
-              </template>
-            </el-table-column>
-            <el-table-column prop="searchTerm_type" label="词根类型" sortable>
-              <template #header>
-                <el-icon style="top: 2px; right: 2px">
-                  <Coin />
-                </el-icon>
-                <span>词根类型</span>
-              </template>
-              <template #default="{ row }">
-                <el-popconfirm
-                  title="确定修改吗?"
-                  @confirm="updateSearchTermType(row)"
-                  @cancel="cancelUpdate(row)"
-                  :visible="row.popConfirmVisible">
-                  <template #reference>
-                    <el-select v-model="row.searchTerm_type" @change="showPopConfirm(row)" style="width: 150px">
-                      <el-option label="positive" value="positive" />
-                      <el-option label="negative" value="negative" />
-                    </el-select>
-                  </template>
-                </el-popconfirm>
-              </template>
-            </el-table-column>
-            <el-table-column fixed="right" label="操作" width="120">
-              <template #header>
-                <el-icon style="top: 2px; margin-right: 5px">
-                  <EditPen />
-                </el-icon>
-                <span>操作</span>
-              </template>
-              <template #default="{ row }">
-                <el-button link type="primary" size="small" @click="handleClick(row)" v-if="!row.isEditing"> 编辑</el-button>
-                <el-button link type="primary" size="small" @click="handleClick(row)" v-else> 取消</el-button>
-                <el-popconfirm title="确定删除吗?" @confirm="handleDelete(row)">
-                  <template #reference>
-                    <el-button link type="danger" size="small">删除</el-button>
-                  </template>
-                </el-popconfirm>
-              </template>
-            </el-table-column>
-          </el-table>
-        </div>
-        <div class="mt-3.5 flex justify-end">
-          <el-pagination
-            v-model:current-page="currentPage"
-            v-model:page-size="pageSize"
-            :page-sizes="[20, 40, 50, 100]"
-            layout="sizes, prev, pager, next"
-            :total="total"
-            @size-change="handleSizeChange"
-            @current-change="handleCurrentChange" />
+      </div>
+      <div class="flex gap-3.5">
+        <el-button plain type="primary" @click="handleDialogVisible">
+          <el-icon>
+            <Plus />
+          </el-icon>
+          添加词根
+        </el-button>
+        <!-- 想要不页面不跳动可以加72的高度 -->
+        <div>
+          <el-upload
+            ref="upload"
+            action="#"
+            :limit="1"
+            :auto-upload="true"
+            :on-exceed="handleExceed"
+            :http-request="handleCustomUpload">
+            <template #trigger>
+              <el-button plain round type="warning" :icon="Upload">批量词根上传</el-button>
+            </template>
+          </el-upload>
         </div>
-      </el-card>
+      </div>
+    </el-card>
+    <!-- 表格 -->
+    <el-card shadow="hover" style="border: none">
+      <div style="height: 800px">
+        <el-table :data="tableData" height="800" stripe style="width: 100%">
+          <el-table-column fixed prop="add_date" label="添加日期" width="180" sortable>
+            <template #header>
+              <el-icon style="top: 2px; margin-right: 3px">
+                <Calendar />
+              </el-icon>
+              <span>添加日期</span>
+            </template>
+            <template #default="{ row }">
+              <span class="font-medium">{{ row.add_date }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="searchTerm" label="词根" sortable>
+            <template #header>
+              <el-icon style="top: 2px; right: 2px">
+                <Key />
+              </el-icon>
+              <span>词根</span>
+            </template>
+            <template #default="{ row }">
+              <el-input ref="searchTermInpRef" v-if="row.isEditing" v-model="row.searchTerm" @change="updateSearchTerm(row)" />
+              <span class="font-semibold" v-else>{{ row.searchTerm }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="searchTerm_type" label="词根类型" sortable>
+            <template #header>
+              <el-icon style="top: 2px; right: 2px">
+                <Coin />
+              </el-icon>
+              <span>词根类型</span>
+            </template>
+            <template #default="{ row }">
+              <el-popconfirm
+                title="确定修改吗?"
+                @confirm="updateSearchTermType(row)"
+                @cancel="cancelUpdate(row)"
+                :visible="row.popConfirmVisible">
+                <template #reference>
+                  <el-select v-model="row.searchTerm_type" @change="showPopConfirm(row)" style="width: 150px">
+                    <el-option label="positive" value="positive" />
+                    <el-option label="negative" value="negative" />
+                  </el-select>
+                </template>
+              </el-popconfirm>
+            </template>
+          </el-table-column>
+          <el-table-column fixed="right" label="操作" width="120">
+            <template #header>
+              <el-icon style="top: 2px; margin-right: 5px">
+                <EditPen />
+              </el-icon>
+              <span>操作</span>
+            </template>
+            <template #default="{ row }">
+              <el-button link type="primary" size="small" @click="handleClick(row)" v-if="!row.isEditing"> 编辑</el-button>
+              <el-button link type="primary" size="small" @click="handleClick(row)" v-else> 取消</el-button>
+              <el-popconfirm title="确定删除吗?" @confirm="handleDelete(row)">
+                <template #reference>
+                  <el-button link type="danger" size="small">删除</el-button>
+                </template>
+              </el-popconfirm>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      <div class="mt-3.5 flex justify-end">
+        <el-pagination
+          v-model:current-page="currentPage"
+          v-model:page-size="pageSize"
+          :page-sizes="[20, 40, 50, 100]"
+          layout="sizes, prev, pager, next"
+          :total="total"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange" />
+      </div>
     </el-card>
   </div>
+
   <!-- 添加词根弹窗 -->
   <el-dialog v-model="dialogVisible" title="添加关键词" width="500" :before-close="handleClose">
     <el-form ref="ruleFormRef" style="max-width: 600px" :model="ruleForm" status-icon :rules="rules" label-width="auto">

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

@@ -124,7 +124,7 @@ function handleJump() {
       </div>
     </el-divider>
   </div>
-  <el-card class="mx-3 mb-2.5 h-full" v-loading="tableLoading" style="border: none;">
+  <el-card class="mx-3 mb-2.5" v-loading="tableLoading" style="border: none;">
     <!-- table筛选栏 -->
     <div class="flex justify-between">
       <div class="flex gap-5 flex-wrap">

+ 8 - 7
src/views/searchTerm/topSearchTermTable/index.vue

@@ -16,7 +16,6 @@ import dayjs from 'dayjs';
 import enLocale from 'element-plus/es/locale/lang/en';
 
 const router = useRouter();
-
 const { tableData, total, currentPage, pageSize, handlePageChange } = usePagination(fetchTableData);
 const marketplaceSelect = ref(marketplaceIdEnum[0].value); // 当前只有美国区 默认第一个为美国
 const marketplaceOptions = marketplaceIdEnum;
@@ -107,11 +106,17 @@ async function fetchTableData() {
   }
 }
 
+/**
+ * 下拉框值改变和input清空事件触发
+ */
 async function handleSelectChange() {
   calculateDate();
   await fetchTableData();
 }
 
+/**
+ * 输入框按下回车后触发
+ */
 async function handleQueryChange() {
   if (!validateSearchTermInput(searchTermInp.value)) {
     if (searchTermInp.value.length == 0) {
@@ -234,7 +239,7 @@ function arraySpanMethod({ row, column, rowIndex, columnIndex }) {
       </div>
     </el-divider>
   </div>
-  <el-card class="mx-3 mb-2.5 h-full" v-loading="tableLoading" style="border: none;">
+  <el-card class="mx-3 mb-2.5" v-loading="tableLoading" style="border: none;">
     <!-- table筛选栏 -->
     <div class="flex justify-between">
       <div class="flex gap-5 flex-wrap">
@@ -280,7 +285,6 @@ function arraySpanMethod({ row, column, rowIndex, columnIndex }) {
         </div>
         <div>
           <span class="font-medium mr-0.5">报告日期 </span>
-
           <el-config-provider :locale="enLocale">
             <el-date-picker
               v-if="reportTypeSelect === 'weekly'"
@@ -350,7 +354,6 @@ function arraySpanMethod({ row, column, rowIndex, columnIndex }) {
               <span class="font-medium">{{ row.searchFrequencyRank }}</span>
             </template>
           </el-table-column>
-
           <el-table-column prop="clickShareSummary" label="点击分享率(SUM)" align="center" width="150">
             <template #header>
               <el-icon style="top: 2px; margin-right: 4px">
@@ -373,7 +376,6 @@ function arraySpanMethod({ row, column, rowIndex, columnIndex }) {
               <span class="font-medium">{{ row.conversionShareSummary }}</span>
             </template>
           </el-table-column>
-
           <el-table-column prop="clickedAsin" label="Asin" align="center">
             <template #header>
               <el-icon style="top: 2px; margin-right: 5px">
@@ -404,7 +406,6 @@ function arraySpanMethod({ row, column, rowIndex, columnIndex }) {
               <div class="text-sm text-left">
                 <el-tooltip class="box-item" effect="dark" :content="row.clickedItemName" placement="top" :show-after="500">
                   <div class="tooltip-text">
-                    <!--<span class="font-medium mr-1">Title:</span>-->
                     {{ row.clickedItemName }}
                   </div>
                 </el-tooltip>
@@ -419,7 +420,6 @@ function arraySpanMethod({ row, column, rowIndex, columnIndex }) {
               <span>点击分享率排名</span>
             </template>
             <template #default="{ row }">
-              <!--<span class="font-semibold">{{ row.clickShareRank }}</span>-->
               <el-tag :style="getTagStyle(row.clickShareRank)">
                 {{ row.clickShareRank }}
               </el-tag>
@@ -463,6 +463,7 @@ function arraySpanMethod({ row, column, rowIndex, columnIndex }) {
 </template>
 
 <style scoped>
+/* 修改 el-divider 的背景颜色 */
 :deep(.el-divider__text.is-center.el-divider__text) {
   background-color: #f8f8f8;
 }