Browse Source

fix: 修复专题装修页面跳转问题

周玉环 3 weeks ago
parent
commit
ce6d483b3a

+ 22 - 4
xinkeaboard-promotion-portal/src/components/TopContent.vue

@@ -19,7 +19,10 @@
           ref="ProductDescriptionRef"
           v-show="currentStep === 2"
         ></ProductDescription>
-        <CompetitorWebsite ref="CompetitorWebsiteRef" v-show="currentStep === 3"></CompetitorWebsite>
+        <CompetitorWebsite
+          ref="CompetitorWebsiteRef"
+          v-show="currentStep === 3"
+        ></CompetitorWebsite>
         <el-button v-if="currentStep !== 1" @click="prevStep">上一步</el-button>
         <el-button v-if="currentStep !== 3" @click="nextStep">下一步</el-button>
         <el-button v-if="currentStep === 3" @click="acceptRecod">获取报告</el-button>
@@ -35,6 +38,12 @@ import CountrySelct from '@/components/CountrySelct.vue';
 import ProductDescription from '@/components/ProductDescription.vue';
 import CompetitorWebsite from '@/components/CompetitorWebsite.vue';
 import Logo from '@/assets/images/logo.svg';
+import {
+  analysisKeyword,
+  analysisSuggestions,
+  analysisRival,
+  analysisQualitative
+} from '@/utils/api';
 
 const currentStep = ref<number>(1);
 const CountrySelctRef = ref();
@@ -54,10 +63,19 @@ const acceptRecod = () => {
   const productName = CountrySelctRef.value.getProductName();
   const description = ProductDescriptionRef.value.getDescription();
   const competitorWebsite = CompetitorWebsiteRef.value.getWebsite();
-  console.log(locationName, productName, description, competitorWebsite, '-=-=-=-=')
+  console.log(locationName, productName, description, competitorWebsite, '-=-=-=-=');
+  Promise.all([
+    analysisKeyword({ productName: '自行车', locationName: 'United States' }),
+    analysisSuggestions({ productName: '自行车', locationName: 'United States' }),
+    analysisRival({
+      competitorWebsite: 'https://www.popmart.com,https://us.louisvuitton.com/eng-us/homepage',
+      locationName: 'United States'
+    }),
+    analysisQualitative('自行车')
+  ]).then((res) => {
+    console.log(res, '=========');
+  });
 };
-
-
 </script>
 
 <style lang="scss" scoped>

+ 28 - 5
xinkeaboard-promotion-portal/src/utils/api.ts

@@ -1,10 +1,33 @@
-import { request } from "@/utils/http";
+import http from '@/utils/http';
+
+// export function getUserInfo() {
+//   return http.get('/user/info');
+// }
+
+// export function login(data: { username: string; password: string }) {
+//   return http.post('/auth/login', data);
+// }
 
 interface AnalysisKeyword {
-    productName: string;
-    locationName: string;
+  productName: string;
+  locationName: string;
+}
+
+interface AnalysisRival {
+  competitorWebsite: string;
+  locationName: string;
 }
 
+export const analysisKeyword = (payload: AnalysisKeyword) =>
+  http.post('/analysis/keyword', payload);
+
+//推荐
+export const analysisSuggestions = (payload: AnalysisKeyword) =>
+  http.post('/analysis/suggestions', payload);
+
+// 竞品
+export const analysisRival = (payload: AnalysisRival) => http.post('/analysis/rival', payload);
 
-// 关键词
-export const analysisKeyword = (payload: AnalysisKeyword) => request.post('/analysis/keyword', payload)
+// 定性分析接口
+export const analysisQualitative = (payload: string) =>
+  http.get(`/analysis/qualitative?keyword=${payload}`);

+ 30 - 0
xinkeaboard-promotion-portal/src/utils/api2.ts

@@ -0,0 +1,30 @@
+import { request } from '@/utils/http1';
+
+interface AnalysisKeyword {
+  productName: string;
+  locationName: string;
+}
+
+interface AnalysisRival {
+  competitorWebsite: string;
+  locationName: string;
+}
+
+interface AnalysisQualitative {
+    keyword: string;
+}
+
+// 关键词
+export const analysisKeyword = (payload: AnalysisKeyword) =>
+  request.post('/analysis/keyword', payload);
+
+//推荐
+export const analysisSuggestions = (payload: AnalysisKeyword) =>
+  request.post('/analysis/suggestions', payload);
+
+// 竞品
+export const analysisRival = (payload: AnalysisRival) => request.post('/analysis/rival', payload);
+
+
+// 定性分析接口
+export const analysisQualitative = (payload: AnalysisQualitative) => request.get('/analysis/qualitative', payload);

