Browse Source

Merge branch 'master' of http://8.152.4.3:3000/yp_web/yoe-shop-web

hurx 1 month ago
parent
commit
f2de80f53a

+ 14 - 0
package-lock.json

@@ -47,6 +47,7 @@
         "@unocss/preset-attributify": "66.5.4",
         "@unocss/preset-icons": "66.5.4",
         "@unocss/preset-uno": "66.5.4",
+        "@vitejs/plugin-basic-ssl": "^2.1.4",
         "@vitejs/plugin-vue": "5.2.4",
         "@vue/compiler-sfc": "3.5.22",
         "@vue/eslint-config-prettier": "10.2.0",
@@ -2953,6 +2954,19 @@
         "node": ">=8.10.0"
       }
     },
+    "node_modules/@vitejs/plugin-basic-ssl": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmmirror.com/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.1.4.tgz",
+      "integrity": "sha512-HXciTXN/sDBYWgeAD4V4s0DN0g72x5mlxQhHxtYu3Tt8BLa6MzcJZUyDVFCdtjNs3bfENVHVzOsmooTVuNgAAw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+      },
+      "peerDependencies": {
+        "vite": "^6.0.0 || ^7.0.0"
+      }
+    },
     "node_modules/@vitejs/plugin-vue": {
       "version": "5.2.4",
       "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz",

+ 1 - 0
package.json

@@ -58,6 +58,7 @@
     "@unocss/preset-attributify": "66.5.4",
     "@unocss/preset-icons": "66.5.4",
     "@unocss/preset-uno": "66.5.4",
+    "@vitejs/plugin-basic-ssl": "^2.1.4",
     "@vitejs/plugin-vue": "5.2.4",
     "@vue/compiler-sfc": "3.5.22",
     "@vue/eslint-config-prettier": "10.2.0",

+ 7 - 7
src/api/home/index.ts

@@ -137,13 +137,13 @@ export function getPurchaseGuideTitle(query: any) {
 }
 
 //采购指南列表
-// export function getPurchaseGuideTitle(query: any) {
-//   return request({
-//     url: '/system/indexSystem/getPurchaseGuideTitle',
-//     method: 'get',
-//     params: query
-//   });
-// }
+export function getProcurementTopicsList(query: any) {
+  return request({
+    url: '/product/indexProduct/getProcurementTopicsList',
+    method: 'get',
+    params: query
+  });
+}
 
 //外面循环
 export function getClassificationFloorList(query: any) {

+ 18 - 9
src/api/plan/index.ts

@@ -1,15 +1,15 @@
 import request from '@/utils/request';
 
-// 采购方案列表
-export function getProcurementProgramList(query: any) {
+// 解决方案/采购方案列表
+export function getPurchaseCategoryList(query: any) {
   return request({
-    url: '/product/indexProduct/getProcurementProgramList',
+    url: '/system/indexSystem/getPurchaseCategoryList',
     method: 'get',
     params: query
   });
 }
 
-// 采购方案详情
+// 解决方案/采购方案详情
 export function getProcurementProgramDetail(id: any) {
   return request({
     url: '/product/indexProduct/getProcurementProgramDetail/' + id,
@@ -17,7 +17,7 @@ export function getProcurementProgramDetail(id: any) {
   });
 }
 
-// 获取采购方案下的分组
+// 解决方案/采购方案详情下的分组
 export function getProcurementProgramGroupList(id: any) {
   return request({
     url: '/product/indexProduct/getProcurementProgramGroupList/' + id,
@@ -25,7 +25,7 @@ export function getProcurementProgramGroupList(id: any) {
   });
 }
 
-// 获取采购方案分组下的商品
+// 解决方案/采购方案分组下的商品
 export function getProcurementProgramGroupProductList(query: any) {
   return request({
     url: '/product/indexProduct/getProcurementProgramGroupProductList',
@@ -34,10 +34,19 @@ export function getProcurementProgramGroupProductList(query: any) {
   });
 }
 
-// 采购分类列表
-export function getPurchaseCategoryList(query: any) {
+// 场景采购列表
+export function getProductProgramPage(query: any) {
   return request({
-    url: '/system/indexSystem/getPurchaseCategoryList',
+    url: '/product/indexProduct/getProductProgramPage',
+    method: 'get',
+    params: query
+  });
+}
+
+// 采购方案列表
+export function getProcurementProgramList(query: any) {
+  return request({
+    url: '/product/indexProduct/getProcurementProgramList',
     method: 'get',
     params: query
   });

BIN
src/assets/images/logo2.png


+ 269 - 0
src/layout/components/nav copy.vue

@@ -0,0 +1,269 @@
+<template>
+  <!-- 导航组件 -->
+  <div class="nav flex-row-center">
+    <div class="nav-bos">
+      <div class="nav-all flex-row-center" @mouseenter="openClassify">
+        <img src="@/assets/images/layout/layout2.png" alt="" />
+        <div>全部商品分类</div>
+      </div>
+      <div @click="onPath(item.url)" v-for="(item, index) in navList" :key="index" class="nav-list" :class="item.url == route.path ? 'hig' : ''">
+        {{ item.title }}
+      </div>
+      <div class="nav-classify" v-if="classifyOpen" @mouseleave="leaveClassify">
+        <div class="classify">
+          <div
+            class="classify-list"
+            v-for="(item, index) in classifyList"
+            :class="item.id == classifyId && classifyShow ? 'classify-hig' : ''"
+            :key="index"
+            @mouseenter="enterClassify(item)"
+          >
+            <div class="label ellipsis" @click="onPath('/search?type=1&topCategoryId=' + item.id)">{{ item.label }}</div>
+            <div class="info info1 ellipsis" v-if="item.extra && item.extra.oneLable1">{{ item.extra.oneLable1 }}</div>
+            <div class="info ellipsis" v-if="item.extra && item.extra.oneLable2">{{ item.extra.oneLable2 }}</div>
+            <div class="classify-border" v-if="item.id == classifyId && classifyShow"></div>
+          </div>
+        </div>
+        <div class="classify-bos" v-if="classifyShow">
+          <div v-for="(item, index) in classifyInfo" :key="index" class="classify-item" @click="onPath('/search?type=2&middleCategoryId=' + item.id)">
+            <div class="two-level ellipsis">{{ item.label || '' }}</div>
+            <el-icon class="classify-icon" :size="14" color="#364153">
+              <ArrowRight />
+            </el-icon>
+            <div class="classify-label">
+              <div v-for="(item1, index1) in item.children" :key="index1" @click="onPath('/search?type=3&bottomCategoryId=' + item1.id)">
+                {{ item1.label || '' }}
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { onPath } from '@/utils/siteConfig';
+import { categoryStore } from '@/store/modules/category';
+const categoryStoreStore = categoryStore();
+const route = useRoute();
+const classifyList = ref<any>([]);
+const classifyOpen = ref<any>(false);
+const classifyShow = ref<any>(false);
+const classifyId = ref<any>('');
+const classifyInfo = ref<any>([]);
+const navList = ref<any>([
+  { title: '首页', url: '/index' },
+  { title: '工业品商城', url: '/indexMro' },
+  { title: '福礼商城', url: '/indexFuli' },
+  { title: '商用工程' },
+  { title: '场景采购', url: '/plan/procure' },
+  { title: '解决方案', url: '/plan' },
+  { title: '特价专区', url: '/search/special' }
+]);
+
+onMounted(async () => {
+  try {
+    // 根据当前路由路径获取分类数据
+    let path = route.path;
+    if (path != '/indexMro' && path != '/indexFuli') {
+      path = '/index';
+    }
+    classifyList.value = await categoryStoreStore.fetchCategories(path);
+  } catch (error) {
+    console.error('获取分类失败:', error);
+  }
+});
+
+//移入分类
+const openClassify = () => {
+  const path = route.path;
+  if (path != '/indexMro' && path != '/indexFuli' && path != '/index') {
+    classifyOpen.value = true;
+  }
+};
+const enterClassify = (res: any) => {
+  classifyShow.value = true;
+  classifyId.value = res.id;
+  classifyInfo.value = res.children;
+};
+
+//移出分类
+const leaveClassify = () => {
+  classifyOpen.value = false;
+  classifyShow.value = false;
+};
+</script>
+
+<style lang="scss" scoped>
+.nav {
+  width: 100%;
+  height: 48px;
+  background: #f9fafb;
+  border-top: 1px solid #e5e7eb;
+  border-bottom: 1px solid #e5e7eb;
+
+  .nav-bos {
+    width: 1200px;
+    display: flex;
+    position: relative;
+
+    .nav-all {
+      width: 234px;
+      height: 48px;
+      background: #e7000b;
+      padding: 0 10px;
+      font-size: 16px;
+      color: #ffffff;
+      cursor: pointer;
+
+      img {
+        height: 16px;
+        width: 16px;
+        margin-right: 6px;
+      }
+    }
+
+    .nav-list {
+      line-height: 48px;
+      font-size: 16px;
+      color: #364153;
+      margin: 0 16px;
+      text-align: center;
+      cursor: pointer;
+
+      &.hig {
+        color: #e7000b;
+        position: relative;
+        &::before {
+          content: '';
+          position: absolute;
+          bottom: 0;
+          left: 0px;
+          display: inline-block;
+          width: 100%;
+          height: 3px;
+          background: #e7000b;
+          margin-right: 8px;
+        }
+      }
+
+      &:hover {
+        color: #e7000b;
+      }
+    }
+
+    // 分类
+    .nav-classify {
+      position: absolute;
+      top: 100%;
+      left: 0;
+      z-index: 20;
+      .classify {
+        width: 234px;
+        background: #ffffff;
+
+        .classify-list {
+          width: 100%;
+          height: 40px;
+          cursor: pointer;
+          display: flex;
+          align-items: center;
+          padding-left: 15px;
+          position: relative;
+
+          &.classify-hig {
+            border: 1px solid var(--el-color-primary);
+            border-right: 0px solid var(--el-color-primary);
+          }
+
+          .label {
+            max-width: 100px;
+            font-weight: 600;
+            font-size: 14px;
+            color: #101828;
+            white-space: nowrap;
+            margin-right: 10px;
+
+            &:hover {
+              color: var(--el-color-primary);
+            }
+          }
+
+          .info {
+            max-width: 50px;
+            font-size: 12px;
+            color: #364153;
+            white-space: nowrap;
+
+            &.info1 {
+              margin-right: 6px;
+            }
+
+            &:hover {
+              color: var(--el-color-primary);
+            }
+          }
+
+          .classify-border {
+            position: absolute;
+            right: -1px;
+            top: 0px;
+            width: 1px;
+            height: 38px;
+            background-color: #ffffff;
+            z-index: 2;
+          }
+        }
+      }
+
+      .classify-bos {
+        position: absolute;
+        top: 0;
+        left: 100%;
+        width: 966px;
+        height: 100%;
+        border: 1px solid var(--el-color-primary);
+        background-color: #ffffff;
+        overflow-y: auto;
+        padding-left: 30px;
+
+        .classify-item {
+          display: flex;
+          padding-top: 10px;
+          border-bottom: 1px solid #e5e7eb;
+
+          .two-level {
+            width: 90px;
+            font-size: 14px;
+            color: var(--el-color-primary);
+            cursor: pointer;
+          }
+
+          .classify-icon {
+            margin: 4px 15px 0 15px;
+          }
+
+          .classify-label {
+            display: flex;
+            flex-wrap: wrap;
+            flex: 1;
+            font-size: 14px;
+            color: #364153;
+
+            div {
+              margin-right: 20px;
+              margin-bottom: 10px;
+              cursor: pointer;
+
+              &:hover {
+                color: var(--el-color-primary);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 13 - 153
src/layout/components/nav.vue

@@ -1,43 +1,14 @@
 <template>
   <!-- 导航组件 -->
-  <div class="nav flex-row-center">
+  <div class="nav-pages">
     <div class="nav-bos">
-      <div class="nav-all flex-row-center" @mouseenter="openClassify">
+      <div class="nav-all flex-row-start">
         <img src="@/assets/images/layout/layout2.png" alt="" />
         <div>全部商品分类</div>
       </div>
       <div @click="onPath(item.url)" v-for="(item, index) in navList" :key="index" class="nav-list" :class="item.url == route.path ? 'hig' : ''">
         {{ item.title }}
       </div>
-      <div class="nav-classify" v-if="classifyOpen" @mouseleave="leaveClassify">
-        <div class="classify">
-          <div
-            class="classify-list"
-            v-for="(item, index) in classifyList"
-            :class="item.id == classifyId && classifyShow ? 'classify-hig' : ''"
-            :key="index"
-            @mouseenter="enterClassify(item)"
-          >
-            <div class="label ellipsis" @click="onPath('/search?type=1&topCategoryId=' + item.id)">{{ item.label }}</div>
-            <div class="info info1 ellipsis" v-if="item.extra && item.extra.oneLable1">{{ item.extra.oneLable1 }}</div>
-            <div class="info ellipsis" v-if="item.extra && item.extra.oneLable2">{{ item.extra.oneLable2 }}</div>
-            <div class="classify-border" v-if="item.id == classifyId && classifyShow"></div>
-          </div>
-        </div>
-        <div class="classify-bos" v-if="classifyShow">
-          <div v-for="(item, index) in classifyInfo" :key="index" class="classify-item" @click="onPath('/search?type=2&middleCategoryId=' + item.id)">
-            <div class="two-level ellipsis">{{ item.label || '' }}</div>
-            <el-icon class="classify-icon" :size="14" color="#364153">
-              <ArrowRight />
-            </el-icon>
-            <div class="classify-label">
-              <div v-for="(item1, index1) in item.children" :key="index1" @click="onPath('/search?type=3&bottomCategoryId=' + item1.id)">
-                {{ item1.label || '' }}
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
     </div>
   </div>
 </template>
@@ -57,7 +28,7 @@ const navList = ref<any>([
   { title: '工业品商城', url: '/indexMro' },
   { title: '福礼商城', url: '/indexFuli' },
   { title: '商用工程' },
-  { title: '采购指南', url: '/plan/guide' },
+  { title: '场景采购', url: '/plan/procure' },
   { title: '解决方案', url: '/plan' },
   { title: '特价专区', url: '/search/special' }
 ]);
@@ -96,24 +67,24 @@ const leaveClassify = () => {
 </script>
 
 <style lang="scss" scoped>
-.nav {
+//分类
+.nav-pages {
   width: 100%;
-  height: 48px;
-  background: #f9fafb;
-  border-top: 1px solid #e5e7eb;
-  border-bottom: 1px solid #e5e7eb;
+  background-color: #ffffff;
 
   .nav-bos {
+    margin: 0 auto;
     width: 1200px;
     display: flex;
     position: relative;
+    padding-top: 15px;
 
     .nav-all {
       width: 234px;
-      height: 48px;
+      height: 40px;
       background: #e7000b;
       padding: 0 10px;
-      font-size: 16px;
+      font-size: 15px;
       color: #ffffff;
       cursor: pointer;
 
@@ -125,8 +96,8 @@ const leaveClassify = () => {
     }
 
     .nav-list {
-      line-height: 48px;
-      font-size: 16px;
+      line-height: 40px;
+      font-size: 15px;
       color: #364153;
       margin: 0 16px;
       text-align: center;
@@ -135,6 +106,7 @@ const leaveClassify = () => {
       &.hig {
         color: #e7000b;
         position: relative;
+
         &::before {
           content: '';
           position: absolute;
@@ -152,118 +124,6 @@ const leaveClassify = () => {
         color: #e7000b;
       }
     }
-
-    // 分类
-    .nav-classify {
-      position: absolute;
-      top: 100%;
-      left: 0;
-      z-index: 20;
-      .classify {
-        width: 234px;
-        background: #ffffff;
-
-        .classify-list {
-          width: 100%;
-          height: 40px;
-          cursor: pointer;
-          display: flex;
-          align-items: center;
-          padding-left: 15px;
-          position: relative;
-
-          &.classify-hig {
-            border: 1px solid var(--el-color-primary);
-            border-right: 0px solid var(--el-color-primary);
-          }
-
-          .label {
-            max-width: 100px;
-            font-weight: 600;
-            font-size: 14px;
-            color: #101828;
-            white-space: nowrap;
-            margin-right: 10px;
-
-            &:hover {
-              color: var(--el-color-primary);
-            }
-          }
-
-          .info {
-            max-width: 50px;
-            font-size: 12px;
-            color: #364153;
-            white-space: nowrap;
-
-            &.info1 {
-              margin-right: 6px;
-            }
-
-            &:hover {
-              color: var(--el-color-primary);
-            }
-          }
-
-          .classify-border {
-            position: absolute;
-            right: -1px;
-            top: 0px;
-            width: 1px;
-            height: 38px;
-            background-color: #ffffff;
-            z-index: 2;
-          }
-        }
-      }
-
-      .classify-bos {
-        position: absolute;
-        top: 0;
-        left: 100%;
-        width: 966px;
-        height: 100%;
-        border: 1px solid var(--el-color-primary);
-        background-color: #ffffff;
-        overflow-y: auto;
-        padding-left: 30px;
-
-        .classify-item {
-          display: flex;
-          padding-top: 10px;
-          border-bottom: 1px solid #e5e7eb;
-
-          .two-level {
-            width: 90px;
-            font-size: 14px;
-            color: var(--el-color-primary);
-            cursor: pointer;
-          }
-
-          .classify-icon {
-            margin: 4px 15px 0 15px;
-          }
-
-          .classify-label {
-            display: flex;
-            flex-wrap: wrap;
-            flex: 1;
-            font-size: 14px;
-            color: #364153;
-
-            div {
-              margin-right: 20px;
-              margin-bottom: 10px;
-              cursor: pointer;
-
-              &:hover {
-                color: var(--el-color-primary);
-              }
-            }
-          }
-        }
-      }
-    }
   }
 }
 </style>

+ 91 - 79
src/layout/components/search.vue

@@ -1,7 +1,7 @@
 <template>
   <!-- 搜索组件 -->
-  <div class="search flex-row-center" :style="{ 'background': meta.workbench ? '#F4F4F4' : '#ffffff' }">
-    <div class="search-bos">
+  <div class="search-bos" :style="{ 'background': meta.workbench ? '#F4F4F4' : '#ffffff' }">
+    <div class="search-content">
       <img class="logo" src="@/assets/images/head.png" alt="" />
       <div class="search-box">
         <div class="search-div flex-row-start">
@@ -13,21 +13,17 @@
               </el-icon>
             </div>
           </div>
-          <el-badge :value="cartCount">
-            <div class="cat-bos flex-row-center" @click="onPath('/cart')">
+
+          <div class="cat-bos flex-row-center">
+            <el-badge :value="cartCount" v-if="cartCount > 0">
               <img src="@/assets/images/layout/layout4.png" alt="" />
-              <span>我的购物车</span>
-            </div>
-          </el-badge>
+            </el-badge>
+            <img v-else src="@/assets/images/layout/layout4.png" alt="" />
+            <span @click="onPath('/cart')" class="ml-[15px]">我的购物车</span>
+          </div>
         </div>
         <div class="search-text">
-          <div>家纺</div>
-          <div>打印机</div>
-          <div>打印耗材</div>
-          <div>空调</div>
-          <div>取暖</div>
-          <div>开门红</div>
-          <div>劳保福利</div>
+          <div v-for="(item, index) in toplabel" :key="index">{{ item.title }}</div>
         </div>
       </div>
       <img class="code" src="@/assets/images/code.png" alt="" />
@@ -43,6 +39,15 @@ const cartCount = computed(() => cartStoreData.cartCount);
 const input = ref<any>('');
 const route = useRoute();
 const meta = ref<any>({});
+const toplabel = ref<any>([
+  { title: '家纺', url: '' },
+  { title: '打印机', url: '' },
+  { title: '打印耗材', url: '' },
+  { title: '空调', url: '' },
+  { title: '取暖', url: '' },
+  { title: '开门红', url: '' },
+  { title: '劳保福利', url: '' }
+]);
 meta.value = route.meta;
 
 watch(route, () => {
@@ -51,94 +56,101 @@ watch(route, () => {
 </script>
 
 <style lang="scss" scoped>
-.search {
+// 搜索栏
+.search-bos {
   width: 100%;
   background-color: #ffffff;
-  padding-bottom: 25px;
 
-  .search-bos {
+  .search-content {
+    margin: 0 auto;
     width: 1200px;
     display: flex;
+    padding: 0 30px;
+  }
 
-    .logo {
-      width: 185px;
-      height: 90px;
-      margin-top: 14px;
-    }
+  .logo {
+    width: 170px;
+    height: 88px;
+    border-radius: 4px;
+    margin-top: 10px;
+    margin-right: 30px;
+  }
 
-    .search-box {
-      flex: 1;
-      padding-top: 50px;
-      padding-left: 36px;
-
-      .search-div {
-        .search-input {
-          width: 656px;
-          height: 48px;
-          border-radius: 10px;
-          border: 2px solid #fb2c36;
-          padding-right: 4px;
-
-          .el-input {
-            height: 40px;
-            width: 100%;
-            font-size: 16px;
-
-            :deep(.el-input__wrapper) {
-              border: none;
-              /* 可选:去除聚焦时的高亮 */
-              box-shadow: none;
-              outline: none;
-            }
-          }
+  .search-box {
+    flex: 1;
+    height: 88px;
+    padding-top: 40px;
 
-          .bnt {
-            width: 68px;
-            height: 40px;
-            background: #e7000b;
-            border-radius: 8px;
-            font-weight: bold;
-            cursor: pointer;
-          }
-        }
+    .search-div {
+      .search-input {
+        flex: 1;
+        height: 38px;
+        border-radius: 3px;
+        border: 2px solid #fb2c36;
+        padding-right: 4px;
+        font-size: 14px;
 
-        .cat-bos {
-          width: 143px;
-          height: 48px;
-          background: #ffffff;
-          border-radius: 10px;
-          border: 1px solid #e5e7eb;
-          margin-left: 24px;
+        .el-input {
+          height: 30px;
+          width: 100%;
           font-size: 16px;
-          color: #e7000b;
-          cursor: pointer;
 
-          img {
-            width: 16px;
-            height: 16px;
-            margin-right: 8px;
-            margin-top: 2px;
+          :deep(.el-input__wrapper) {
+            border: none;
+            /* 可选:去除聚焦时的高亮 */
+            box-shadow: none;
+            outline: none;
           }
         }
+
+        .bnt {
+          width: 50px;
+          height: 30px;
+          background: #e7000b;
+          border-radius: 3px;
+          font-weight: bold;
+          cursor: pointer;
+        }
       }
 
-      .search-text {
+      .cat-bos {
+        width: 143px;
+        height: 38px;
+        background: #ffffff;
+        border-radius: 10px;
+        border: 1px solid #e5e7eb;
+        margin-left: 24px;
         font-size: 14px;
         color: #e7000b;
-        display: flex;
-        margin-top: 6px;
+        cursor: pointer;
 
-        div {
-          margin-left: 10px;
+        img {
+          width: 14px;
+          height: 14px;
+          margin-top: 2px;
         }
       }
     }
 
-    .code {
-      height: 90px;
-      width: 90px;
-      margin-top: 30px;
+    .search-text {
+      font-size: 12px;
+      color: #e7000b;
+      display: flex;
+      margin-top: 4px;
+      cursor: pointer;
+
+      div {
+        margin-left: 10px;
+      }
     }
   }
+
+  .code {
+    height: 90px;
+    width: 90px;
+    margin-top: 20px;
+    margin-left: 70px;
+    // border-radius: 4px;
+  }
 }
 </style>

+ 102 - 0
src/permission copy.ts

@@ -0,0 +1,102 @@
+import { to as tos } from 'await-to-js';
+import router from './router';
+import NProgress from 'nprogress';
+import 'nprogress/nprogress.css';
+import { getToken } from '@/utils/auth';
+import { isPathMatch } from '@/utils/validate';
+import { useUserStore } from '@/store/modules/user';
+import { ElMessage } from 'element-plus/es';
+import { getCurrentSite, SITE_ROUTES } from '@/utils/siteConfig';
+import { cartStore } from '@/store/modules/cart';
+
+NProgress.configure({ showSpinner: false });
+const whiteList = [
+  '/login',
+  '/register',
+  '/social-callback',
+  '/register*',
+  '/register/*',
+  '/index',
+  '/',
+  '/indexB',
+  '/indexMro',
+  '/indexFuli',
+  '/reg',
+  '/search',
+  '/item',
+  '/breg',
+  '/greg',
+  '/diy'
+];
+
+
+const isWhiteList = (path: string) => {
+  return whiteList.some((pattern) => isPathMatch(pattern, path));
+};
+
+// 获取主站完整 URL(用于跨域跳转)
+function getMainSiteUrl(path: string) {
+  if (import.meta.env.PROD) {
+    return `https://www.xiaoluwebsite.xyz${path}`;
+  } else {
+    // 本地开发:指向 www.xiaoluwebsite.xyz 加上当前运行的端口
+    // 假设你启动 vite 后访问的是 http://www.xiaoluwebsite.xyz
+    const devPort = window.location.port || import.meta.env.VITE_APP_PORT;
+    return `http://www.xiaoluwebsite.xyz:${devPort}${path}`;
+  }
+}
+
+router.beforeEach(async (to, from, next) => {
+  NProgress.start();
+  const site = getCurrentSite();
+  const allowedPaths = SITE_ROUTES[site];
+
+  if (getToken()) {
+    const [err] = await tos(useUserStore().getInfo());
+    if (err) {
+      await useUserStore().logout();
+      ElMessage.error(err);
+      if (import.meta.env.VITE_DOMAIN_NAME == 'true') {
+        window.location.href = getMainSiteUrl('/login');
+      } else {
+        next('/login');
+      }
+      NProgress.done();
+    } else {
+      cartStore().onCartCount();
+      // 是否开启多域名
+      if (import.meta.env.VITE_DOMAIN_NAME == 'true') {
+        if (!allowedPaths.includes(to.path)) {
+          console.warn(`[${site}] 禁止访问 ${to.path}`);
+          window.location.href = getMainSiteUrl('/index');
+          NProgress.done();
+        } else {
+          next();
+          NProgress.done();
+        }
+      } else {
+        next();
+        NProgress.done();
+      }
+    }
+  } else {
+    // 没有 token
+    if (isWhiteList(to.path)) {
+      next();
+      NProgress.done();
+    } else {
+      // 非白名单且无 token,强制去登录
+      if (import.meta.env.VITE_DOMAIN_NAME == 'true') {
+        window.location.href = getMainSiteUrl('/login');
+      } else {
+        next('/login');
+      }
+      // 或者 '/login' 根据你的白名单设置
+      NProgress.done();
+    }
+  }
+});
+
+router.afterEach(() => {
+  NProgress.done();
+});

+ 11 - 21
src/permission.ts

@@ -36,12 +36,12 @@ const isWhiteList = (path: string) => {
 // 获取主站完整 URL(用于跨域跳转)
 function getMainSiteUrl(path: string) {
   if (import.meta.env.PROD) {
-    return `https://www.yingpai365.com${path}`;
+    return `https://www.xiaoluwebsite.xyz${path}`;
   } else {
-    // 本地开发:指向 www.yingpai365.com 加上当前运行的端口
-    // 假设你启动 vite 后访问的是 http://www.yingpai365.com
+    // 本地开发:指向 www.xiaoluwebsite.xyz 加上当前运行的端口
+    // 假设你启动 vite 后访问的是 http://www.xiaoluwebsite.xyz
     const devPort = window.location.port || import.meta.env.VITE_APP_PORT;
-    return `http://www.yingpai365.com:${devPort}${path}`;
+    return `https://www.xiaoluwebsite.xyz:${devPort}${path}`;
   }
 }
 
@@ -50,9 +50,14 @@ router.beforeEach(async (to, from, next) => {
   const site = getCurrentSite();
   const allowedPaths = SITE_ROUTES[site];
 
-  if (getToken()) {
+  //不需要请求登录信息
+  if (isWhiteList(to.path)) {
+    next();
+    NProgress.done();
+  } else {
     const [err] = await tos(useUserStore().getInfo());
-    if (err) {
+    // if (err) {
+    if (false) {
       await useUserStore().logout();
       ElMessage.error(err);
       if (import.meta.env.VITE_DOMAIN_NAME == 'true') {
@@ -78,21 +83,6 @@ router.beforeEach(async (to, from, next) => {
         NProgress.done();
       }
     }
-  } else {
-    // 没有 token
-    if (isWhiteList(to.path)) {
-      next();
-      NProgress.done();
-    } else {
-      // 非白名单且无 token,强制去登录
-      if (import.meta.env.VITE_DOMAIN_NAME == 'true') {
-        window.location.href = getMainSiteUrl('/login');
-      } else {
-        next('/login');
-      }
-      // 或者 '/login' 根据你的白名单设置
-      NProgress.done();
-    }
   }
 });
 

+ 2 - 2
src/router/index.ts

@@ -228,13 +228,13 @@ export const constantRoutes: RouteRecordRaw[] = [
         path: '/plan/procure',
         component: () => import('@/views/plan/procure.vue'),
         name: 'PlanProcure',
-        meta: { title: '采购方案', nav: true, breadcrumb: true }
+        meta: { title: '场景采购', nav: true, breadcrumb: true }
       },
       {
         path: '/plan_info/procure',
         component: () => import('@/views/plan_info/procure.vue'),
         name: 'PlanInfoProcure',
-        meta: { title: '采购方案详情', nav: true, breadcrumb: true, navList: [{ title: '采购方案', url: '/plan/procure' }] }
+        meta: { title: '场景采购详情', nav: true, breadcrumb: true, navList: [{ title: '场景采购', url: '/plan/procure' }] }
       },
       {
         path: '/plan/guide',

+ 3 - 0
src/types/components.d.ts

@@ -14,6 +14,7 @@ declare module 'vue' {
     copy: typeof import('./../components/TablePagination copy/index.vue')['default']
     DictTag: typeof import('./../components/DictTag/index.vue')['default']
     Editor: typeof import('./../components/Editor/index.vue')['default']
+    ElAffix: typeof import('element-plus/es')['ElAffix']
     ElAutocomplete: typeof import('element-plus/es')['ElAutocomplete']
     ElAvatar: typeof import('element-plus/es')['ElAvatar']
     ElBadge: typeof import('element-plus/es')['ElBadge']
@@ -29,6 +30,8 @@ declare module 'vue' {
     ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
     ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
+    ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
+    ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
     ElDialog: typeof import('element-plus/es')['ElDialog']
     ElDivider: typeof import('element-plus/es')['ElDivider']
     ElDrawer: typeof import('element-plus/es')['ElDrawer']

+ 62 - 37
src/utils/request.ts

@@ -15,12 +15,12 @@ import router from '@/router';
 // 获取主站完整 URL(用于跨域跳转)
 function getMainSiteUrl(path: string) {
   if (import.meta.env.PROD) {
-    return `https://www.yingpai365.com${path}`;
+    return `https://www.xiaoluwebsite.xyz${path}`;
   } else {
-    // 本地开发:指向 www.yingpai365.com 加上当前运行的端口
-    // 假设你启动 vite 后访问的是 http://www.yingpai365.com
+    // 本地开发:指向 www.xiaoluwebsite.xyz 加上当前运行的端口
+    // 假设你启动 vite 后访问的是 http://www.xiaoluwebsite.xyz
     const devPort = window.location.port || import.meta.env.VITE_APP_PORT;
-    return `http://www.yingpai365.com:${devPort}${path}`;
+    return `https://www.xiaoluwebsite.xyz:${devPort}${path}`;
   }
 }
 
@@ -40,7 +40,7 @@ axios.defaults.headers['clientid'] = import.meta.env.VITE_APP_CLIENT_ID;
 // 创建 axios 实例
 const service = axios.create({
   baseURL: import.meta.env.VITE_APP_BASE_API,
-  // withCredentials: true,
+  withCredentials: true,
   timeout: 50000,
   transitional: {
     // 超时错误更明确
@@ -139,6 +139,30 @@ service.interceptors.response.use(
       return res.data;
     }
     if (code === 401) {
+      if (!isRelogin.show) {
+        isRelogin.show = true;
+        ElMessageBox.confirm('登录状态已过期,请重新登录', '登录提示', {
+          confirmButtonText: '重新登录',
+          type: 'warning'
+        })
+          .then(() => {
+            isRelogin.show = false;
+            useUserStore()
+              .logout()
+              .then(() => {
+                if (import.meta.env.VITE_DOMAIN_NAME == 'true') {
+                  window.location.href = getMainSiteUrl('/login');
+                } else {
+                  router.push('/login');
+                }
+              });
+          })
+          .catch(() => {
+            isRelogin.show = false;
+          });
+      }
+      return Promise.reject('无效的会话,或者会话已过期,请重新登录。');
+
       // prettier-ignore
       // if (!isRelogin.show) {
       //   isRelogin.show = true;
@@ -164,14 +188,15 @@ service.interceptors.response.use(
       //   isRelogin.show = false;
       // });
       // }
-      useUserStore().logout().then(() => {
-        if (import.meta.env.VITE_DOMAIN_NAME == 'true') {
-          window.location.href = getMainSiteUrl('/login');
-        }else{
-          router.push('/login');
-        }
-      });
-      return Promise.reject('无效的会话,或者会话已过期,请重新登录。');
+
+      // useUserStore().logout().then(() => {
+      //   if (import.meta.env.VITE_DOMAIN_NAME == 'true') {
+      //     window.location.href = getMainSiteUrl('/login');
+      //   }else{
+      //     router.push('/login');
+      //   }
+      // });
+      // return Promise.reject('无效的会话,或者会话已过期,请重新登录。');
     } else if (code === HttpStatus.SERVER_ERROR) {
       ElMessage({ message: msg, type: 'error' });
       // return Promise.reject(new Error(msg));
@@ -204,31 +229,31 @@ export function download(url: string, params: any, fileName: string) {
   downloadLoadingInstance = ElLoading.service({ text: '正在下载数据,请稍候', background: 'rgba(0, 0, 0, 0.7)' });
   // prettier-ignore
   return service.post(url, params, {
-      transformRequest: [
-        (params: any) => {
-          return tansParams(params);
-        }
-      ],
-      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
-      responseType: 'blob'
-    }).then(async (resp: any) => {
-      const isLogin = blobValidate(resp);
-      if (isLogin) {
-        const blob = new Blob([resp]);
-        FileSaver.saveAs(blob, fileName);
-      } else {
-        const blob = new Blob([resp]);
-        const resText = await blob.text();
-        const rspObj = JSON.parse(resText);
-        const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'];
-        ElMessage.error(errMsg);
+    transformRequest: [
+      (params: any) => {
+        return tansParams(params);
       }
-      downloadLoadingInstance.close();
-    }).catch((r: any) => {
-      console.error(r);
-      ElMessage.error('下载文件出现错误,请联系管理员!');
-      downloadLoadingInstance.close();
-    });
+    ],
+    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+    responseType: 'blob'
+  }).then(async (resp: any) => {
+    const isLogin = blobValidate(resp);
+    if (isLogin) {
+      const blob = new Blob([resp]);
+      FileSaver.saveAs(blob, fileName);
+    } else {
+      const blob = new Blob([resp]);
+      const resText = await blob.text();
+      const rspObj = JSON.parse(resText);
+      const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'];
+      ElMessage.error(errMsg);
+    }
+    downloadLoadingInstance.close();
+  }).catch((r: any) => {
+    console.error(r);
+    ElMessage.error('下载文件出现错误,请联系管理员!');
+    downloadLoadingInstance.close();
+  });
 }
 // 导出 axios 实例
 export default service;

+ 37 - 38
src/utils/siteConfig.ts

@@ -71,24 +71,24 @@ export function getCurrentSite(): any {
 
   // 定义本地开发环境的域名映射关系
   // 确保你的 hosts 文件已经配置了这些域名指向 127.0.0.1
-  if (host === 'www.yingpai365.com' || host === 'localhost') return 'www'; // 兼容未配hosts的情况
-  if (host === 'b.yingpai365.com') return 'b';
-  if (host === 'mro.yingpai365.com') return 'mro';
-  if (host === 'fuli.yingpai365.com') return 'fuli';
-  if (host === 'reg.yingpai365.com') return 'reg';
-  if (host === 'breg.yingpai365.com') return 'breg';
-  if (host === 'greg.yingpai365.com') return 'greg';
-  if (host === 'passport.yingpai365.com') return 'passport';
-  if (host === 'search.yingpai365.com') return 'search';
-  if (host === 'item.yingpai365.com') return 'item';
-  if (host === 'cart.yingpai365.com') return 'cart';
-  if (host === 'trad.yingpai365.com') return 'trad';
-  if (host === 'payc.yingpai365.com') return 'payc';
-  if (host === 'order.yingpai365.com') return 'order';
-  if (host === 'plan.yingpai365.com') return 'plan';
-  if (host === 'plan_info.yingpai365.com') return 'plan_info';
-  if (host === 'i.yingpai365.com') return 'i';
-  if (host === 'easybuv.yingpai365.com') return 'easybuv';
+  if (host === 'www.xiaoluwebsite.xyz' || host === 'localhost') return 'www'; // 兼容未配hosts的情况
+  if (host === 'b.xiaoluwebsite.xyz') return 'b';
+  if (host === 'mro.xiaoluwebsite.xyz') return 'mro';
+  if (host === 'fuli.xiaoluwebsite.xyz') return 'fuli';
+  if (host === 'reg.xiaoluwebsite.xyz') return 'reg';
+  if (host === 'breg.xiaoluwebsite.xyz') return 'breg';
+  if (host === 'greg.xiaoluwebsite.xyz') return 'greg';
+  if (host === 'passport.xiaoluwebsite.xyz') return 'passport';
+  if (host === 'search.xiaoluwebsite.xyz') return 'search';
+  if (host === 'item.xiaoluwebsite.xyz') return 'item';
+  if (host === 'cart.xiaoluwebsite.xyz') return 'cart';
+  if (host === 'trad.xiaoluwebsite.xyz') return 'trad';
+  if (host === 'payc.xiaoluwebsite.xyz') return 'payc';
+  if (host === 'order.xiaoluwebsite.xyz') return 'order';
+  if (host === 'plan.xiaoluwebsite.xyz') return 'plan';
+  if (host === 'plan_info.xiaoluwebsite.xyz') return 'plan_info';
+  if (host === 'i.xiaoluwebsite.xyz') return 'i';
+  if (host === 'easybuv.xiaoluwebsite.xyz') return 'easybuv';
 
   // 生产环境逻辑 (保持不变,或者合并到上面的判断中)
   if (import.meta.env.PROD) {
@@ -133,24 +133,24 @@ export function onPath(path: string) {
     let url = '';
     // 域名映射表 (保持不变)
     const domainMap: Record<string, string> = {
-      www: 'www.yingpai365.com',
-      b: 'b.yingpai365.com',
-      mro: 'mro.yingpai365.com',
-      fuli: 'fuli.yingpai365.com',
-      reg: 'reg.yingpai365.com',
-      breg: 'breg.yingpai365.com',
-      greg: 'greg.yingpai365.com',
-      passport: 'passport.yingpai365.com',
-      search: 'search.yingpai365.com',
-      item: 'item.yingpai365.com',
-      cart: 'cart.yingpai365.com',
-      trad: 'trad.yingpai365.com',
-      payc: 'payc.yingpai365.com',
-      order: 'order.yingpai365.com',
-      plan: 'plan.yingpai365.com',
-      plan_info: 'plan_info.yingpai365.com',
-      i: 'i.yingpai365.com',
-      easybuv: 'easybuv.yingpai365.com'
+      www: 'www.xiaoluwebsite.xyz',
+      b: 'b.xiaoluwebsite.xyz',
+      mro: 'mro.xiaoluwebsite.xyz',
+      fuli: 'fuli.xiaoluwebsite.xyz',
+      reg: 'reg.xiaoluwebsite.xyz',
+      breg: 'breg.xiaoluwebsite.xyz',
+      greg: 'greg.xiaoluwebsite.xyz',
+      passport: 'passport.xiaoluwebsite.xyz',
+      search: 'search.xiaoluwebsite.xyz',
+      item: 'item.xiaoluwebsite.xyz',
+      cart: 'cart.xiaoluwebsite.xyz',
+      trad: 'trad.xiaoluwebsite.xyz',
+      payc: 'payc.xiaoluwebsite.xyz',
+      order: 'order.xiaoluwebsite.xyz',
+      plan: 'plan.xiaoluwebsite.xyz',
+      plan_info: 'plan_info.xiaoluwebsite.xyz',
+      i: 'i.xiaoluwebsite.xyz',
+      easybuv: 'easybuv.xiaoluwebsite.xyz'
     };
 
     const baseDomain = domainMap[targetSite];
@@ -159,9 +159,8 @@ export function onPath(path: string) {
       url = `https://${baseDomain}${path}`;
     } else {
       const devPort = window.location.port || import.meta.env.VITE_APP_PORT;
-      url = `http://${baseDomain}:${devPort}${path}`;
+      url = `https://${baseDomain}:${devPort}${path}`;
     }
-
     // 【修改】根据是否同域名决定跳转方式
     if (isSameSite) {
       // 同域名且传入了 router,使用 Vue Router 内部跳转

File diff suppressed because it is too large
+ 25 - 5
src/views/breg/index.vue


+ 1471 - 0
src/views/home/index copy.vue

@@ -0,0 +1,1471 @@
+<template>
+  <div class="home-pages">
+    <!--  头部 -->
+    <div class="home-head" @mouseleave="leaveClassify">
+      <div class="classify" :class="classifyShow ? 'classify-show' : ''">
+        <div
+          class="classify-list"
+          v-for="(item, index) in classifyList"
+          :class="item.id == classifyId && classifyShow ? 'classify-hig' : ''"
+          :key="index"
+          @mouseenter="enterClassify(item)"
+          v-show="classifyShow ? true : Number(index) < 13"
+        >
+          <div class="label ellipsis" @click="onPath('/search?type=1&topCategoryId=' + item.id)">{{ item.label }}</div>
+          <div class="info info1 ellipsis" v-if="item.extra && item.extra.oneLable1">{{ item.extra.oneLable1 }}</div>
+          <div class="info ellipsis" v-if="item.extra && item.extra.oneLable2">{{ item.extra.oneLable2 }}</div>
+          <div class="classify-border" v-if="item.id == classifyId && classifyShow"></div>
+        </div>
+      </div>
+      <div class="classify-bos" v-if="classifyShow">
+        <div v-for="(item, index) in classifyInfo" :key="index" class="classify-item" @click="onPath('/search?type=2&middleCategoryId=' + item.id)">
+          <div class="two-level ellipsis">{{ item.label || '' }}</div>
+          <el-icon class="classify-icon" :size="14" color="#364153">
+            <ArrowRight />
+          </el-icon>
+          <div class="classify-label">
+            <div v-for="(item1, index1) in item.children" :key="index1" @click="onPath('/search?type=3&bottomCategoryId=' + item1.id)">
+              {{ item1.label || '' }}
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="head-bos">
+        <div class="carousel">
+          <el-carousel trigger="click" height="407px">
+            <el-carousel-item v-for="item in carouselList" :key="item">
+              <img :src="item.imageUrl" alt="" />
+            </el-carousel-item>
+          </el-carousel>
+        </div>
+        <div class="head-box">
+          <div v-for="(item, index) in AdList" :key="index" class="head-item" @click="onPath('/plan?id=' + item.id)">
+            <img :src="item.imageUrl" alt="" />
+          </div>
+        </div>
+      </div>
+      <!-- 右边 -->
+      <div class="head-right">
+        <div class="login-bos">
+          <div class="login-box">
+            <img :src="userInfo.avatar ? userInfo.avatar : profile" alt="" />
+            <div>
+              <div class="login1">您好,欢迎来到优易达</div>
+              <div class="login2">{{ userInfo.nickName ? userInfo.nickName : '请先登录' }}</div>
+            </div>
+          </div>
+          <div class="login-btn">
+            <el-button v-if="!userInfo.nickName" type="primary" round size="small" style="width: 64px" @click="onPath('/login')">登录</el-button>
+            <!-- @click="onPath('/reg')" -->
+            <el-button v-if="!userInfo.nickName" type="primary" plain round size="small" style="width: 64px">注册</el-button>
+            <el-button v-if="userInfo.nickName" type="primary" plain round size="small" style="width: 64px" @click="onlogout()">退出</el-button>
+          </div>
+        </div>
+        <div class="real-time">
+          <div class="real-title flex-row-between">
+            <div class="real1">优易资讯</div>
+            <div class="real2 flex-row-start">
+              <div>更多</div>
+              <el-icon :size="13" color="#83899F">
+                <ArrowRight />
+              </el-icon>
+            </div>
+          </div>
+          <template v-for="(item, index) in realList" :key="index">
+            <div @click="onPath('/solve/real?id=' + item.id)" class="real-list ellipsis" v-if="Number(index) < 7">{{ item.announcementTitle }}</div>
+          </template>
+        </div>
+        <div class="interests">
+          <div class="interests-title">企业会员权益</div>
+          <div class="interests-bos">
+            <div v-for="(item, index) in interestsList" :key="index" class="interests-item flex-column-center">
+              <img :src="item.imageUrl" alt="" />
+              <div>{{ item.title }}</div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <!-- 热门方案 -->
+    <div class="home-title flex-row-between">
+      <div>
+        <span class="title1">{{ hotTitle.title }}</span>
+        <span class="title2">{{ hotTitle.subtitle }}</span>
+      </div>
+      <div class="title-more flex-row-start" @click="onPath(hotTitle.linkUrl ? hotTitle.linkUrl : '/plan')">
+        <div style="margin-right: 5px">{{ hotTitle.linkWord }}</div>
+        <el-icon :size="13" color="#83899F">
+          <ArrowRight />
+        </el-icon>
+      </div>
+    </div>
+    <div class="hot-bos">
+      <template v-for="(item, index) in hotList" :key="index">
+        <div class="hot-list flex-column-center" v-if="Number(index) < 4" @click="onPath('/plan_info?id=' + item.id)">
+          <div class="hot1">{{ item.advertTitle }}</div>
+          <div class="hot2">{{ item.advertBrief }}</div>
+          <img :src="item.coverImage" alt="" />
+        </div>
+      </template>
+    </div>
+    <!-- 场景采购 -->
+    <div class="home-title flex-row-between">
+      <div>
+        <span class="title1">{{ sceneTitle.title }}</span>
+        <span class="title2">{{ sceneTitle.subtitle }}</span>
+      </div>
+      <div class="title-more flex-row-start" @click="onPath(sceneTitle.linkUrl ? sceneTitle.linkUrl : '/plan/procure')">
+        <div style="margin-right: 5px">{{ sceneTitle.linkWord }}</div>
+        <el-icon :size="13" color="#83899F">
+          <ArrowRight />
+        </el-icon>
+      </div>
+    </div>
+    <div class="scene-bos">
+      <template v-for="(item, index) in sceneList" :key="index">
+        <div class="scene-list" v-if="Number(index) < 5" @click="onPath('/plan_info?id=' + item.id)">
+          <div class="scene-box">
+            <!-- <div class="scene1">{{ item.advertTitle }}</div>
+            <div class="scene2">{{ item.advertBrief }}</div> -->
+          </div>
+          <img :src="item.coverImage" alt="" />
+        </div>
+      </template>
+    </div>
+    <!-- 大牌推荐 -->
+    <div class="home-title flex-row-between">
+      <div>
+        <span class="title1">{{ bigbrandTitle.title }}</span>
+        <span class="title2">{{ bigbrandTitle.subtitle }}</span>
+      </div>
+      <div class="title-more flex-row-start">
+        <div style="margin-right: 5px">{{ bigbrandTitle.linkWord }}</div>
+        <el-icon :size="13" color="#83899F">
+          <ArrowRight />
+        </el-icon>
+      </div>
+    </div>
+    <div class="big-brand">
+      <img class="bigBrand-one" :src="bigbrandOne.coverImage" alt="" />
+      <div class="bigBrand-bos">
+        <template v-for="(item, index) in bigbrandList" :key="index">
+          <div class="bigBrand-list" v-if="Number(index) < 10">
+            <img :src="item.brandLogo" alt="" />
+            <div class="bigBrand1">{{ item.brandName || '' }}</div>
+            <div class="bigBrand2">
+              {{ item.advertBrief || '' }}
+            </div>
+          </div>
+        </template>
+      </div>
+    </div>
+    <!-- 行家精选 -->
+    <div class="home-title flex-row-between">
+      <div>
+        <span class="title1">{{ expertTitle.title }}</span>
+        <span class="title2">{{ expertTitle.subtitle }}</span>
+      </div>
+      <div class="title-more flex-row-start" @click="onPath(hotTitle.linkUrl ? hotTitle.linkUrl : '/search/special')">
+        <div style="margin-right: 5px">{{ expertTitle.linkWord }}</div>
+        <el-icon :size="13" color="#83899F">
+          <ArrowRight />
+        </el-icon>
+      </div>
+    </div>
+    <div class="expert-bos">
+      <template v-for="(item, index) in expertList" :key="index">
+        <div class="expert-list" v-if="Number(index) < 5" @click="onPath('/item?id=' + item.id)">
+          <img :src="item.productImage" alt="" />
+          <div class="itemName ellipsis">{{ item.itemName || '' }}</div>
+          <div class="price">
+            <span class="memberPrice">¥{{ item.memberPrice }}</span>
+            <span class="marketPrice">¥{{ item.marketPrice }}</span>
+          </div>
+        </div>
+      </template>
+    </div>
+    <!-- 采购指南 -->
+    <div class="home-title flex-row-between">
+      <div>
+        <span class="title1">{{ procureTitle.title }}</span>
+        <span class="title2">{{ procureTitle.subtitle }}</span>
+      </div>
+      <div class="title-more flex-row-start" @click="onPath(hotTitle.linkUrl ? hotTitle.linkUrl : '/plan/guide')">
+        <div style="margin-right: 5px">{{ procureTitle.linkWord }}</div>
+        <el-icon :size="13" color="#83899F">
+          <ArrowRight />
+        </el-icon>
+      </div>
+    </div>
+    <div class="procure-bos">
+      <template v-for="(item, index) in procureList" :key="index">
+        <div class="procure-list" v-if="Number(index) < 3">
+          <img :src="item.coverImage" alt="" />
+          <div class="procure1">{{ item.title || '' }}</div>
+          <div class="procure2">{{ item.subtitle || '' }}</div>
+        </div>
+      </template>
+    </div>
+    <!-- 循环-商品 -->
+    <div v-for="(item1, index1) in homeList" :key="index1">
+      <!-- 头部 -->
+      <div class="home-title flex-row-between">
+        <div>
+          <span class="title1">{{ item1.floorName }}</span>
+          <span class="title2">{{ item1.floorDescribe }}</span>
+        </div>
+        <div class="title-more flex-row-start">
+          <div style="margin-right: 5px">{{ item1.floorLabel }}</div>
+          <el-icon :size="13" color="#83899F">
+            <ArrowRight />
+          </el-icon>
+        </div>
+      </div>
+      <!-- 商品 -->
+      <div class="goods-bos" v-if="item1.imgOne">
+        <img class="goods-img" :src="item1.imgOne.advertiseImage" alt="" />
+        <div class="home1-bos" v-if="item1.home1List && item1.home1List.length > 0">
+          <div class="floorName">{{ item1.floorName }}</div>
+          <div v-for="(item2, index2) in item1.home1List" :key="index2" class="home1-list">
+            <img :src="item2.advertiseImage" alt="" />
+            <div style="flex: 1; width: 0">
+              <div class="advertiseName">{{ item2.advertiseName }}</div>
+              <div class="advertiseDescribe ellipsis">{{ item2.advertiseDescribe }}</div>
+            </div>
+          </div>
+        </div>
+        <div class="home2-bos" v-if="item1.home2List && item1.home2List.length > 0">
+          <div>
+            <div class="floorName">发现</div>
+            <div class="home2-box">
+              <div v-for="(item2, index2) in item1.home2List" :key="index2" class="home2-list">
+                <div style="flex: 1; width: 0">
+                  <div class="advertiseName ellipsis">{{ item2.advertiseName }}</div>
+                  <div class="advertiseDescribe ellipsis">{{ item2.advertiseDescribe }}</div>
+                  <el-button class="bnt" type="primary" size="small">立即进入</el-button>
+                </div>
+                <img :src="item2.advertiseImage" alt="" />
+              </div>
+            </div>
+          </div>
+          <div class="home2-two" v-if="item1.infoTwo">
+            <div class="advertiseName">{{ item1.infoTwo.advertiseName }}</div>
+            <div class="advertiseDescribe">{{ item1.infoTwo.advertiseDescribe }}</div>
+            <img :src="item1.infoTwo.advertiseImage" alt="" />
+          </div>
+        </div>
+      </div>
+      <div class="shop-bos">
+        <div class="shop-nav">
+          <div class="nav-title">采购导航</div>
+          <div class="nav-bos">
+            <div v-for="(item2, index2) in item1.navList" :key="index2" class="nav-list flex-row-center" :class="index2 == 0 ? 'hig' : ''">
+              {{ item2.labelName }}
+            </div>
+          </div>
+        </div>
+        <template v-for="(item2, index2) in item1.shopList" :key="index2">
+          <div class="shop-list" v-if="Number(index2) < 4" @click="onInfo(item2)">
+            <img :src="item2.productImage" alt="" />
+            <div class="itemName ellipsis2">{{ item2.itemName }}</div>
+            <div class="price">
+              <span class="memberPrice">¥{{ item2.memberPrice }}</span>
+              <span class="marketPrice">¥{{ item2.marketPrice }}</span>
+            </div>
+          </div>
+        </template>
+      </div>
+    </div>
+    <!-- 项目案例 -->
+    <div class="home-title flex-row-between">
+      <div>
+        <span class="title1">{{ projectTitle.title }}</span>
+        <span class="title2">{{ projectTitle.subtitle }}</span>
+      </div>
+      <div class="title-more flex-row-start" @click="onPath(projectTitle.linkUrl ? projectTitle.linkUrl : '/plan/project')">
+        <div style="margin-right: 5px">{{ projectTitle.linkWord }}</div>
+        <el-icon :size="13" color="#83899F">
+          <ArrowRight />
+        </el-icon>
+      </div>
+    </div>
+    <div class="project-bos">
+      <template v-for="(item, index) in projectList" :key="index">
+        <div class="project-list" v-if="Number(index) < 3">
+          <img :src="item.uploadProgram" alt="" />
+          <div class="project-box">
+            <div class="project1">{{ item.caseTitle || '' }}</div>
+            <div class="project2">
+              {{ item.projectBrief || '' }}
+            </div>
+            <div class="project-more flex-row-between">
+              <div></div>
+              <div class="flex-row-start">
+                <div style="margin-right: 5px">了解详情</div>
+                <el-icon :size="14" color="#E7000B">
+                  <ArrowRight />
+                </el-icon>
+              </div>
+            </div>
+          </div>
+        </div>
+      </template>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import profile from '@/assets/images/profile.jpg';
+import { onPath } from '@/utils/siteConfig';
+import { getToken } from '@/utils/auth';
+import { getInfo } from '@/api/login';
+import { getPlatformIndexDiyPcPage } from '@/api/home/diy';
+import {
+  getProductCategoryTree,
+  getHomeAdList,
+  getYouYiZiXunPage,
+  getEnterpriseMemberEquityList,
+  getHomeThreeAdList,
+  getHotSchemeTitle,
+  getHotSchemeList,
+  getScenePurchaseTitle,
+  getScenePurchaseList,
+  getPlatformFlashSaleTitle,
+  getPlatformFlashSaleList,
+  getPlatformFlashSaleBrand,
+  getExpertSelectionTitle,
+  getProcurementTopicsList,
+  getPurchaseGuideTitle,
+  getExpertSelectionList,
+  getClassificationFloorList,
+  getClassificationFloorDetail,
+  getClassificationFloorLabel,
+  getClassificationFloorDetail2,
+  getProjectCaseTitle,
+  getProjectCaseList
+} from '@/api/home/index';
+
+const userInfo = ref<any>({});
+const classifyList = ref<any>([]);
+const classifyShow = ref<any>(false);
+const classifyId = ref<any>('');
+const classifyInfo = ref<any>([]);
+const carouselList = ref<any>([]);
+const realList = ref<any>([]);
+const interestsList = ref<any>([]);
+const AdList = ref<any>([]);
+const hotTitle = ref<any>('');
+const hotList = ref<any>([]);
+const sceneTitle = ref<any>('');
+const sceneList = ref<any>([]);
+const expertTitle = ref<any>('');
+const expertList = ref<any>([{}, {}, {}, {}, {}]);
+const procureTitle = ref<any>('');
+const procureList = ref<any>([{}, {}, {}, {}, {}]);
+const projectTitle = ref<any>('');
+const projectList = ref<any>([{}, {}, {}, {}, {}]);
+
+const bigbrandTitle = ref<any>('');
+const bigbrandList = ref<any>([]);
+const bigbrandOne = ref<any>({});
+
+const homeList = ref<any>([]);
+const router = useRouter();
+
+getPlatformIndexDiyPcPage({}).then((res) => {
+  if (res.code == 200) {
+  }
+});
+
+onMounted(() => {
+  if (getToken()) {
+    getInfo().then((res) => {
+      if (res.code == 200) {
+        userInfo.value = res.data.user;
+      }
+    });
+  }
+});
+
+//移入分类
+const enterClassify = (res: any) => {
+  classifyShow.value = true;
+  classifyId.value = res.id;
+  classifyInfo.value = res.children;
+};
+
+//移出分类
+const leaveClassify = () => {
+  classifyShow.value = false;
+};
+
+//头部分类
+import { categoryStore } from '@/store/modules/category';
+const categoryStoreStore = categoryStore();
+onMounted(async () => {
+  try {
+    // 根据当前路由路径获取分类数据
+    classifyList.value = await categoryStoreStore.fetchCategories('/index');
+  } catch (error) {
+    console.error('获取分类失败:', error);
+  }
+});
+
+//头部轮播
+getHomeAdList({}).then((res) => {
+  if (res.code == 200) {
+    carouselList.value = res.data;
+  }
+});
+
+//头部优易资讯
+getYouYiZiXunPage({}).then((res) => {
+  if (res.code == 200) {
+    realList.value = res.data;
+  }
+});
+
+//头部会员权益
+getEnterpriseMemberEquityList({}).then((res) => {
+  if (res.code == 200) {
+    interestsList.value = res.data;
+  }
+});
+
+//首页三联广告(平台装修-楼层广告-首页三联广告)
+getHomeThreeAdList({}).then((res) => {
+  if (res.code == 200) {
+    AdList.value = res.data;
+  }
+});
+
+//热门方案标题
+getHotSchemeTitle({}).then((res) => {
+  if (res.code == 200) {
+    hotTitle.value = res.data;
+  }
+});
+
+//热门方案列表
+getHotSchemeList({}).then((res) => {
+  if (res.code == 200) {
+    hotList.value = res.data;
+  }
+});
+
+//场景采购标题
+getScenePurchaseTitle({}).then((res) => {
+  if (res.code == 200) {
+    sceneTitle.value = res.data;
+  }
+});
+
+//场景采购列表
+getScenePurchaseList({}).then((res) => {
+  if (res.code == 200) {
+    sceneList.value = res.data;
+  }
+});
+
+//大牌推荐标题(平台闪购标题)
+getPlatformFlashSaleTitle({}).then((res) => {
+  if (res.code == 200) {
+    bigbrandTitle.value = res.data;
+  }
+});
+
+//大牌推荐列表(平台闪购列表)
+getPlatformFlashSaleList({}).then((res) => {
+  if (res.code == 200) {
+    if (res.data.length > 0) {
+      bigbrandOne.value = res.data[0];
+      // bigbrandList.value = res.data.slice(1);
+    }
+  }
+});
+
+//大牌推荐列表(平台闪购列表)-右
+getPlatformFlashSaleBrand({}).then((res) => {
+  if (res.code == 200) {
+    if (res.data.length > 0) {
+      bigbrandList.value = res.data;
+    }
+  }
+});
+
+//行家精选标题
+getExpertSelectionTitle({}).then((res) => {
+  if (res.code == 200) {
+    expertTitle.value = res.data;
+  }
+});
+
+//行家精选列表
+getExpertSelectionList({}).then((res) => {
+  if (res.code == 200) {
+    expertList.value = res.data;
+  }
+});
+
+//采购指南标题
+getPurchaseGuideTitle({}).then((res) => {
+  if (res.code == 200) {
+    procureTitle.value = res.data;
+  }
+});
+
+//采购指南列表
+getProcurementTopicsList({}).then((res) => {
+  if (res.code == 200) {
+    procureList.value = res.data;
+    console.log(res.data);
+  }
+});
+
+//项目案例标题(平台装修-项目案例)
+getProjectCaseTitle({}).then((res) => {
+  if (res.code == 200) {
+    projectTitle.value = res.data;
+  }
+});
+
+//项目案例列表(平台装修-项目案例)
+getProjectCaseList({}).then((res) => {
+  if (res.code == 200) {
+    projectList.value = res.data;
+  }
+});
+
+//外面循环
+getClassificationFloorList({}).then(async (res) => {
+  if (res.code == 200) {
+    for (const item of res.data) {
+      try {
+        const datas1 = await getClassificationFloorDetail(item.floorNo);
+        if (datas1.code == 200) {
+          if (datas1.data.length > 0) {
+            item.imgOne = datas1.data[0];
+            if (datas1.data.length > 1) {
+              item.home1List = datas1.data.slice(1, 4);
+            }
+            if (datas1.data.length > 4) {
+              item.home2List = datas1.data.slice(4, 6);
+            }
+            if (datas1.data.length > 6) {
+              item.infoTwo = datas1.data[6];
+            }
+          }
+        }
+
+        const datas2 = await getClassificationFloorLabel(item.floorNo);
+        if (datas2.code == 200) {
+          item.navList = datas2.data;
+        }
+
+        const datas3 = await getClassificationFloorDetail2(item.floorNo);
+        if (datas2.code == 200) {
+          item.shopList = datas3.data;
+        }
+      } catch (error) {}
+    }
+
+    homeList.value = res.data;
+
+    console.log(homeList.value);
+  }
+});
+
+const onInfo = (res: any) => {
+  router.push('/shop/info?id=' + res.id);
+};
+import { useUserStore } from '@/store/modules/user';
+const onlogout = () => {
+  useUserStore()
+    .logout()
+    .then(() => {
+      onPath('/login');
+    });
+};
+</script>
+
+<style lang="scss" scoped>
+.home-pages {
+  width: 1200px;
+  margin: 0 auto;
+
+  // 头部
+  .home-head {
+    width: 1200px;
+    position: relative;
+    display: flex;
+    gap: 0px 10px;
+    margin-top: 10px;
+
+    .classify {
+      width: 234px;
+      height: 540px;
+      background: #ffffff;
+
+      &.classify-show {
+        position: absolute;
+        z-index: 10;
+        height: auto;
+        min-height: 540px;
+      }
+
+      .classify-list {
+        width: 100%;
+        height: 40px;
+        cursor: pointer;
+        display: flex;
+        align-items: center;
+        padding-left: 15px;
+        position: relative;
+
+        &.classify-hig {
+          border: 1px solid var(--el-color-primary);
+          border-right: 0px solid var(--el-color-primary);
+        }
+
+        .label {
+          max-width: 100px;
+          font-weight: 600;
+          font-size: 14px;
+          color: #101828;
+          white-space: nowrap;
+          margin-right: 10px;
+
+          &:hover {
+            color: var(--el-color-primary);
+          }
+        }
+
+        .info {
+          max-width: 50px;
+          font-size: 12px;
+          color: #364153;
+          white-space: nowrap;
+
+          &.info1 {
+            margin-right: 6px;
+          }
+
+          &:hover {
+            color: var(--el-color-primary);
+          }
+        }
+
+        .classify-border {
+          position: absolute;
+          right: -1px;
+          top: 0px;
+          width: 1px;
+          height: 38px;
+          background-color: #ffffff;
+          z-index: 2;
+        }
+      }
+    }
+
+    .classify-bos {
+      position: absolute;
+      top: 0;
+      right: 0;
+      width: 966px;
+      height: 100%;
+      border: 1px solid var(--el-color-primary);
+      background-color: #ffffff;
+      overflow-y: auto;
+      padding-left: 30px;
+      z-index: 10;
+
+      .classify-item {
+        display: flex;
+        padding-top: 10px;
+        border-bottom: 1px solid #e5e7eb;
+
+        .two-level {
+          width: 90px;
+          font-size: 14px;
+          color: var(--el-color-primary);
+          cursor: pointer;
+        }
+
+        .classify-icon {
+          margin: 4px 15px 0 15px;
+        }
+
+        .classify-label {
+          display: flex;
+          flex-wrap: wrap;
+          flex: 1;
+          font-size: 14px;
+          color: #364153;
+
+          div {
+            margin-right: 20px;
+            margin-bottom: 10px;
+            cursor: pointer;
+
+            &:hover {
+              color: var(--el-color-primary);
+            }
+          }
+        }
+      }
+    }
+
+    // 头部中间
+    .head-bos {
+      width: 756px;
+
+      .carousel {
+        width: 756px;
+        height: 407px;
+        background: #ffffff;
+
+        img {
+          width: 756px;
+          height: 407px;
+        }
+      }
+
+      .head-box {
+        width: 756px;
+        height: 122px;
+        margin-top: 10px;
+        display: flex;
+        gap: 0 10px;
+
+        .head-item {
+          flex: 1;
+          background-color: #ffffff;
+          height: 122px;
+          display: flex;
+          // padding-left: 20px;
+          align-items: center;
+          cursor: pointer;
+          border-radius: 5px;
+          overflow: hidden;
+
+          img {
+            width: 100%;
+            height: 100%;
+            // width: 84px;
+            // height: 84px;
+            // margin-right: 10px;
+            // border-radius: 10px;
+          }
+
+          .head-title {
+            .head1 {
+              font-weight: 550;
+              font-size: 22px;
+              color: #101828;
+            }
+
+            .head2 {
+              width: 76px;
+              height: 21px;
+              border-radius: 34px;
+              border: 1px solid #000000;
+              font-size: 12px;
+              color: #000000;
+              margin-top: 6px;
+            }
+          }
+        }
+      }
+    }
+
+    //右边
+    .head-right {
+      flex: 1;
+      width: 0;
+      height: 540px;
+      background: #ffffff;
+      display: flex;
+      flex-direction: column;
+
+      .login-bos {
+        width: calc(100% - 20px);
+        height: 110px;
+        border-bottom: 1px solid #e5e7eb;
+        margin: 0 10px;
+        display: flex;
+        flex-direction: column;
+        justify-content: space-between;
+        padding: 16px 0;
+
+        .login-box {
+          display: flex;
+          align-items: center;
+
+          img {
+            width: 40px;
+            height: 40px;
+            margin-right: 8px;
+            border-radius: 40px;
+          }
+
+          .login-btn {
+            width: 100%;
+          }
+
+          .login1 {
+            font-size: 13px;
+            color: #444444;
+          }
+
+          .login2 {
+            margin-top: 2px;
+            font-size: 12px;
+            color: #6a7282;
+          }
+        }
+      }
+
+      .real-time {
+        width: calc(100% - 20px);
+        height: 227px;
+        border-bottom: 1px solid #e5e7eb;
+        margin: 0 10px;
+        padding-top: 15px;
+
+        .real-title {
+          position: relative;
+          margin-bottom: 12px;
+
+          &::after {
+            content: '';
+            top: 3px;
+            left: 0;
+            position: absolute;
+            width: 4px;
+            height: 14px;
+            background: var(--el-color-primary);
+          }
+
+          .real1 {
+            font-weight: 600;
+            font-size: 14px;
+            color: #1d2129;
+            padding-left: 15px;
+          }
+
+          .real2 {
+            font-size: 13px;
+            color: #83899f;
+            cursor: pointer;
+          }
+        }
+
+        .real-list {
+          width: 100%;
+          font-size: 14px;
+          color: #1d2129;
+          margin-bottom: 12px;
+          cursor: pointer;
+
+          &:hover {
+            color: var(--el-color-primary);
+          }
+        }
+      }
+
+      .interests {
+        flex: 1;
+        width: calc(100% - 20px);
+        margin: 0 10px;
+        padding-top: 15px;
+
+        .interests-title {
+          position: relative;
+          font-weight: 600;
+          font-size: 14px;
+          color: #1d2129;
+          padding-left: 15px;
+
+          &::after {
+            content: '';
+            top: 3px;
+            left: 0;
+            position: absolute;
+            width: 4px;
+            height: 14px;
+            background: var(--el-color-primary);
+          }
+        }
+
+        .interests-bos {
+          display: flex;
+          flex-wrap: wrap;
+
+          .interests-item {
+            width: 33.333%;
+            font-size: 12px;
+            color: #101828;
+            margin-top: 15px;
+            cursor: pointer;
+
+            &:hover {
+              color: var(--el-color-primary);
+            }
+
+            img {
+              width: 34px;
+              height: 34px;
+              margin-bottom: 7px;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  //标题
+  .home-title {
+    width: 1200px;
+    height: 56px;
+    background: #ffffff;
+    margin-top: 15px;
+    padding: 0 20px;
+
+    .title1 {
+      font-weight: 600;
+      font-size: 20px;
+      color: #101828;
+    }
+
+    .title2 {
+      font-size: 14px;
+      color: #364153;
+      margin-left: 10px;
+    }
+
+    .title-more {
+      font-size: 14px;
+      color: #364153;
+      cursor: pointer;
+
+      &:hover {
+        color: var(--el-color-primary);
+      }
+    }
+  }
+
+  //热门方案
+  .hot-bos {
+    height: 176px;
+    width: 1200px;
+    display: flex;
+    gap: 0 18px;
+    margin-top: 12px;
+
+    .hot-list {
+      flex: 1;
+      height: 176px;
+      background: #ffffff;
+      border-radius: 10px 10px 10px 10px;
+      cursor: pointer;
+
+      .hot1 {
+        font-weight: 600;
+        font-size: 16px;
+        color: #101828;
+      }
+
+      .hot2 {
+        font-size: 14px;
+        color: #364153;
+        margin: 2px 0 14px 0;
+      }
+
+      img {
+        width: 80px;
+        height: 80px;
+      }
+    }
+  }
+
+  // 场景采购
+  .scene-bos {
+    height: 304px;
+    width: 1200px;
+    display: flex;
+    gap: 0 18px;
+    margin-top: 12px;
+
+    .scene-list {
+      flex: 1;
+      height: 304px;
+      background: #ffffff;
+      position: relative;
+      cursor: pointer;
+
+      .scene-box {
+        padding-top: 35px;
+        position: absolute;
+        z-index: 2;
+        width: 100%;
+
+        .scene1 {
+          font-weight: 600;
+          font-size: 30px;
+          color: #ffffff;
+          text-align: center;
+          width: 100%;
+        }
+
+        .scene2 {
+          font-size: 17px;
+          color: #ffffff;
+          text-align: center;
+          width: 100%;
+          margin-top: 4px;
+        }
+      }
+
+      img {
+        position: absolute;
+        width: 100%;
+        height: 100%;
+        top: 0;
+        left: 0;
+        background-color: #f4f4f4;
+      }
+    }
+  }
+
+  // 大牌推荐
+  .big-brand {
+    height: 334px;
+    width: 1200px;
+    margin-top: 12px;
+    display: flex;
+
+    .bigBrand-one {
+      width: 233px;
+      height: 334px;
+      margin-right: 10px;
+    }
+
+    .bigBrand-bos {
+      flex: 1;
+      width: 0;
+      display: flex;
+      flex-wrap: wrap;
+      gap: 10px;
+
+      .bigBrand-list {
+        width: 183.4px;
+        height: 162px;
+        background: #ffffff;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        padding: 32px 20px 0 20px;
+
+        img {
+          width: 152px;
+          height: 29px;
+        }
+
+        .bigBrand1 {
+          font-weight: 600;
+          font-size: 14px;
+          color: #101828;
+          margin: 10px 0 4px 0;
+        }
+
+        .bigBrand2 {
+          font-weight: 400;
+          font-size: 12px;
+          color: #364153;
+          display: -webkit-box;
+          -webkit-line-clamp: 2;
+          line-clamp: 2;
+          /* 添加标准属性 */
+          -webkit-box-orient: vertical;
+          overflow: hidden;
+          text-overflow: ellipsis;
+        }
+      }
+    }
+  }
+
+  // 行家精选
+  .expert-bos {
+    width: 1200px;
+    display: flex;
+    margin-top: 12px;
+
+    .expert-list {
+      width: 230px;
+      height: 306px;
+      background: #ffffff;
+      margin-left: 12.5px;
+      padding: 20px;
+      cursor: pointer;
+
+      &:first-child {
+        margin-left: 0;
+      }
+
+      img {
+        width: 100%;
+        height: 190px;
+      }
+
+      .itemName {
+        font-weight: 400;
+        font-size: 14px;
+        color: #101828;
+        margin: 27px 0 2px 0;
+      }
+
+      .price {
+        .memberPrice {
+          color: var(--el-color-primary);
+          font-size: 16px;
+          color: #e7000b;
+        }
+
+        .marketPrice {
+          font-size: 12px;
+          color: #99a1af;
+          line-height: 20px;
+          text-decoration-line: line-through;
+          text-transform: none;
+          margin-left: 6px;
+        }
+      }
+    }
+  }
+
+  // 采购指南
+  .procure-bos {
+    display: flex;
+    width: 1200px;
+    margin-top: 12px;
+    gap: 0 15px;
+
+    .procure-list {
+      width: 390px;
+      height: 268px;
+      background: #ffffff;
+      cursor: pointer;
+
+      img {
+        width: 390px;
+        height: 200px;
+      }
+
+      .procure1 {
+        padding: 12px 0 4px 20px;
+        font-weight: 600;
+        font-size: 14px;
+        color: #101828;
+      }
+
+      .procure2 {
+        padding-left: 20px;
+        font-size: 12px;
+        color: #364153;
+      }
+    }
+  }
+
+  //循环-商品
+  .goods-bos {
+    height: 340px;
+    width: 1200px;
+    margin-top: 14px;
+    display: flex;
+
+    .goods-img {
+      width: 230px;
+      height: 340px;
+      cursor: pointer;
+    }
+
+    .home1-bos {
+      width: 476px;
+      height: 340px;
+      background: #ffffff;
+      margin-left: 10px;
+      padding: 18px 20px 0 20px;
+      display: flex;
+      flex-direction: column;
+
+      .floorName {
+        font-weight: 600;
+        font-size: 16px;
+        color: #101828;
+      }
+
+      .home1-list {
+        flex: 1;
+        display: flex;
+        border-bottom: 1px solid #e5e7eb;
+        align-items: center;
+        cursor: pointer;
+
+        .advertiseName {
+          font-weight: 600;
+          font-size: 14px;
+          color: #101828;
+        }
+
+        .advertiseDescribe {
+          font-weight: 400;
+          font-size: 12px;
+          color: #364153;
+          margin-top: 4px;
+        }
+
+        img {
+          width: 72px;
+          height: 72px;
+          margin-right: 10px;
+        }
+
+        &:last-child {
+          border-bottom: none;
+        }
+      }
+    }
+
+    .home2-bos {
+      width: 474px;
+      margin-left: 10px;
+      height: 340px;
+      background: #ffffff;
+      padding: 18px 20px 0 20px;
+      display: flex;
+      justify-content: space-between;
+
+      .floorName {
+        font-weight: 600;
+        font-size: 16px;
+        color: #101828;
+      }
+
+      .home2-box {
+        width: 249px;
+
+        .home2-list {
+          width: 249px;
+          height: 130px;
+          border: 1px solid #e5e7eb;
+          margin-top: 13px;
+          display: flex;
+          align-items: center;
+          padding: 0 10px;
+          cursor: pointer;
+
+          .advertiseName {
+            font-weight: 600;
+            font-size: 14px;
+            color: #101828;
+          }
+
+          .advertiseDescribe {
+            font-size: 12px;
+            color: #364153;
+            margin: 4px 0 10px 0;
+          }
+
+          .bnt {
+            width: 68px;
+            height: 24px;
+          }
+
+          img {
+            width: 72px;
+            height: 72px;
+            margin-left: 10px;
+          }
+        }
+      }
+
+      .home2-two {
+        width: 177px;
+        height: 273px;
+        border-radius: 0px 0px 0px 0px;
+        border: 1px solid #e5e7eb;
+        margin-top: 34px;
+        padding: 26px 10px;
+        cursor: pointer;
+
+        .advertiseName {
+          font-weight: 600;
+          font-size: 14px;
+          color: #101828;
+        }
+
+        .advertiseDescribe {
+          font-size: 12px;
+          color: #364153;
+          margin: 4px 0 30px 0;
+          display: -webkit-box;
+          -webkit-line-clamp: 2;
+          line-clamp: 2;
+          /* 添加标准属性 */
+          -webkit-box-orient: vertical;
+          overflow: hidden;
+          text-overflow: ellipsis;
+        }
+
+        img {
+          width: 123px;
+          height: 122px;
+          margin: 0 17px;
+        }
+      }
+    }
+  }
+
+  .shop-bos {
+    display: flex;
+    margin-top: 14px;
+
+    .shop-nav {
+      width: 230px;
+      height: 306px;
+      background: #ffffff;
+      padding: 16px;
+
+      .nav-title {
+        font-weight: 600;
+        font-size: 16px;
+        color: #101828;
+        margin-bottom: 10px;
+      }
+
+      .nav-bos {
+        width: 100%;
+        display: flex;
+        flex-wrap: wrap;
+        justify-content: space-between;
+
+        .nav-list {
+          width: 94px;
+          height: 32px;
+          background: #f4f4f4;
+          border-radius: 4px 4px 4px 4px;
+          font-size: 14px;
+          color: #101828;
+          margin-bottom: 10px;
+          cursor: pointer;
+
+          &:hover {
+            color: var(--el-color-primary);
+          }
+
+          &.hig {
+            background: var(--el-color-primary);
+            color: #ffffff;
+          }
+        }
+      }
+    }
+
+    .shop-list {
+      width: 230px;
+      height: 306px;
+      background: #ffffff;
+      margin-left: 12.5px;
+      padding: 20px;
+      cursor: pointer;
+
+      img {
+        width: 100%;
+        height: 190px;
+      }
+
+      .itemName {
+        font-weight: 400;
+        font-size: 14px;
+        color: #101828;
+        margin: 27px 0 2px 0;
+      }
+
+      .price {
+        .memberPrice {
+          color: var(--el-color-primary);
+          font-size: 16px;
+          color: #e7000b;
+        }
+
+        .marketPrice {
+          font-size: 12px;
+          color: #99a1af;
+          line-height: 20px;
+          text-decoration-line: line-through;
+          text-transform: none;
+          margin-left: 6px;
+        }
+      }
+    }
+  }
+
+  //项目案例
+  .project-bos {
+    display: flex;
+    width: 1200px;
+    margin-top: 12px;
+    gap: 0 15px;
+
+    .project-list {
+      width: 390px;
+      height: 350px;
+      cursor: pointer;
+      border-radius: 10px;
+      overflow: hidden;
+
+      .project-box {
+        width: 390px;
+        height: 156px;
+        background: #ffffff;
+        border-radius: 10px;
+        margin-top: -20px;
+        z-index: 2;
+        position: absolute;
+        padding: 20px;
+      }
+
+      img {
+        width: 390px;
+        height: 180px;
+      }
+
+      .project1 {
+        font-weight: 600;
+        font-size: 16px;
+        color: #101828;
+        margin-bottom: 14px;
+      }
+
+      .project2 {
+        font-size: 14px;
+        color: #364153;
+        display: -webkit-box;
+        -webkit-line-clamp: 2;
+        line-clamp: 2;
+        /* 添加标准属性 */
+        -webkit-box-orient: vertical;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+
+      .project-more {
+        text-align: right;
+        font-size: 14px;
+        color: var(--el-color-primary);
+        margin-top: 18px;
+      }
+    }
+  }
+}
+</style>

+ 18 - 9
src/views/home/index.vue

@@ -192,27 +192,27 @@
       </template>
     </div>
     <!-- 采购指南 -->
-    <!-- <div class="home-title flex-row-between">
+    <div class="home-title flex-row-between">
       <div>
         <span class="title1">{{ procureTitle.title }}</span>
         <span class="title2">{{ procureTitle.subtitle }}</span>
       </div>
-      <div class="title-more flex-row-start" @click="onPath(hotTitle.linkUrl ? hotTitle.linkUrl : '/plan/guide', 2)">
+      <div class="title-more flex-row-start" @click="onPath(hotTitle.linkUrl ? hotTitle.linkUrl : '/plan/guide')">
         <div style="margin-right: 5px">{{ procureTitle.linkWord }}</div>
         <el-icon :size="13" color="#83899F">
           <ArrowRight />
         </el-icon>
       </div>
-    </div> -->
-    <!-- <div class="procure-bos">
-      <template v-for="(item, index) in expertList" :key="index">
+    </div>
+    <div class="procure-bos">
+      <template v-for="(item, index) in procureList" :key="index">
         <div class="procure-list" v-if="Number(index) < 3">
-          <img :src="item.productImage" alt="" />
-          <div class="procure1">{{ item.itemName || '办公室' }}</div>
-          <div class="procure2">{{ item.itemName || '干款好礼·百大品牌·个性定制' }}</div>
+          <img :src="item.coverImage" alt="" />
+          <div class="procure1">{{ item.title || '' }}</div>
+          <div class="procure2">{{ item.subtitle || '' }}</div>
         </div>
       </template>
-    </div> -->
+    </div>
     <!-- 循环-商品 -->
     <div v-for="(item1, index1) in homeList" :key="index1">
       <!-- 头部 -->
@@ -341,6 +341,7 @@ import {
   getPlatformFlashSaleList,
   getPlatformFlashSaleBrand,
   getExpertSelectionTitle,
+  getProcurementTopicsList,
   getPurchaseGuideTitle,
   getExpertSelectionList,
   getClassificationFloorList,
@@ -520,6 +521,14 @@ getPurchaseGuideTitle({}).then((res) => {
   }
 });
 
+//采购指南列表
+getProcurementTopicsList({}).then((res) => {
+  if (res.code == 200) {
+    procureList.value = res.data;
+    console.log(res.data);
+  }
+});
+
 //项目案例标题(平台装修-项目案例)
 getProjectCaseTitle({}).then((res) => {
   if (res.code == 200) {

+ 28 - 1
src/views/login.vue

@@ -60,6 +60,20 @@
         </el-form>
       </div>
     </div>
+    <div class="login-foot flex-column-between">
+      <div class="font-bos flex-row-center">
+        <div>客户管理</div>
+        <div style="margin: 0 10px">|</div>
+        <div>供应商合作</div>
+        <div style="margin: 0 10px">|</div>
+        <div>关于我们</div>
+        <div style="margin: 0 10px">|</div>
+        <div>帮助中心</div>
+        <div style="margin: 0 10px">|</div>
+        <div>联系我们</div>
+      </div>
+      <div class="font-box">CopyRight @ 优易365 2026</div>
+    </div>
   </div>
 </template>
 
@@ -138,7 +152,7 @@ const handleLogin = () => {
       const [err] = await to(userStore.login(loginForm.value));
       if (!err) {
         const redirectUrl = redirect.value || '/';
-        await router.push(redirectUrl);
+        onPath(redirectUrl);
         loading.value = false;
       } else {
         loading.value = false;
@@ -317,5 +331,18 @@ onMounted(() => {
       }
     }
   }
+  .font-bos {
+    width: 100%;
+    font-size: 13px;
+    color: #999999;
+    margin-top: 30px;
+  }
+  .font-box {
+    width: 100%;
+    font-size: 13px;
+    color: #999999;
+    margin-top: 20px;
+    text-align: center;
+  }
 }
 </style>

+ 1 - 1
src/views/plan/guide.vue

@@ -194,7 +194,7 @@ const getList = () => {
     if (res.code == 200) {
       dataList.value = res.rows;
       // 判断是否还有更多数据
-      hasMore.value = dataList.value.length === httpObj.value.pageSize;
+      hasMore.value = res.total > httpObj.value.pageSize * httpObj.value.pageNum;
     }
   });
 };

+ 148 - 49
src/views/plan/index.vue

@@ -3,14 +3,26 @@
     <div class="solve-head">
       <div class="head-bos">
         <div class="nav-bos flex-row-start">
-          <div v-for="(item, index) in navList" :key="index" class="nav-list" :class="navIndex == index ? 'hig' : ''">
-            {{ item.title }}
+          <div
+            @click="onNav(item)"
+            v-for="(item, index) in navList"
+            :key="index"
+            class="nav-list"
+            :class="item.id == httpObj.tweetsCategory ? 'hig' : ''"
+          >
+            {{ item.categoryName }}
           </div>
         </div>
         <div class="filter-bos">
           <div v-for="(item1, index1) in filterListy" :key="index1" class="filter-list flex-row-start">
             <div class="filter-title">{{ item1.title }}</div>
-            <div v-for="(item2, index2) in item1.list" :key="index2" class="filter-item" :class="item1.hig == item2.id ? 'hig' : ''">
+            <div
+              @click="onFilter(item1, item2)"
+              v-for="(item2, index2) in item1.list"
+              :key="index2"
+              class="filter-item"
+              :class="item1.id == item2.id ? 'hig' : ''"
+            >
               {{ item2.title }}
             </div>
           </div>
@@ -19,12 +31,12 @@
     </div>
     <!-- 数据 -->
     <div class="data-bos">
-      <div v-for="(item, index) in 9" :key="index" class="data-list" @click="onPath">
-        <img class="data-img" src="@/assets/images/login-background.jpg" alt="" />
+      <div v-for="(item, index) in dataList" :key="index" class="data-list" @click="onPath('/plan_info?id=' + item.id)">
+        <img class="data-img" :src="item.coverImage" alt="" />
         <div class="data-box flex-column-between">
           <div>
-            <div class="title ellipsis">2025中秋福利 企业团购方案</div>
-            <div class="info ellipsis">干款好礼·百大品牌·个性定制</div>
+            <div class="title ellipsis">{{ item.tweetsTitle }}</div>
+            <div class="info ellipsis">{{ item.programDescribe }}</div>
           </div>
           <div class="text flex-row-start">
             <div>了解详情</div>
@@ -35,82 +47,169 @@
         </div>
       </div>
     </div>
-    <!-- 分页 -->
+    <!-- 游标分页控制 -->
     <div class="pagination-bos flex-row-between">
       <div></div>
-      <el-pagination
-        v-model:current-page="currentPage1"
-        v-model:page-size="pageSize2"
-        layout="sizes, prev, pager, next ,jumper"
-        :total="1000"
-        @size-change="handleSizeChange"
-        @current-change="handleCurrentChange"
+      <pagination
+        v-show="dataList.length > 0"
+        v-model:page="httpObj.pageNum"
+        v-model:limit="httpObj.pageSize"
+        v-model:way="way"
+        :cursor-mode="true"
+        :has-more="hasMore"
+        @pagination="getList"
       />
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
-const currentPage1 = ref(5);
-const pageSize2 = ref(100);
+import { onPath } from '@/utils/siteConfig';
+import {
+  getProcurementProgramList,
+  getPurchaseCategoryList,
+  getAdaptSceneList,
+  getCustomerIndustry,
+  getPriceRangeList,
+  getCustomerTag
+} from '@/api/plan/index';
+import Pagination from '@/components/Pagination/index.vue';
+const httpObj = ref<any>({
+  tweetsCategory: '',
+  adaptNo: '',
+  adaptIndustry: '',
+  price: '',
+  lable: '',
+  pageSize: 10,
+  pageNum: 1
+});
+const dataList = ref<any>([]);
+const hasMore = ref(true); // 是否还有更多数据
+const way = ref<any>(1);
+const navList = ref<any>([]);
 
-const navList = ref<any>([
-  { title: '专题分类' },
-  { title: '大中型企业采购' },
-  { title: '政府&公共采购' },
-  { title: '营销福利' },
-  { title: '商用工程' },
-  { title: '中小型企业采购' }
-]);
 const filterListy = ref<any>([
   {
     title: '适配场景',
-    hig: 1,
+    key: 'adaptNo',
+    id: '',
     list: [
-      { title: '全部', id: 1 },
-      { title: '1-100', id: 2 },
-      { title: '100-500', id: 3 },
-      { title: '1000+', id: 4 }
+      {
+        id: '',
+        title: '全部'
+      }
     ]
   },
   {
     title: '适配行业',
-    hig: 1,
+    key: 'adaptIndustry',
+    id: '',
     list: [
-      { title: '全部', id: 1 },
-      { title: '食品饮料', id: 2 },
-      { title: '食品饮料', id: 3 },
-      { title: '食品饮料', id: 4 }
+      {
+        id: '',
+        title: '全部'
+      }
     ]
   },
   {
     title: '价格区间',
-    hig: 1,
+    key: 'price',
+    id: '',
     list: [
-      { title: '全部', id: 1 },
-      { title: '香港小熊', id: 2 }
+      {
+        id: '',
+        title: '全部'
+      }
     ]
   },
   {
     title: '推荐标签',
-    hig: 1,
+    key: 'lable',
+    id: '',
     list: [
-      { title: '全部', id: 1 },
-      { title: '香港小熊', id: 2 }
+      {
+        id: '',
+        title: '全部'
+      }
     ]
   }
 ]);
-const navIndex = ref(0);
-const router = useRouter();
-const handleSizeChange = (val: number) => {
-  console.log(`${val} items per page`);
+
+onMounted(() => {
+  // 列表
+  getPurchaseCategoryList({}).then((res) => {
+    if (res.code == 200) {
+      res.data.unshift({
+        id: '',
+        categoryName: '全部'
+      });
+      navList.value = res.data;
+      httpObj.value.tweetsCategory = '';
+    }
+  });
+
+  // 适配场景列表
+  getAdaptSceneList({}).then((res) => {
+    if (res.code == 200) {
+      res.data.forEach((item: any) => {
+        item.title = item.sceneName;
+        filterListy.value[0].list.push(item);
+      });
+    }
+  });
+
+  //适配行业
+  getCustomerIndustry({}).then((res) => {
+    if (res.code == 200) {
+      res.data.forEach((item: any) => {
+        item.title = item.industryCategoryName;
+        filterListy.value[1].list.push(item);
+      });
+    }
+  });
+
+  // 获取价格区间列表
+  getPriceRangeList({}).then((res) => {
+    if (res.code == 200) {
+      res.data.forEach((item: any) => {
+        item.title = item.minPrice + '-' + item.maxPrice;
+        item.id = item.minPrice + '-' + item.maxPrice;
+        filterListy.value[2].list.push(item);
+      });
+    }
+  });
+
+  // 推荐标签
+  getCustomerTag({}).then((res) => {
+    if (res.code == 200) {
+      res.data.forEach((item: any) => {
+        item.title = item.tagName;
+        filterListy.value[3].list.push(item);
+      });
+    }
+  });
+
+  getList();
+});
+const getList = () => {
+  getProcurementProgramList(httpObj.value).then((res) => {
+    if (res.code == 200) {
+      dataList.value = res.rows;
+      // 判断是否还有更多数据
+      hasMore.value = res.total > httpObj.value.pageSize * httpObj.value.pageNum;
+    }
+  });
 };
-const handleCurrentChange = (val: number) => {
-  console.log(`current page: ${val}`);
+
+const onNav = (item: any) => {
+  httpObj.value.tweetsCategory = item.id;
+  getList();
 };
 
-const onPath = () => {
-  router.push('/solve/info');
+const onFilter = (item1: any, item2: any) => {
+  item1.id = item2.id;
+  httpObj.value[item1.key] = item2.id;
+  getList();
 };
 </script>
 

+ 161 - 44
src/views/plan/procure.vue

@@ -29,24 +29,40 @@
         </div>
       </div>
     </div>
-    <!-- 数据 -->
-    <div class="data-bos">
-      <div v-for="(item, index) in dataList" :key="index" class="data-list" @click="onPath('/plan_info/procure?id=' + item.id)">
-        <img class="data-img" :src="item.coverImage" alt="" />
-        <div class="data-box flex-column-between">
-          <div>
-            <div class="title ellipsis">{{ item.tweetsTitle }}</div>
-            <div class="info ellipsis">{{ item.programDescribe }}</div>
-          </div>
-          <div class="text flex-row-start">
-            <div>了解详情</div>
-            <el-icon color="#e7000b" size="14" style="margin: 0 0 0 10px">
-              <ArrowRight />
-            </el-icon>
+    <div class="data-all">
+      <!-- 数据 -->
+      <div class="data-bos">
+        <div v-for="(item, index) in dataList" :key="index" class="data-list" @click="onPath('/plan_info/procure?id=' + item.id)">
+          <img class="data-img" :src="item.coverImage" alt="" />
+          <div class="data-box">
+            <div class="title ellipsis">{{ item.title }}</div>
+            <div class="info ellipsis2">
+              {{ item.subtilte }}
+            </div>
+            <div class="time">2026-03-12</div>
           </div>
         </div>
       </div>
+      <el-affix :offset="0">
+        <div class="related-bos">
+          <div class="related-box">
+            <div class="flex-row-between related-title">
+              <div>历史浏览商品</div>
+            </div>
+            <div class="procure-bos">
+              <div v-for="(item, index) in goodsList" :key="index" class="procure-list" @click="onPath('/item?id=' + item.id)">
+                <img :src="item.productImage" alt="" />
+                <div>
+                  <div class="procure1">{{ item.itemName }}</div>
+                  <div class="procure2">¥{{ item.memberPrice }}</div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </el-affix>
     </div>
+
     <!-- 游标分页控制 -->
     <div class="pagination-bos flex-row-between">
       <div></div>
@@ -66,13 +82,14 @@
 <script setup lang="ts">
 import { onPath } from '@/utils/siteConfig';
 import {
-  getProcurementProgramList,
+  getProductProgramPage,
   getPurchaseCategoryList,
   getAdaptSceneList,
   getCustomerIndustry,
   getPriceRangeList,
   getCustomerTag
 } from '@/api/plan/index';
+import { browsingHistoryList } from '@/api/goods/index';
 import Pagination from '@/components/Pagination/index.vue';
 const httpObj = ref<any>({
   tweetsCategory: '',
@@ -87,6 +104,7 @@ const dataList = ref<any>([]);
 const hasMore = ref(true); // 是否还有更多数据
 const way = ref<any>(1);
 const navList = ref<any>([]);
+const goodsList = ref<any>([]);
 
 const filterListy = ref<any>([
   {
@@ -189,14 +207,22 @@ onMounted(() => {
     }
   });
 
+  // 历史
+  browsingHistoryList({}).then((res) => {
+    if (res.code == 200) {
+      goodsList.value = getLatest5Items(res.data);
+      console.log(goodsList.value);
+    }
+  });
+
   getList();
 });
 const getList = () => {
-  getProcurementProgramList(httpObj.value).then((res) => {
+  getProductProgramPage(httpObj.value).then((res) => {
     if (res.code == 200) {
       dataList.value = res.rows;
       // 判断是否还有更多数据
-      hasMore.value = dataList.value.length === httpObj.value.pageSize;
+      hasMore.value = res.total > httpObj.value.pageSize * httpObj.value.pageNum;
     }
   });
 };
@@ -211,12 +237,41 @@ const onFilter = (item1: any, item2: any) => {
   httpObj.value[item1.key] = item2.id;
   getList();
 };
-const handleSizeChange = (val: number) => {
-  console.log(`${val} items per page`);
-};
-const handleCurrentChange = (val: number) => {
-  console.log(`current page: ${val}`);
-};
+/**
+ * 获取最新日期的 5 个商品
+ * 逻辑:先按日期降序,然后依次从每个日期的列表中取商品,凑满 5 个即停止
+ */
+function getLatest5Items(data: any): any[] {
+  if (!data) return [];
+
+  // 1. 获取所有日期并按时间倒序排序 (最新的在前面)
+  const sortedDates = Object.keys(data).sort((a, b) => {
+    return new Date(b).getTime() - new Date(a).getTime();
+  });
+
+  const result: any[] = [];
+  const seenIds = new Set(); // 用于去重,防止不同日期出现相同商品
+
+  // 2. 遍历排序后的日期
+  for (const date of sortedDates) {
+    if (result.length >= 5) break; // 如果已经凑满 5 个,直接跳出循环
+
+    const items = data[date] || [];
+
+    for (const item of items) {
+      if (result.length >= 5) break; // 再次检查,防止单次循环多取
+
+      // 可选:如果同一个商品在不同日期出现,只取一次(推荐)
+      const itemId = String(item.id);
+      if (seenIds.has(itemId)) continue;
+
+      result.push(item);
+      seenIds.add(itemId);
+    }
+  }
+
+  return result;
+}
 </script>
 
 <style lang="scss" scoped>
@@ -283,49 +338,111 @@ const handleCurrentChange = (val: number) => {
       }
     }
   }
+  .data-all {
+    display: flex;
+    width: 1200px;
+    margin: 22px auto 0 auto;
+    gap: 0 15px;
+    .related-bos {
+      flex: 1;
+      .related-box {
+        width: 100%;
+        background-color: #ffffff;
+        border-radius: 10px;
+        padding: 20px;
+        .related-title {
+          font-size: 16px;
+          color: #666666;
+          .related-huan {
+            font-size: 14px;
+            cursor: pointer;
+          }
+        }
+        // 采购指南
+        .procure-bos {
+          width: 100%;
+          display: flex;
+          flex-direction: column;
+          margin-top: 12px;
+          gap: 12px 0px;
+          .procure-list {
+            width: 100%;
+            height: 130px;
+            background: #ffffff;
+            cursor: pointer;
+            border-radius: 10px;
+            overflow: hidden;
+            border: 1px solid #d0d5dd;
+            padding: 15px;
+            display: flex;
+            img {
+              width: 100px;
+              height: 100px;
+              border-radius: 5px;
+            }
+            .procure1 {
+              padding: 12px 0 10px 20px;
+              font-weight: 600;
+              font-size: 14px;
+              color: #101828;
+            }
+            .procure2 {
+              padding-left: 20px;
+              color: var(--el-color-primary);
+              font-size: 16px;
+              color: #e7000b;
+            }
+          }
+        }
+      }
+    }
+  }
 
   // 数据
   .data-bos {
-    width: 1200px;
-    margin: 0 auto;
-    display: flex;
-    gap: 20px;
-    flex-wrap: wrap;
-    padding: 22px 0 40px 0;
+    width: 778px;
+    padding: 0px 0 40px 0;
 
     .data-list {
-      width: 386px;
-      height: 302px;
+      width: 778px;
+      height: 200px;
       background: #ffffff;
       border-radius: 10px;
       overflow: hidden;
       cursor: pointer;
+      margin-bottom: 20px;
+      padding: 15px;
+      display: flex;
+      gap: 0 15px;
 
       .data-img {
-        height: 200px;
-        width: 386px;
+        height: 170px;
+        width: 220px;
+        border-radius: 10px;
       }
 
       .data-box {
-        height: 102px;
-        width: 386px;
-        padding: 12px 20px;
+        height: 170px;
+        flex: 1;
+        padding: 10px 20px;
+        width: 0;
 
         .title {
           font-weight: 600;
-          font-size: 14px;
-          color: #101828;
+          font-size: 18px;
+          color: #333333;
         }
 
         .info {
-          font-size: 12px;
-          color: #364153;
-          margin-top: 4px;
+          font-size: 14px;
+          color: #666666;
+          margin-top: 10px;
         }
 
-        .text {
-          font-size: 14px;
-          color: #e7000b;
+        .time {
+          font-size: 12px;
+          color: #666666;
+          margin-top: 10px;
         }
       }
     }

+ 55 - 28
src/views/plan_info/guide.vue

@@ -4,52 +4,60 @@
       <div class="real-info">
         <div class="title">{{ dataInfo.title }}</div>
         <div class="time">{{ dataInfo.releaseTime }}</div>
-        <div class="real-html" v-html="dataInfo.content"></div>
+        <div v-if="dataInfo.content" class="real-html" v-html="dataInfo.content"></div>
       </div>
     </div>
-    <div class="related-bos">
-      <div class="related-box">
-        <div class="flex-row-between related-title">
-          <div>相关专题</div>
-          <div class="flex-row-start related-huan" @click="getList">
-            <el-icon><Sort /></el-icon>
-            <div style="margin-left: 4px">换一换</div>
+    <el-affix :offset="0">
+      <div class="related-bos">
+        <div class="related-box">
+          <div class="flex-row-between related-title">
+            <div>相关专题</div>
+            <div class="flex-row-start related-huan" @click="getList">
+              <el-icon><Sort /></el-icon>
+              <div style="margin-left: 4px">换一换</div>
+            </div>
           </div>
-        </div>
-        <div class="procure-bos">
-          <div v-for="(item, index) in dataList" :key="index" class="procure-list" @click="onPath('/plan_info/guide?id=' + item.id)">
-            <img :src="item.coverImage" alt="" />
-            <div class="procure1">{{ item.title }}</div>
-            <div class="procure2">{{ item.releaseTime }}</div>
+          <div class="procure-bos">
+            <div v-for="(item, index) in dataList" :key="index" class="procure-list" @click="onPath('/plan_info/guide?id=' + item.id)">
+              <img :src="item.coverImage" alt="" />
+              <div class="procure1">{{ item.title }}</div>
+              <div class="procure2">{{ item.releaseTime }}</div>
+            </div>
           </div>
         </div>
       </div>
-    </div>
+    </el-affix>
   </div>
 </template>
 
 <script setup lang="ts">
-import { onPath } from '@/utils/siteConfig';
+import { onMounted, ref } from 'vue';
+import { useRoute, onBeforeRouteUpdate } from 'vue-router';
 import { getPurchaseGuideDetail, getPurchaseGuideList } from '@/api/plan/index';
+import { onPath } from '@/utils/siteConfig';
+
+const route = useRoute();
 const id = ref<any>(null);
 const dataInfo = ref<any>({});
 const dataList = ref<any>([]);
-const route = useRoute();
-
-onMounted(() => {
-  id.value = route.query.id;
-  getInfo();
-  getList();
-});
 
+// 封装获取详情的逻辑
 const getInfo = () => {
+  // 防御性编程:如果 id 为空,可以不请求或清空数据
+  if (!id.value) return;
+
+  // 可选:添加 loading 状态或清空旧数据以避免视觉残留
+  // dataInfo.value = {};
+
   getPurchaseGuideDetail(id.value).then((res) => {
     if (res.code == 200) {
+      console.log('详情数据已更新,ID:', id.value);
       dataInfo.value = res.data;
     }
   });
 };
 
+// 封装获取列表的逻辑
 const getList = () => {
   getPurchaseGuideList({
     pageSize: 10,
@@ -57,9 +65,9 @@ const getList = () => {
   }).then((res) => {
     if (res.code == 200) {
       if (res.rows && res.rows.length > 0) {
-        if (res.rows.length == 1 || res.rows.length == 2) {
+        if (res.rows.length <= 2) {
           dataList.value = res.rows;
-        } else if (res.rows.length > 2) {
+        } else {
           dataList.value = getRandomElements(res.rows, 3);
         }
       }
@@ -67,19 +75,38 @@ const getList = () => {
   });
 };
 
+// 核心修复:统一初始化函数
+const initData = () => {
+  // 1. 【关键步骤】先从路由获取最新的 ID
+  id.value = route.query.id;
+  console.log('当前路由 ID 已更新为:', id.value);
+
+  // 2. 再请求数据
+  getInfo();
+  getList();
+};
+
+onMounted(() => {
+  initData();
+});
+
+// 修复后的路由更新监听
+onBeforeRouteUpdate((to, from, next) => {
+  // 执行初始化逻辑(包含更新 id 和请求数据)
+  initData();
+  next();
+});
+
 function getRandomElements(arr: any, count: any) {
   if (count > arr.length) {
     throw new Error('要取的数量不能超过数组长度');
   }
-
   const copy = [...arr];
   const result = [];
-
   for (let i = 0; i < count; i++) {
     const randomIndex = Math.floor(Math.random() * copy.length);
     result.push(copy.splice(randomIndex, 1)[0]);
   }
-
   return result;
 }
 </script>

+ 185 - 126
src/views/plan_info/index.vue

@@ -1,112 +1,220 @@
 <template>
   <div class="solve-page">
     <div class="solve-bos">
-      <img class="solve-img" src="@/assets/images/login-background.jpg" alt="" />
-      <div class="title">香港小熊食品 甜蜜褔利 品质之选</div>
-      <div class="info">节日专题:节日、节日等主题盒子;可定制企业及福语、传递节日贺卡 员工关怀:办公室下午茶分享</div>
-      <div class="filter-bos">
-        <div v-for="(item1, index1) in filterListy" :key="index1" class="filter-list flex-row-start">
-          <div class="filter-title">{{ item1.title }}</div>
-          <div v-for="(item2, index2) in item1.list" :key="index2" class="filter-item" :class="item1.hig == item2.id ? 'hig' : ''">
-            {{ item2.title }}
-          </div>
-        </div>
-      </div>
-      <div class="nav-bos flex-row-start">
-        <div v-for="(item, index) in navList" :key="index" class="nav-list" :class="navIndex == index ? 'hig' : ''">
-          {{ item.title }}
+      <img class="solve-img" :src="dataInfo.coverImage" alt="" />
+      <div v-for="(item, index) in dataList" :key="index">
+        <!-- 标题 -->
+        <div class="solve-title">
+          <div class="title1">{{ item.title }}</div>
+          <div class="title2">{{ item.subtitle }}</div>
         </div>
-      </div>
-      <!-- 数据 -->
-      <div class="data-bos">
-        <div v-for="(item, index) in 13" :key="index" class="data-list" @click="onPath">
-          <img class="data-img" src="@/assets/images/login-background.jpg" alt="" />
-          <div class="data-title">格力KFR-72LW/定频冷暖空调柜机3P</div>
-          <div class="money">
-            <span class="money1">¥1,299</span>
-            <span class="money2">¥1,899</span>
+        <!-- 数据 -->
+        <div class="data-bos">
+          <div class="data-box">
+            <div v-for="(item1, index1) in item.list" :key="index1" class="data-list" @click="onPath('/item?id=' + item1.id)">
+              <img class="data-img" :src="item1.productImage" alt="" />
+              <div class="data-title ellipsis">{{ item1.itemName }}</div>
+              <div class="money">
+                <span class="money1">¥{{ item1.memberPrice }}</span>
+                <span class="money2">¥{{ item1.marketPrice }}</span>
+              </div>
+              <div class="data-cat" @click.stop="onCart(item1)">加入购物车</div>
+            </div>
           </div>
-          <div class="data-cat">加入购物车</div>
+          <!-- 游标分页控制 -->
+          <pagination
+            v-show="item.list.length > 0"
+            v-model:page="item.pageNum"
+            v-model:limit="item.pageSize"
+            :cursor-mode="true"
+            :has-more="item.hasMore"
+            @pagination="getList(item)"
+          />
         </div>
       </div>
-      <!-- 分页 -->
-      <div class="pagination-bos flex-row-between">
-        <div></div>
-        <el-pagination
-          v-model:current-page="currentPage1"
-          v-model:page-size="pageSize2"
-          layout="sizes, prev, pager, next ,jumper"
-          :total="1000"
-          @size-change="handleSizeChange"
-          @current-change="handleCurrentChange"
-        />
-      </div>
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
-const currentPage1 = ref(5);
-const pageSize2 = ref(100);
-const navIndex = ref(0);
-const router = useRouter();
-const navList = ref<any>([{ title: '智能匹配' }, { title: '人气排序' }, { title: '价格排序' }]);
-const filterListy = ref<any>([
-  {
-    title: '价格区间',
-    hig: 1,
-    list: [
-      { title: '全部', id: 1 },
-      { title: '1-100', id: 2 },
-      { title: '100-500', id: 3 },
-      { title: '1000+', id: 4 }
-    ]
-  },
-  {
-    title: '产品分类',
-    hig: 1,
-    list: [
-      { title: '全部', id: 1 },
-      { title: '食品饮料', id: 2 },
-      { title: '食品饮料', id: 3 },
-      { title: '食品饮料', id: 4 }
-    ]
-  },
-  {
-    title: '产品品牌',
-    hig: 1,
-    list: [
-      { title: '全部', id: 1 },
-      { title: '香港小熊', id: 2 }
-    ]
+import { onPath } from '@/utils/siteConfig';
+import { getProcurementProgramDetail, getProcurementProgramGroupList, getProcurementProgramGroupProductList } from '@/api/plan/index';
+import { addProductShoppingCart } from '@/api/goods/index';
+const id = ref<any>(null);
+const dataInfo = ref<any>({});
+const dataList = ref<any>({});
+const route = useRoute();
+
+onMounted(() => {
+  id.value = route.query.id;
+  getInfo();
+});
+
+const getInfo = async () => {
+  try {
+    // 获取采购计划详情
+    const detailRes = await getProcurementProgramDetail(id.value);
+    if (detailRes.code === 200) {
+      dataInfo.value = detailRes.data;
+    }
+
+    // 获取采购计划分组列表
+    const groupRes = await getProcurementProgramGroupList(id.value);
+    if (groupRes.code === 200) {
+      // 使用 Promise.all 等待所有 onGoods 异步操作完成
+      const updatedData = await Promise.all(
+        groupRes.data.map(async (item: any) => {
+          item.pageSize = 10;
+          item.pageNum = 1;
+          item.hasMore = false;
+          const productList: any = await onGoods(item); // 等待每个分组的商品列表加载完成
+          item.hasMore = productList.total > item.pageSize * item.pageNum;
+          return {
+            ...item,
+            list: productList.rows // 将商品列表赋值给 item.List
+          };
+        })
+      );
+      dataList.value = updatedData; // 更新 dataList
+      console.log(dataList.value);
+    }
+  } catch (error) {
+    console.error('获取数据失败:', error);
   }
-]);
+};
 
-const handleSizeChange = (val: number) => {
-  console.log(`${val} items per page`);
+// 修改 onGoods 返回 Promise
+const onGoods = async (item: any) => {
+  try {
+    const res = await getProcurementProgramGroupProductList({ groupId: item.id, pageSize: item.pageSize, pageNum: item.pageNum });
+    if (res.code === 200) {
+      return res; // 返回商品列表
+    }
+    return []; // 如果请求失败,返回空数组
+  } catch (error) {
+    console.error('获取商品列表失败:', error);
+    return []; // 异常情况下也返回空数组
+  }
 };
-const handleCurrentChange = (val: number) => {
-  console.log(`current page: ${val}`);
+
+const getList = (row: any) => {
+  getProcurementProgramGroupProductList({ groupId: row.id, pageSize: row.pageSize, pageNum: row.pageNum }).then((res) => {
+    if (res.code == 200) {
+      row.list = res.rows;
+      // 判断是否还有更多数据
+      row.hasMore = res.total > row.pageSize * row.pageNum;
+    }
+  });
 };
 
-const onPath = () => {
-  router.push('/shop/info');
+import { cartStore } from '@/store/modules/cart';
+const cart = cartStore();
+//加入购物车
+const onCart = (row: any) => {
+  addProductShoppingCart({
+    productId: row.id,
+    productNum: 1
+  }).then((res) => {
+    if (res.code == 200) {
+      cart.onCartCount();
+      ElMessage.success('加入购物车成功');
+    }
+  });
 };
 </script>
 
 <style lang="scss" scoped>
 .solve-page {
   width: 100%;
-  background-color: #ffffff;
 
   .solve-bos {
     width: 1200px;
     margin: 0 auto;
+    padding-bottom: 30px;
     .solve-img {
       width: 1200px;
       height: 380px;
       border-radius: 10px;
+      margin-top: 20px;
+    }
+
+    .solve-title {
+      width: 1200px;
+      padding: 10px 20px;
+      background-color: #ffffff;
+      border-radius: 10px;
+      margin-top: 20px;
+      .title1 {
+        font-size: 24px;
+        font-weight: bold;
+        margin-bottom: 20px;
+      }
+      .title2 {
+        background-color: #f2f2f2;
+        padding: 17px 20px;
+        font-size: 14px;
+        color: #666666;
+        border-radius: 10px;
+      }
+    }
+
+    //数据
+    .data-bos {
+      background-color: #ffffff;
+      padding: 20px 20px;
+      margin-top: 20px;
+      border-radius: 10px;
+      .data-box {
+        width: 100%;
+        display: flex;
+        flex-wrap: wrap;
+        gap: 20px;
+        .data-list {
+          flex: 0 0 calc((100% - 80px) / 5);
+          width: 0;
+          background: #f4f4f4;
+          border-radius: 10px;
+          padding: 20px 20px 22px 20px;
+          cursor: pointer;
+          .data-img {
+            width: 184px;
+            height: 184px;
+            border-radius: 10px;
+          }
+          .data-title {
+            margin-top: 4px;
+            font-size: 14px;
+            color: #101828;
+            height: 40px;
+          }
+          .money {
+            margin-top: 4px;
+            .money1 {
+              font-size: 16px;
+              color: #e7000b;
+            }
+            .money2 {
+              font-size: 12px;
+              color: #99a1af;
+              text-decoration: line-through;
+              padding-left: 6px;
+            }
+          }
+          .data-cat {
+            width: 86px;
+            height: 26px;
+            background: #e7000b;
+            border-radius: 2px;
+            font-size: 14px;
+            color: #ffffff;
+            line-height: 26px;
+            text-align: center;
+            margin-top: 16px;
+          }
+        }
+      }
     }
+
     .title {
       font-weight: 600;
       font-size: 20px;
@@ -169,55 +277,6 @@ const onPath = () => {
         }
       }
     }
-    //数据
-    .data-bos {
-      display: flex;
-      gap: 20px;
-      flex-wrap: wrap;
-      margin-bottom: 40px;
-      .data-list {
-        width: 224px;
-        background: #f4f4f4;
-        border-radius: 10px;
-        padding: 20px 20px 22px 20px;
-        cursor: pointer;
-        .data-img {
-          width: 184px;
-          height: 184px;
-          border-radius: 10px;
-        }
-        .data-title {
-          margin-top: 4px;
-          font-size: 14px;
-          color: #101828;
-          height: 40px;
-        }
-        .money {
-          margin-top: 4px;
-          .money1 {
-            font-size: 16px;
-            color: #e7000b;
-          }
-          .money2 {
-            font-size: 12px;
-            color: #99a1af;
-            text-decoration: line-through;
-            padding-left: 6px;
-          }
-        }
-        .data-cat {
-          width: 86px;
-          height: 26px;
-          background: #e7000b;
-          border-radius: 2px;
-          font-size: 14px;
-          color: #ffffff;
-          line-height: 26px;
-          text-align: center;
-          margin-top: 16px;
-        }
-      }
-    }
   }
 }
 </style>

+ 21 - 20
vite.config.ts

@@ -21,33 +21,34 @@ export default defineConfig(({ mode, command }) => {
     server: {
       host: '0.0.0.0',
       allowedHosts: [
-        'www.yingpai365.com',
-        'b.yingpai365.com',
-        'mro.yingpai365.com',
-        'fuli.yingpai365.com',
-        'reg.yingpai365.com',
-        'breg.yingpai365.com',
-        'greg.yingpai365.com',
-        'passport.yingpai365.com',
-        'search.yingpai365.com',
-        'item.yingpai365.com',
-        'cart.yingpai365.com',
-        'trad.yingpai365.com',
-        'payc.yingpai365.com',
-        'order.yingpai365.com',
-        'plan.yingpai365.com',
-        'plan_info.yingpai365.com',
-        'i.yingpai365.com',
-        'easybuv.yingpai365.com'
+        'www.xiaoluwebsite.xyz',
+        'b.xiaoluwebsite.xyz',
+        'mro.xiaoluwebsite.xyz',
+        'fuli.xiaoluwebsite.xyz',
+        'reg.xiaoluwebsite.xyz',
+        'breg.xiaoluwebsite.xyz',
+        'greg.xiaoluwebsite.xyz',
+        'passport.xiaoluwebsite.xyz',
+        'search.xiaoluwebsite.xyz',
+        'item.xiaoluwebsite.xyz',
+        'cart.xiaoluwebsite.xyz',
+        'trad.xiaoluwebsite.xyz',
+        'payc.xiaoluwebsite.xyz',
+        'order.xiaoluwebsite.xyz',
+        'plan.xiaoluwebsite.xyz',
+        'plan_info.xiaoluwebsite.xyz',
+        'i.xiaoluwebsite.xyz',
+        'easybuv.xiaoluwebsite.xyz'
       ],
       port: Number(env.VITE_APP_PORT),
       // port: Number(env.VITE_APP_PORT),
       open: true,
       proxy: {
         [env.VITE_APP_BASE_API]: {
-          target: 'http://localhost:8080',
+          // target: 'http://localhost:8080',
           // target: 'http://yp1.yingpaipay.com:9026',
-          // target: 'https://jingyang.xiaoluwebsite.xyz',
+          target: 'https://jingyang.xiaoluwebsite.xyz',
+          // target: 'https://one.yoe365.com',
           // target: 'http://192.168.1.52:8080',
           changeOrigin: true,
           ws: true,

+ 2 - 0
vite/plugins/index.ts

@@ -8,6 +8,7 @@ import createIcons from './icons';
 import createSvgIconsPlugin from './svg-icon';
 import createCompression from './compression';
 import createSetupExtend from './setup-extend';
+import basicSsl from '@vitejs/plugin-basic-ssl';
 import path from 'path';
 
 export default (viteEnv: any, isBuild = false): [] => {
@@ -21,5 +22,6 @@ export default (viteEnv: any, isBuild = false): [] => {
   vitePlugins.push(createIcons());
   vitePlugins.push(createSvgIconsPlugin(path));
   vitePlugins.push(createSetupExtend());
+  vitePlugins.push(basicSsl());
   return vitePlugins;
 };

Some files were not shown because too many files changed in this diff