Browse Source

feat: 关键词接口调试

周玉环 1 day ago
parent
commit
fc0a5969c5

+ 2 - 0
xinkeaboard-promotion-portal/components.d.ts

@@ -9,6 +9,7 @@ export {}
 declare module 'vue' {
   export interface GlobalComponents {
     ColorLevel: typeof import('./src/components/ColorLevel.vue')['default']
+    CommonEmpty: typeof import('./src/components/CommonEmpty.vue')['default']
     CompetitorWebsite: typeof import('./src/components/CompetitorWebsite.vue')['default']
     ConfirmInfoDialog: typeof import('./src/components/ConfirmInfoDialog.vue')['default']
     CountrySelct: typeof import('./src/components/CountrySelct.vue')['default']
@@ -21,6 +22,7 @@ declare module 'vue' {
     ElInput: typeof import('element-plus/es')['ElInput']
     ElTable: typeof import('element-plus/es')['ElTable']
     ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
+    Empty: typeof import('./src/components/Empty.vue')['default']
     Home: typeof import('./src/components/Home.vue')['default']
     KeywordSearch: typeof import('./src/components/KeywordSearch.vue')['default']
     Level: typeof import('./src/components/Level.vue')['default']

+ 122 - 0
xinkeaboard-promotion-portal/src/components/CommonEmpty.vue

@@ -0,0 +1,122 @@
+<template>
+  <div class="loading-wrapper">
+    <div class="loading-content">
+      <div class="loading-text">{{ text }}</div>
+      <div class="progress-bar">
+        <div
+          class="progress-fill"
+          :style="{
+            width: progress + '%',
+            backgroundColor: fail ? '#FF3C1C' : '#036EB8'
+          }"
+        ></div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, onMounted, onUnmounted, watch } from 'vue';
+
+const props = defineProps({
+  text: {
+    type: String,
+    default: 'AI数据生成中...'
+  },
+  autoFinish: {
+    type: Boolean,
+    default: false // true 时直接满进度
+  },
+  fail: {
+    type: Boolean,
+    default: false // true 时失败,进度条停止 & 变红
+  }
+});
+
+const progress = ref(0);
+let timer: any = null;
+
+// 进度逻辑
+const startProgress = () => {
+  if (props.autoFinish) {
+    progress.value = 100;
+    return;
+  }
+
+  timer = setInterval(() => {
+    if (!props.fail && progress.value < 100) {
+      progress.value += 1;
+    } else {
+      clearInterval(timer);
+    }
+  }, 1000); // 每秒 +1%
+};
+
+onMounted(() => {
+  startProgress();
+});
+
+onUnmounted(() => {
+  clearInterval(timer);
+});
+
+// 监听 autoFinish 变化,支持动态切换
+watch(
+  () => props.autoFinish,
+  (val) => {
+    if (val) {
+      progress.value = 100;
+      clearInterval(timer);
+    }
+  }
+);
+
+// 监听 fail
+watch(
+  () => props.fail,
+  (val) => {
+    if (val) {
+      clearInterval(timer); // 停止进度
+    } else {
+      // 从失败恢复时重新启动
+      if (progress.value < 100) startProgress();
+    }
+  }
+);
+</script>
+
+<style scoped>
+.loading-wrapper {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.loading-content {
+  text-align: center;
+}
+
+.loading-text {
+  font-weight: bold;
+  font-size: 20px;
+  color: #282e30;
+  margin-bottom: 20px;
+}
+
+.progress-bar {
+  width: 350px;
+  height: 10px;
+  background-color: #e7e9ec;
+  border-radius: 5px;
+  overflow: hidden;
+  margin: 0 auto;
+}
+
+.progress-fill {
+  height: 100%;
+  background-color: var(--promotion--color-primary);
+  transition: width 0.3s ease;
+}
+</style>

+ 2 - 2
xinkeaboard-promotion-portal/src/components/keyword/search.vue

@@ -29,8 +29,8 @@ const loading = computed(() => keywordInfo.value.loading);
 
 const data = computed(() => keywordInfo.value.data);
 
-const year = computed(() => data.value.monthlySearchesBO.year);
-const month = computed(() => data.value.monthlySearchesBO.month);
+const year = computed(() => data.value.monthlySearchesBO?.year);
+const month = computed(() => data.value.monthlySearchesBO?.month);
 const competition = computed(() => data.value.competition ?? 0);
 
 const searchVolume = computed(() => {

+ 18 - 31
xinkeaboard-promotion-portal/src/components/keyword/table.vue

@@ -1,19 +1,20 @@
 <template>
   <div class="keyword-table">
-    <el-table :data="tableData">
-      <el-table-column prop="date" align="right">
+    <Empty :autoFinish="autoFinish" v-if="loading"></Empty>
+    <el-table :data="tableData" v-else>
+      <el-table-column prop="keyword" align="right">
         <template #header>
           <img :src="Help" />
           关键词
         </template>
       </el-table-column>
-      <el-table-column prop="name" align="center">
+      <el-table-column prop="searchVolume" align="center">
         <template #header>
           <img :src="Help" />
           搜索量
         </template>
       </el-table-column>
-      <el-table-column prop="address" align="center">
+      <el-table-column prop="competition" align="center">
         <template #header>
           <img :src="Help" />
           关键词难度
@@ -25,37 +26,23 @@
 
 <script lang="ts" setup>
 import Help from '../../assets/images/help.png';
-const tableData = [
-  {
-    date: '2016-05-03',
-    name: 'Tom',
-    address: '189'
-  },
-  {
-    date: '2016-05-02',
-    name: 'Tom',
-    address: '189'
-  },
-  {
-    date: '2016-05-04',
-    name: 'Tom',
-    address: '189'
-  },
-  {
-    date: '2016-05-01',
-    name: 'Tom',
-    address: '189'
-  },
-  {
-    date: '2016-05-01',
-    name: 'Tom',
-    address: '189'
-  }
-];
+import { computed } from 'vue';
+import { useMainStore } from '@/store';
+import Empty from '@/components/CommonEmpty.vue';
+
+const mainStore = useMainStore();
+
+const tableData = computed(() => mainStore.suggestionsInfo.data);
+const loading = computed(() => mainStore.suggestionsInfo.loading);
+const autoFinish = computed(() => mainStore.suggestionsInfo.autoFinish);
 </script>
 
 <style lang="scss" scoped>
 .keyword-table {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 100%;
   :deep(.el-table) {
     width: 100%;
     height: 317px;

+ 26 - 5
xinkeaboard-promotion-portal/src/store/index.ts

@@ -1,7 +1,7 @@
 import { defineStore } from 'pinia';
 import { safeJsonParse } from '../utils/common';
-import { analysisKeyword } from '../utils/api';
-import type { FormDataInfo, KeywordInfo } from '@/types';
+import { analysisKeyword, analysisSuggestions } from '../utils/api';
+import type { FormDataInfo, KeywordInfo, RelatedInfoBOItem } from '@/types';
 
 export const useMainStore = defineStore('main', {
   state: () => ({
@@ -10,6 +10,11 @@ export const useMainStore = defineStore('main', {
     keywordInfo: {
       loading: true,
       data: {} as KeywordInfo
+    },
+    suggestionsInfo: {
+      loading: true,
+      autoFinish: false,
+      data: [] as RelatedInfoBOItem[]
     }
   }),
   actions: {
@@ -25,11 +30,21 @@ export const useMainStore = defineStore('main', {
     // 获取竞品
     getRival() {},
     // 获取推荐
-    getSuggestions() {},
+    async getSuggestions() {
+      const { productName, locationName } = this.getFormData;
+      return analysisSuggestions({ productName, locationName })
+        .then((res) => {
+          this.suggestionsInfo.data = res.data.relatedInfoBOList;
+          this.suggestionsInfo.autoFinish = true;
+          setTimeout(() => {
+            this.suggestionsInfo.loading = false;
+          }, 300);
+        })
+        .finally(() => {});
+    },
     // 获取关键词
     async getKeywordData() {
       const { productName, locationName } = this.getFormData;
-      console.log(productName, 'productName')
       return analysisKeyword({ productName, locationName })
         .then((res) => {
           this.keywordInfo.data = res.data;
@@ -40,7 +55,7 @@ export const useMainStore = defineStore('main', {
     },
     initData() {
       this.getKeywordData();
-      // this.getSuggestions();
+      this.getSuggestions();
       // this.getRival();
       // this.getQualitative();
     }
@@ -58,6 +73,12 @@ export const useMainStore = defineStore('main', {
     },
     getKeywordInfo(): { data: KeywordInfo } & { loading: boolean } {
       return this.keywordInfo;
+    },
+    getSuggestionsInfo(): { data: RelatedInfoBOItem[] } & {
+      autoFinish: boolean;
+      loading: boolean;
+    } {
+      return this.suggestionsInfo;
     }
   }
 });

+ 6 - 0
xinkeaboard-promotion-portal/src/types/index.ts

@@ -28,3 +28,9 @@ export interface KeywordInfo {
     search_volume: number;
   };
 }
+
+export interface RelatedInfoBOItem {
+  searchVolume: string;
+  competition: number;
+  keyword: string;
+}