+ 58 - 124
xinkeaboard-promotion-portal/src/utils/http.ts

@@ -1,132 +1,66 @@
-import axios, {
-  AxiosInstance,
-  AxiosRequestConfig,
-  AxiosResponse,
-  Canceler,
-  AxiosError
-} from 'axios';
+import axios from "axios";
+import { ElMessage } from "element-plus"; // 你也可以换成自己的提示组件
+import router from "@/router"; // 如果需要跳转登录页
+import type { CommonReqResponseData } from '../types/index';
 
-type RequestInterceptor = (
-  config: AxiosRequestConfig
-) => AxiosRequestConfig | Promise<AxiosRequestConfig>;
-type ResponseInterceptor = (response: AxiosResponse) => AxiosResponse | Promise<AxiosResponse>;
+// 创建 axios 实例
+const http = axios.create({
+  baseURL: import.meta.env.VITE_BASE_API || "/api", // 根据环境变量配置
+  timeout: 60000, // 超时时间
+});
 
-interface AxiosInterceptorOptions {
-  requestInterceptors?: RequestInterceptor[];
-  responseInterceptors?: ResponseInterceptor[];
-}
-
-class AxiosHttp {
-  private axiosInstance: AxiosInstance;
-  private cancelMap = new Map<string, Canceler>();
-
-  constructor(config?: AxiosRequestConfig, options?: AxiosInterceptorOptions) {
-    this.axiosInstance = axios.create(config);
-
-    // 添加请求拦截器
-    options?.requestInterceptors?.forEach((interceptor) => {
-      this.axiosInstance.interceptors.request.use(
-        async (config: any) => {
-          config = await interceptor(config);
-          return config;
-        },
-        (error: AxiosError) => Promise.reject(error)
-      );
-    });
-
-    // 添加响应拦截器
-    options?.responseInterceptors?.forEach((interceptor) => {
-      this.axiosInstance.interceptors.response.use(
-        async (response: AxiosResponse) => {
-          response = await interceptor(response);
-          return response;
-        },
-        (error: AxiosError) => Promise.reject(error)
-      );
-    });
-
-    // 统一请求拦截 - 生成取消Token
-    this.axiosInstance.interceptors.request.use((config) => {
-      const requestKey = this.getRequestKey(config);
-      if (this.cancelMap.has(requestKey)) {
-        // 取消重复请求
-        const cancel = this.cancelMap.get(requestKey);
-        cancel?.(`取消重复请求: ${requestKey}`);
-        this.cancelMap.delete(requestKey);
-      }
-      config.cancelToken = new axios.CancelToken((cancel: Canceler) => {
-        this.cancelMap.set(requestKey, cancel);
-      });
-      return config;
-    });
-
-    // 请求完成后清除取消函数
-    this.axiosInstance.interceptors.response.use(
-      (response: AxiosResponse) => {
-        const requestKey = this.getRequestKey(response.config);
-        this.cancelMap.delete(requestKey);
-        return response;
-      },
-      (error: AxiosError) => {
-        if (axios.isCancel(error)) {
-          console.warn(error.message);
-        }
-        if (error.config) {
-          const requestKey = this.getRequestKey(error.config);
-          this.cancelMap.delete(requestKey);
-        }
-        return Promise.reject(error);
-      }
-    );
-  }
-
-  private getRequestKey(config: AxiosRequestConfig): string {
-    const url = config.url || '';
-    const method = config.method || 'get';
-    const params = config.params ? JSON.stringify(config.params) : '';
-    const data = config.data ? JSON.stringify(config.data) : '';
-    return [method, url, params, data].join('&');
+// 请求拦截器
+http.interceptors.request.use(
+  (config) => {
+    // 这里可以统一加 token
+    const token = localStorage.getItem("token");
+    if (token) {
+      config.headers.Authorization = `Bearer ${token}`;
+    }
+    return config;
+  },
+  (error) => {
+    return Promise.reject(error);
   }
+);
 
-  request<T = any>(config: AxiosRequestConfig): Promise<T> {
-    return this.axiosInstance.request<T>(config).then((res: AxiosResponse) => res.data);
-  }
-
-  get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
-    return this.request<T>({ ...config, method: 'get', url });
-  }
-
-  post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
-    return this.request<T>({ ...config, method: 'post', url, data });
-  }
-}
-
-export const request = new AxiosHttp(
-  { baseURL: '/api', timeout: 5000 },
-  {
-    requestInterceptors: [
-      (config) => {
-        config.headers = config.headers || {};
-        config.headers.Authorization = 'Bearer token';
-        return config;
+// 响应拦截器
+http.interceptors.response.use(
+  (response: any) => {
+    console.log(response, '-=-=-')
+    const state = response.state;
+    // 你可以根据后端约定的 code 来处理
+    if (state !== 200) {
+      ElMessage.error(response.msg || "请求出错");
+    }
+    return response; // 正常返回数据
+  },
+  (error) => {
+    if (error.response) {
+      const status = error.response.status;
+      switch (status) {
+        case 401:
+          ElMessage.error("登录已过期,请重新登录");
+          localStorage.removeItem("token");
+          router.push("/login");
+          break;
+        case 403:
+          ElMessage.error("没有权限");
+          break;
+        case 404:
+          ElMessage.error("接口不存在");
+          break;
+        case 500:
+          ElMessage.error("服务器错误");
+          break;
+        default:
+          ElMessage.error(error.message);
       }
-    ],
-    responseInterceptors: [
-      (response) => {
-        if (response.status !== 200) {
-          throw new Error('请求失败');
-        }
-        return response;
-      }
-    ]
+    } else {
+      ElMessage.error("网络错误");
+    }
+    return Promise.reject(error);
   }
 );
 
-// api
-//   .get('/users')
-//   .then((data) => console.log(data))
-//   .catch((err) => console.error(err.message));
-
-// // 同时重复请求会取消前一个请求
-// api.get('/users');
-// api.get('/users'); // 这会取消上一个
+export default http;

+ 132 - 0
xinkeaboard-promotion-portal/src/utils/http1.ts

@@ -0,0 +1,132 @@
+import axios, {
+  AxiosInstance,
+  AxiosRequestConfig,
+  AxiosResponse,
+  Canceler,
+  AxiosError
+} from 'axios';
+
+type RequestInterceptor = (
+  config: AxiosRequestConfig
+) => AxiosRequestConfig | Promise<AxiosRequestConfig>;
+type ResponseInterceptor = (response: AxiosResponse) => AxiosResponse | Promise<AxiosResponse>;
+
+interface AxiosInterceptorOptions {
+  requestInterceptors?: RequestInterceptor[];
+  responseInterceptors?: ResponseInterceptor[];
+}
+
+class AxiosHttp {
+  private axiosInstance: AxiosInstance;
+  private cancelMap = new Map<string, Canceler>();
+
+  constructor(config?: AxiosRequestConfig, options?: AxiosInterceptorOptions) {
+    this.axiosInstance = axios.create(config);
+
+    // 添加请求拦截器
+    options?.requestInterceptors?.forEach((interceptor) => {
+      this.axiosInstance.interceptors.request.use(
+        async (config: any) => {
+          config = await interceptor(config);
+          return config;
+        },
+        (error: AxiosError) => Promise.reject(error)
+      );
+    });
+
+    // 添加响应拦截器
+    options?.responseInterceptors?.forEach((interceptor) => {
+      this.axiosInstance.interceptors.response.use(
+        async (response: AxiosResponse) => {
+          response = await interceptor(response);
+          return response;
+        },
+        (error: AxiosError) => Promise.reject(error)
+      );
+    });
+
+    // 统一请求拦截 - 生成取消Token
+    this.axiosInstance.interceptors.request.use((config) => {
+      const requestKey = this.getRequestKey(config);
+      if (this.cancelMap.has(requestKey)) {
+        // 取消重复请求
+        const cancel = this.cancelMap.get(requestKey);
+        cancel?.(`取消重复请求: ${requestKey}`);
+        this.cancelMap.delete(requestKey);
+      }
+      config.cancelToken = new axios.CancelToken((cancel: Canceler) => {
+        this.cancelMap.set(requestKey, cancel);
+      });
+      return config;
+    });
+
+    // 请求完成后清除取消函数
+    this.axiosInstance.interceptors.response.use(
+      (response: AxiosResponse) => {
+        const requestKey = this.getRequestKey(response.config);
+        this.cancelMap.delete(requestKey);
+        return response;
+      },
+      (error: AxiosError) => {
+        if (axios.isCancel(error)) {
+          console.warn(error.message);
+        }
+        if (error.config) {
+          const requestKey = this.getRequestKey(error.config);
+          this.cancelMap.delete(requestKey);
+        }
+        return Promise.reject(error);
+      }
+    );
+  }
+
+  private getRequestKey(config: AxiosRequestConfig): string {
+    const url = config.url || '';
+    const method = config.method || 'get';
+    const params = config.params ? JSON.stringify(config.params) : '';
+    const data = config.data ? JSON.stringify(config.data) : '';
+    return [method, url, params, data].join('&');
+  }
+
+  request<T = any>(config: AxiosRequestConfig): Promise<T> {
+    return this.axiosInstance.request<T>(config).then((res: AxiosResponse) => res.data);
+  }
+
+  get<T = any>(url: string, params?:any, config?: AxiosRequestConfig): Promise<T> {
+    return this.request<T>({ ...config, method: 'get', url, params });
+  }
+
+  post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
+    return this.request<T>({ ...config, method: 'post', url, data });
+  }
+}
+
+export const request = new AxiosHttp(
+  { baseURL: '/api', timeout: 5000 },
+  {
+    requestInterceptors: [
+      (config) => {
+        config.headers = config.headers || {};
+        config.headers.Authorization = 'Bearer token';
+        return config;
+      }
+    ],
+    responseInterceptors: [
+      (response) => {
+        if (response.status !== 200) {
+          throw new Error('请求失败');
+        }
+        return response;
+      }
+    ]
+  }
+);
+
+// api
+//   .get('/users')
+//   .then((data) => console.log(data))
+//   .catch((err) => console.error(err.message));
+
+// // 同时重复请求会取消前一个请求
+// api.get('/users');
+// api.get('/users'); // 这会取消上一个

+ 1 - 1
xinkeaboard-promotion-portal/vite.config.ts

@@ -33,7 +33,7 @@ export default defineConfig({
       '/api/': {
         target: 'http://54.46.9.88:8001/',
         changeOrigin: true,
-        rewrite: (path) => path.replace(/^\/api"/, '')
+        rewrite: (path) => path.replace(/^\/api/, '')
       }
     }
   }

+ 2 - 8
xinkeaboard-web/components/NavCatHeader.vue

@@ -117,14 +117,8 @@ const navClick = (e, val) => {
     });
     window.open(routeUrl.href, "_blank");
   } else if (val.link_type == "topic") {
-    //跳转专题页
-    let routeUrl = router.resolve({
-      path: "/home/topic",
-      query: {
-        topicId: val.info.decoId,
-      },
-    });
-    window.open(routeUrl.href, "_blank");
+    let href = `/home/topic?topicId=${val.info.decoId}`
+    window.open(href, "_blank");
   } else if (val.link_type == "brand_home") {
     //品牌列表
     let routeUrl = router.resolve({

+ 2 - 7
xinkeaboard-web/components/SldDiy.vue

@@ -1236,14 +1236,9 @@ const gotoFun = (val) => {
     });
     window.open(routeUrl.href, "_blank");
   } else if (val.link_type == "topic") {
+    let href = `/home/topic?topicId=${val.info.decoId}`
     //跳转专题页
-    let routeUrl = router.push({
-      path: "/home/topic",
-      query: {
-        topicId: val.info.decoId,
-      },
-    });
-    window.open(routeUrl.href, "_blank");
+    window.open(href, "_blank");
   } else if (val.link_type == "brand_home") {
     //品牌列表
     let routeUrl = router.resolve({

+ 2 - 7
xinkeaboard-web/composables/common.js

@@ -63,13 +63,8 @@ export function diyNavToFun(val) {
     window.open(routeUrl.href, "_blank");
   } else if (val.link_type == "topic") {
     //跳转专题页
-    let routeUrl = router.push({
-      path: "/home/topic",
-      query: {
-        topicId: val.info.decoId,
-      },
-    });
-    window.open(routeUrl.href, "_blank");
+    let href = `/home/topic?topicId=${val.info.decoId}`
+    window.open(href, "_blank");
   } else if (val.link_type == "brand_home") {
     //品牌列表
     let routeUrl = router.resolve({

+ 130 - 0
xinkeaboard-web/pages/home/topic.vue

@@ -0,0 +1,130 @@
+<template>
+  <div>
+    <SldHomeTopSearch />
+    <NavTopBar/>
+    <NavCatHeader />
+    <div class="bottom_line"></div>
+    <div class="index">
+      <template v-if="firstLoading">
+        <div class="skeleton_banner"></div>
+        <div class="w_sld_react_1210 adv_04_wrap skeleton">
+          <div class="floor_title">
+            <h2>
+              <font>&nbsp;</font>
+              <span></span>
+              <font>&nbsp;</font>
+            </h2>
+          </div>
+          <div class="floor_goods">
+            <div
+              class="item"
+              v-for="(item_main, index_main) in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
+              v-bind:key="index_main"
+            >
+              <div class="wrap">
+                <a href="javascript:void(0)" class="example_text"> </a>
+                <p class="title">
+                  <a href="javascript:void(0)">{{ item_main }}</a>
+                </p>
+                <p class="price"><span></span></p>
+              </div>
+            </div>
+          </div>
+        </div>
+      </template>
+      <SldDiy
+        v-if="decorateData.data.length"
+        :decorateData="decorateData"
+        @adv19="handleAdv19"
+      />
+      <!-- 空页面 start-->
+      <SldCommonEmpty
+        v-if="!firstLoading && !decorateData.data.length"
+        totalHeight="900"
+        paddingTop="250"
+        :tip="L['我们正在努力装修中,敬请期待~']"
+      />
+      <!-- 空页面 end-->
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { useRoute } from "vue-router";
+
+const route = useRoute();
+const decorateData = reactive({ data: [] }); //装修数据
+const firstLoading = ref(true); //是否第一次加载
+const getInitData = async () => {
+  let params = {};
+  params.decoType = "topic";
+  params.decoId = route.query.topicId;
+  const { data: value, pending: pending, error, refresh } = await useFetchRaw(
+    apiUrl + "v3/system/front/pcDeco/index",
+    { params }
+  );
+  if (!pending._rawValue) {
+    firstLoading.value = false;
+  }
+  const res = value._rawValue;
+  if (res.state == 200) {
+    decorateData.data = JSON.parse(res.data.data.replace(/&quot;/g, '"'));
+    decorateData.data.map((item) => {
+      item.json_data = item.json;
+      if (item.json_data.type == "adv_19") {
+        item.json_data.data.map((child) => {
+          child.cur_tab = 0;
+        });
+      }
+      if (item.json_data && item.json_data.type == "main_banner_pc") {
+        item.json_data.data = item.json_data.info.carousel_info.data.filter((i) => i.imgUrl);
+      }
+    });
+  }
+};
+getInitData();
+</script>
+<style lang="scss" scoped>
+@import "@/assets/style/decorate.scss";
+@import "@/assets/style/theme.scss";
+.index {
+  background: #f8f8f8;
+  padding-bottom: 10px;
+}
+
+.skeleton_banner {
+  width: 100%;
+  height: 470px;
+  background: $colorSkeleton;
+  margin-bottom: 10px;
+}
+
+.skeleton {
+  &.adv_04_wrap .floor_title h2 span {
+    background: $colorSkeleton;
+    display: inline-block;
+    width: 200px;
+  }
+
+  &.adv_04_wrap .floor_goods .item .wrap .title {
+    background: $colorSkeleton;
+    width: 100%;
+
+    a {
+      color: transparent;
+    }
+  }
+
+  &.adv_04_wrap .floor_goods .item .wrap .price {
+    width: 100%;
+    height: 25px;
+  }
+
+  &.adv_04_wrap .floor_goods .item .wrap .price span {
+    display: inline-block;
+    width: 70px;
+    height: 100%;
+    background: $colorSkeleton;
+  }
+}
+</style>