소스 검색

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

hurx 1 개월 전
부모
커밋
f94349fbaf

+ 1 - 1
.env.development

@@ -36,4 +36,4 @@ VITE_APP_WEBSOCKET = false
 VITE_APP_SSE = true
 
 # 是否开启多域名
-VITE_DOMAIN_NAME = false
+VITE_DOMAIN_NAME = true

+ 1 - 1
.env.production

@@ -39,4 +39,4 @@ VITE_APP_WEBSOCKET = false
 VITE_APP_SSE = true
 
 # 是否开启多域名
-VITE_DOMAIN_NAME = false
+VITE_DOMAIN_NAME = true

+ 3 - 1
.vscode/settings.json

@@ -1,3 +1,5 @@
 {
-  "i18n-ally.localesPaths": ["src/lang"]
+  "i18n-ally.localesPaths": [
+    "src/lang"
+  ]
 }

+ 9 - 0
src/api/home/index.ts

@@ -208,3 +208,12 @@ export function getProductCategoryList(query: any) {
     params: query
   });
 }
+
+//用户订单
+export function countOrder(query: any) {
+  return request({
+    url: '/order/pcOrder/countOrder',
+    method: 'get',
+    params: query
+  });
+}

+ 27 - 0
src/api/plan/index.ts

@@ -43,6 +43,23 @@ export function getProductProgramPage(query: any) {
   });
 }
 
+// 场景采购详情
+export function getProductProgramDetail(id: any) {
+  return request({
+    url: '/product/indexProduct/getProductProgramDetail/' + id,
+    method: 'get'
+  });
+}
+
+
+// 场景采购详情的商品
+export function getProductProgramProductList(id: any) {
+  return request({
+    url: '/product/indexProduct/getProductProgramProductList/' + id,
+    method: 'get'
+  });
+}
+
 // 采购方案列表
 export function getProcurementProgramList(query: any) {
   return request({
@@ -143,3 +160,13 @@ export function getYouYzXunInfo(id: any) {
     method: 'get'
   });
 }
+
+// 品牌详情
+
+export function getProductBrandDetail(id: any) {
+  return request({
+    url: '/product/indexProduct/getProductBrandDetail/' + id,
+    method: 'get'
+  });
+}
+

+ 47 - 4
src/layout/components/header.vue

@@ -7,11 +7,26 @@
         <div>武汉</div>
       </div>
       <div class="header-box flex-row-start">
-        <div v-if="!isLoggedIn" class="header-text" @click="goToLogin" style="cursor: pointer">请登录</div>
-        <div v-if="!isLoggedIn" class="header-text hig">免费注册</div>
-        <div class="header-text" @click="goToMyOrder">我的订单</div>
+        <el-dropdown v-if="userInfo.nickName">
+          <div class="dropdow header-text hig">{{ userInfo.nickName }}</div>
+          <template #dropdown>
+            <el-dropdown-menu>
+              <el-dropdown-item>
+                <span>姓名:{{ userInfo.nickName }}</span>
+              </el-dropdown-item>
+              <el-dropdown-item>
+                <span>部门:</span>
+                <span class="hig">中国南方电网有</span>
+              </el-dropdown-item>
+              <el-dropdown-item divided @click="onlogout">退出登录</el-dropdown-item>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
+        <div v-if="!userInfo.nickName" class="header-text" @click="onPath('/login')" style="cursor: pointer">请登录</div>
+        <div v-if="!userInfo.nickName" class="header-text hig" @click="onPath('/breg')">免费注册</div>
+        <div class="header-text" @click="onPath('/order/orderManage')">我的订单</div>
         <div class="header-text" @click="onPath('/enterprise/companyInfo')">会员中心</div>
-        <div class="header-text">人才招聘</div>
+        <div class="header-text" @click="onPath('/theme?id=1')">人才招聘</div>
         <div class="header-text">帮助中心</div>
         <div class="header-text end">在线客服</div>
       </div>
@@ -24,6 +39,21 @@ import { useRouter } from 'vue-router';
 import { useUserStore } from '@/store/modules/user';
 import { computed } from 'vue';
 import { onPath } from '@/utils/siteConfig';
+import Cookies from 'js-cookie';
+import { getInfo } from '@/api/login';
+const userInfo = ref<any>({});
+
+onMounted(() => {
+  const token = Cookies.get('Authorization');
+  if (token) {
+    getInfo().then((res) => {
+      if (res.code == 200) {
+        userInfo.value = res.data.user;
+      }
+    });
+  }
+});
+
 const router = useRouter();
 const userStore = useUserStore();
 
@@ -37,6 +67,14 @@ const goToLogin = () => {
 const goToMyOrder = () => {
   onPath('/order/orderManage');
 };
+
+const onlogout = () => {
+  useUserStore()
+    .logout()
+    .then(() => {
+      onPath('/login');
+    });
+};
 </script>
 
 <style lang="scss" scoped>
@@ -65,6 +103,7 @@ const goToMyOrder = () => {
         padding-right: 14px;
         margin-right: 14px;
         position: relative;
+        cursor: pointer;
         &::after {
           content: '';
           position: absolute;
@@ -85,6 +124,10 @@ const goToMyOrder = () => {
           color: #e7000b;
         }
       }
+
+      .dropdow {
+        margin-top: 2px;
+      }
     }
   }
 }

+ 143 - 1
src/layout/components/nav.vue

@@ -2,13 +2,42 @@
   <!-- 导航组件 -->
   <div class="nav-pages">
     <div class="nav-bos">
-      <div class="nav-all flex-row-start">
+      <div class="nav-all flex-row-start" @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>
@@ -124,6 +153,119 @@ const leaveClassify = () => {
         color: #e7000b;
       }
     }
+
+    // 分类
+    .nav-classify {
+      position: absolute;
+      height: 540px;
+      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>

+ 1 - 1
src/layout/index.vue

@@ -38,7 +38,7 @@ watch(route, () => {
 
     &.pages-bos1 {
       width: 1200px;
-      margin: 0 auto;
+      margin: 20px auto 0 auto;
     }
 
     &.pages-bos2 {

+ 7 - 4
src/permission.ts

@@ -8,6 +8,7 @@ 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';
+import Cookies from 'js-cookie';
 
 NProgress.configure({ showSpinner: false });
 const whiteList = [
@@ -26,7 +27,8 @@ const whiteList = [
   '/item',
   '/breg',
   '/greg',
-  '/diy'
+  '/diy',
+  '/theme'
 ];
 
 const isWhiteList = (path: string) => {
@@ -49,15 +51,16 @@ router.beforeEach(async (to, from, next) => {
   NProgress.start();
   const site = getCurrentSite();
   const allowedPaths = SITE_ROUTES[site];
+  const token = Cookies.get('Authorization');
 
   //不需要请求登录信息
-  if (isWhiteList(to.path)) {
+  if (isWhiteList(to.path) && !token) {
     next();
     NProgress.done();
   } else {
     const [err] = await tos(useUserStore().getInfo());
-    // if (err) {
-    if (false) {
+    if (err) {
+      // if (false) {
       await useUserStore().logout();
       ElMessage.error(err);
       if (import.meta.env.VITE_DOMAIN_NAME == 'true') {

+ 6 - 0
src/router/index.ts

@@ -92,6 +92,12 @@ export const constantRoutes: RouteRecordRaw[] = [
         name: 'IndexDiy',
         meta: { title: 'Diy商城', icon: 'dashboard', affix: true, nav: true }
       },
+      {
+        path: '/theme',
+        component: () => import('@/views/home/theme.vue'),
+        name: 'IndexTheme',
+        meta: { title: '详情', icon: 'dashboard', affix: true, nav: true }
+      },
       {
         path: '/indexData',
         component: () => import('@/views/home/index-data.vue'),

+ 31 - 56
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.xiaoluwebsite.xyz${path}`;
+    return `https://passport.xiaoluwebsite.xyz${path}`;
   } else {
     // 本地开发:指向 www.xiaoluwebsite.xyz 加上当前运行的端口
     // 假设你启动 vite 后访问的是 http://www.xiaoluwebsite.xyz
     const devPort = window.location.port || import.meta.env.VITE_APP_PORT;
-    return `https://www.xiaoluwebsite.xyz:${devPort}${path}`;
+    return `https://passport.xiaoluwebsite.xyz:${devPort}${path}`;
   }
 }
 
@@ -57,9 +57,9 @@ service.interceptors.request.use(
     // 是否需要加密
     const isEncrypt = config.headers?.isEncrypt === 'true';
 
-    if (getToken() && !isToken) {
-      config.headers['Authorization'] = 'Bearer ' + getToken(); // 让每个请求携带自定义token 请根据实际情况自行修改
-    }
+    // if (getToken() && !isToken) {
+    //   config.headers['Authorization'] = 'Bearer ' + getToken(); // 让每个请求携带自定义token 请根据实际情况自行修改
+    // }
     // get请求映射params参数
     if (config.method === 'get' && config.params) {
       let url = config.url + '?' + tansParams(config.params);
@@ -141,62 +141,37 @@ service.interceptors.response.use(
     if (code === 401) {
       if (!isRelogin.show) {
         isRelogin.show = true;
-        ElMessageBox.confirm('登录状态已过期,请重新登录', '登录提示', {
-          confirmButtonText: '重新登录',
-          type: 'warning'
-        })
+        // 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;
+        //   });
+        useUserStore()
+          .logout()
           .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;
+            if (import.meta.env.VITE_DOMAIN_NAME == 'true') {
+              window.location.href = getMainSiteUrl('/login');
+            } else {
+              router.push('/login');
+            }
           });
       }
       return Promise.reject('无效的会话,或者会话已过期,请重新登录。');
-
-      // prettier-ignore
-      // if (!isRelogin.show) {
-      //   isRelogin.show = true;
-      //   useUserStore().logout().then(() => {
-      //     window.location.href = getMainSiteUrl('/index');
-      //   });
-
-      // ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
-      //   confirmButtonText: '重新登录',
-      //   cancelButtonText: '取消',
-      //   type: 'warning'
-      // }).then(() => {
-      //   isRelogin.show = false;
-      //   useUserStore().logout().then(() => {
-      //     router.replace({
-      //       path: '/login',
-      //       query: {
-      //         redirect: encodeURIComponent(router.currentRoute.value.fullPath || '/')
-      //       }
-      //     })
-      //   });
-      // }).catch(() => {
-      //   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('无效的会话,或者会话已过期,请重新登录。');
     } else if (code === HttpStatus.SERVER_ERROR) {
       ElMessage({ message: msg, type: 'error' });
       // return Promise.reject(new Error(msg));

+ 3 - 3
src/utils/siteConfig.ts

@@ -1,6 +1,6 @@
 // 每个站点允许的路由 (保持不变)
 export const SITE_ROUTES: Record<any, string[]> = {
-  www: ['/', '/index', '/indexData'], //优易365主站
+  www: ['/', '/index', '/indexData', '/theme'], //优易365主站
   b: ['/indexB'], //企业购商城
   mro: ['/indexMro'], //工业品商城
   fuli: ['/indexFuli'], //福礼商城
@@ -8,14 +8,14 @@ export const SITE_ROUTES: Record<any, string[]> = {
   breg: ['/breg'], //企业注册
   greg: ['/greg'], //供应商注册
   passport: ['/login'], //登录页
-  search: ['/search', '/search/special'], //搜索
+  search: ['/search', '/search/special', '/search/brand'], //搜索
   item: ['/item'], //商品详情,
   cart: ['/cart'], //商品详情
   trad: ['/trad'], //确认订单信息
   payc: ['/payc'], //支付订单
 
   plan: ['/plan', '/plan/procure', '/plan/guide', '/plan/project'], //解决方案
-  plan_info: ['/plan_info', '/plan_info/procure', '/plan_info/guide', '/plan_info/project'], //信息展示
+  plan_info: ['/plan_info', '/plan_info/procure', '/plan_info/guide', '/plan_info/project', '/solve/real'], //信息展示
 
   order: [
     '/order/orderManage',

+ 3 - 3
src/views/home/index-fuli.vue

@@ -165,7 +165,7 @@ import {
   getRecommendGiftFloorList
 } from '@/api/home/index-fuli';
 
-import { getProductCategoryTree } from '@/api/home/index';
+import { getProductCategoryTree, getYouYiZiXunPage } from '@/api/home/index';
 
 onMounted(() => {
   if (getToken()) {
@@ -231,7 +231,7 @@ getIconAdList({}).then((res) => {
 });
 
 // 咨询
-getNoticeList({}).then((res) => {
+getYouYiZiXunPage({}).then((res) => {
   if (res.code == 200) {
     realList.value = res.data;
   }
@@ -285,7 +285,7 @@ getAdvertisementGiftFloorList({}).then(async (res) => {
 //为你推荐
 getRecommendGiftFloorList({}).then((res) => {
   if (res.code == 200) {
-    getGiftFloorLinkProductList({floorId:res.data[0].id}).then((res1) => {
+    getGiftFloorLinkProductList({ floorId: res.data[0].id }).then((res1) => {
       if (res1.code == 200) {
         recommend.value = res1.data;
       }

+ 140 - 30
src/views/home/index.vue

@@ -41,19 +41,45 @@
         <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 class="head-title">
-              <div class="head1">{{ item.title }}</div>
-              <div class="head2 flex-row-center">
-                <div>查看方案</div>
-                <el-icon><ArrowRight /></el-icon>
-              </div>
-            </div> -->
           </div>
         </div>
       </div>
       <!-- 右边 -->
       <div class="head-right">
-        <div class="login-bos">
+        <div v-if="userInfo.userId" class="order-bos flex-column-between">
+          <div class="flex-row-start">
+            <img src="@/assets/images/logo2.png" alt="" />
+            <div class="ellipsis">{{ userInfo.nickName }}</div>
+          </div>
+          <div class="flex-row-start">
+            <div class="flex-1 flex-column-center">
+              <div>待付款</div>
+              <div class="order-num">{{ countData.pendingPaymentCount }}</div>
+            </div>
+            <div class="flex-1 flex-column-center">
+              <div>待发货</div>
+              <div class="order-num">{{ countData.pendingShipmentCount }}</div>
+            </div>
+            <div class="flex-1 flex-column-center">
+              <div>待收货</div>
+              <div class="order-num">{{ countData.pendingReceiptCount }}</div>
+            </div>
+          </div>
+        </div>
+        <div class="login-bos" v-else>
+          <div class="login-box">
+            <img src="@/assets/images/logo2.png" alt="" />
+            <div>
+              <div class="login1">您好,欢迎来到优易达</div>
+              <div class="login2">请先登录</div>
+            </div>
+          </div>
+          <div class="loginBtn-bos flex-row-center">
+            <div @click="onPath('/login')" class="login-bnt1 flex-row-center">登录</div>
+            <div @click="onPath('/breg')" class="login-bnt2 flex-row-center">注册</div>
+          </div>
+        </div>
+        <!-- <div class="login-bos">
           <div class="login-box">
             <img :src="userInfo.avatar ? userInfo.avatar : profile" alt="" />
             <div>
@@ -63,19 +89,18 @@
           </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> -->
         <div class="real-time">
           <div class="real-title flex-row-between">
             <div class="real1">优易资讯</div>
             <div class="real2 flex-row-start">
-              <div>更多</div>
+              <!-- <div>更多</div>
               <el-icon :size="13" color="#83899F">
                 <ArrowRight />
-              </el-icon>
+              </el-icon> -->
             </div>
           </div>
           <template v-for="(item, index) in realList" :key="index">
@@ -145,7 +170,7 @@
         <span class="title1">{{ bigbrandTitle.title }}</span>
         <span class="title2">{{ bigbrandTitle.subtitle }}</span>
       </div>
-      <div class="title-more flex-row-start">
+      <div class="title-more flex-row-start" @click="onPath(hotTitle.linkUrl ? hotTitle.linkUrl : '/search/brand')">
         <div style="margin-right: 5px">{{ bigbrandTitle.linkWord }}</div>
         <el-icon :size="13" color="#83899F">
           <ArrowRight />
@@ -349,8 +374,10 @@ import {
   getClassificationFloorLabel,
   getClassificationFloorDetail2,
   getProjectCaseTitle,
-  getProjectCaseList
+  getProjectCaseList,
+  countOrder
 } from '@/api/home/index';
+import Cookies from 'js-cookie';
 
 const userInfo = ref<any>({});
 const classifyList = ref<any>([]);
@@ -366,16 +393,16 @@ const hotList = ref<any>([]);
 const sceneTitle = ref<any>('');
 const sceneList = ref<any>([]);
 const expertTitle = ref<any>('');
-const expertList = ref<any>([{}, {}, {}, {}, {}]);
+const expertList = ref<any>([]);
 const procureTitle = ref<any>('');
-const procureList = ref<any>([{}, {}, {}, {}, {}]);
+const procureList = ref<any>([]);
 const projectTitle = ref<any>('');
-const projectList = ref<any>([{}, {}, {}, {}, {}]);
+const projectList = ref<any>([]);
 
 const bigbrandTitle = ref<any>('');
 const bigbrandList = ref<any>([]);
 const bigbrandOne = ref<any>({});
-
+const countData = ref<any>({});
 const homeList = ref<any>([]);
 const router = useRouter();
 
@@ -385,10 +412,17 @@ getPlatformIndexDiyPcPage({}).then((res) => {
 });
 
 onMounted(() => {
-  if (getToken()) {
+  const token = Cookies.get('Authorization');
+  if (token) {
     getInfo().then((res) => {
       if (res.code == 200) {
         userInfo.value = res.data.user;
+
+        countOrder({}).then((res1) => {
+          if (res1.code == 200) {
+            countData.value = res1.data;
+          }
+        });
       }
     });
   }
@@ -724,21 +758,21 @@ const onlogout = () => {
 
     // 头部中间
     .head-bos {
-      width: 756px;
+      flex: 1;
 
       .carousel {
-        width: 756px;
+        width: 100%;
         height: 407px;
         background: #ffffff;
 
         img {
-          width: 756px;
+          width: 100%;
           height: 407px;
         }
       }
 
       .head-box {
-        width: 756px;
+        width: 100%;
         height: 122px;
         margin-top: 10px;
         display: flex;
@@ -787,8 +821,7 @@ const onlogout = () => {
 
     //右边
     .head-right {
-      flex: 1;
-      width: 0;
+      width: 230px;
       height: 540px;
       background: #ffffff;
       display: flex;
@@ -815,10 +848,6 @@ const onlogout = () => {
             border-radius: 40px;
           }
 
-          .login-btn {
-            width: 100%;
-          }
-
           .login1 {
             font-size: 13px;
             color: #444444;
@@ -830,6 +859,87 @@ const onlogout = () => {
             color: #6a7282;
           }
         }
+
+        .loginBtn-bos {
+          width: 100%;
+
+          .login-bnt1 {
+            width: 64px;
+            height: 24px;
+            background-color: #e7000b;
+            color: #ffffff;
+            border-radius: 24px;
+            font-size: 12px;
+            cursor: pointer;
+          }
+
+          .login-bnt2 {
+            width: 64px;
+            height: 24px;
+            border: 1px solid #e7000b;
+            color: #e7000b;
+            border-radius: 24px;
+            font-size: 12px;
+            margin-left: 10px;
+            cursor: pointer;
+          }
+        }
+      }
+
+      // .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;
+      //     }
+      //   }
+      // }
+
+      .order-bos {
+        width: calc(100% - 20px);
+        height: 110px;
+        font-size: 14px;
+        padding: 16px 0;
+        border-bottom: 1px solid #e5e7eb;
+        margin: 0 10px;
+        img {
+          width: 28px;
+          height: 28px;
+        }
+        .order-num {
+          color: #e7000b;
+          font-size: 14px;
+          margin-top: 2px;
+        }
       }
 
       .real-time {
@@ -869,7 +979,7 @@ const onlogout = () => {
 
         .real-list {
           width: 100%;
-          font-size: 14px;
+          font-size: 12px;
           color: #1d2129;
           margin-bottom: 12px;
           cursor: pointer;

+ 59 - 0
src/views/home/theme.vue

@@ -0,0 +1,59 @@
+<template>
+  <div class="theme-pages">
+    <div class="content-container" v-html="renderedHtml"></div>
+  </div>
+</template>
+
+<script setup lang="ts">
+const route = useRoute();
+const id = ref<any>(null);
+const base64String = ref<any>('');
+const renderedHtml = ref<any>('');
+
+const initData = () => {
+  id.value = route.query.id;
+  if (id.value == 1) {
+    base64String.value =
+      'PHAgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsiPjxpbWcgc3JjPSJodHRwczovL3l1bnlpbmcueW9lMzY1LmNvbTo4MDQ1L1Bob3Rvcy8xMzM5MTMyODEzNDg5MjQxMDIuanBnIiBhbHQ9IuWbvueJhyIgZGF0YS1ocmVmPSJodHRwczovL3l1bnlpbmcueW9lMzY1LmNvbTo4MDQ1L1Bob3Rvcy8xMzM5MTMyODEzNDg5MjQxMDIuanBnIiBzdHlsZT0iIj48aW1nIHNyYz0iaHR0cHM6Ly95dW55aW5nLnlvZTM2NS5jb206ODA0NS9QaG90b3MvMTMzOTEzMjgxMzUyNDIxNTM1LmpwZyIgYWx0PSLlm77niYciIGRhdGEtaHJlZj0iaHR0cHM6Ly95dW55aW5nLnlvZTM2NS5jb206ODA0NS9QaG90b3MvMTMzOTEzMjgxMzUyNDIxNTM1LmpwZyIgc3R5bGU9IiI+PGltZyBzcmM9Imh0dHBzOi8veXVueWluZy55b2UzNjUuY29tOjgwNDUvUGhvdG9zLzEzMzkxMzI4MTM1Njk1NTYxOS5qcGciIGFsdD0i5Zu+54mHIiBkYXRhLWhyZWY9Imh0dHBzOi8veXVueWluZy55b2UzNjUuY29tOjgwNDUvUGhvdG9zLzEzMzkxMzI4MTM1Njk1NTYxOS5qcGciIHN0eWxlPSIiPjxpbWcgc3JjPSJodHRwczovL3l1bnlpbmcueW9lMzY1LmNvbTo4MDQ1L1Bob3Rvcy8xMzM5MTMyODEzNTk4Nzc2MDMuanBnIiBhbHQ9IuWbvueJhyIgZGF0YS1ocmVmPSJodHRwczovL3l1bnlpbmcueW9lMzY1LmNvbTo4MDQ1L1Bob3Rvcy8xMzM5MTMyODEzNTk4Nzc2MDMuanBnIiBzdHlsZT0iIj48aW1nIHNyYz0iaHR0cHM6Ly95dW55aW5nLnlvZTM2NS5jb206ODA0NS9QaG90b3MvMTMzOTEzMjgxMzYyNzc5Nzc2LmpwZyIgYWx0PSLlm77niYciIGRhdGEtaHJlZj0iaHR0cHM6Ly95dW55aW5nLnlvZTM2NS5jb206ODA0NS9QaG90b3MvMTMzOTEzMjgxMzYyNzc5Nzc2LmpwZyIgc3R5bGU9IiI+PGltZyBzcmM9Imh0dHBzOi8veXVueWluZy55b2UzNjUuY29tOjgwNDUvUGhvdG9zLzEzMzkxMzI4MTM2NTk5MzE1MS5qcGciIGFsdD0i5Zu+54mHIiBkYXRhLWhyZWY9Imh0dHBzOi8veXVueWluZy55b2UzNjUuY29tOjgwNDUvUGhvdG9zLzEzMzkxMzI4MTM2NTk5MzE1MS5qcGciIHN0eWxlPSIiPjxpbWcgc3JjPSJodHRwczovL3l1bnlpbmcueW9lMzY1LmNvbTo4MDQ1L1Bob3Rvcy8xMzM5MTMyODEzNjkyOTkyNzcuanBnIiBhbHQ9IuWbvueJhyIgZGF0YS1ocmVmPSJodHRwczovL3l1bnlpbmcueW9lMzY1LmNvbTo4MDQ1L1Bob3Rvcy8xMzM5MTMyODEzNjkyOTkyNzcuanBnIiBzdHlsZT0iIj48aW1nIHNyYz0iaHR0cHM6Ly95dW55aW5nLnlvZTM2NS5jb206ODA0NS9QaG90b3MvMTMzOTEzMjgxMzczMzM5MDc5LmpwZyIgYWx0PSLlm77niYciIGRhdGEtaHJlZj0iaHR0cHM6Ly95dW55aW5nLnlvZTM2NS5jb206ODA0NS9QaG90b3MvMTMzOTEzMjgxMzczMzM5MDc5LmpwZyIgc3R5bGU9IiI+PC9wPg==';
+  }
+  const htmlString = decodeBase64ToUtf8(base64String.value);
+  renderedHtml.value = htmlString;
+};
+
+// 核心解码函数 (处理 UTF-8 中文不乱码)
+const decodeBase64ToUtf8 = (base64) => {
+  try {
+    // 1. atob 解码为二进制字符串
+    const binaryString = window.atob(base64);
+    // 2. 将二进制字符串转换为 UTF-8
+    const bytes = new Uint8Array(binaryString.length);
+    for (let i = 0; i < binaryString.length; i++) {
+      bytes[i] = binaryString.charCodeAt(i);
+    }
+    // 3. 使用 TextDecoder 解码为正常字符串
+    return new TextDecoder('utf-8').decode(bytes);
+  } catch (e) {
+    console.error('解码失败', e);
+    return '';
+  }
+};
+
+onMounted(() => {
+  initData();
+});
+
+watch(route, () => {
+  initData();
+});
+</script>
+
+<style lang="scss" scoped>
+.theme-pages {
+  width: 100%;
+  .content-container :deep(img) {
+    display: block;
+    margin: 0 auto;
+    width: 100%;
+  }
+}
+</style>

+ 32 - 12
src/views/item/index.vue

@@ -7,6 +7,7 @@
             <img :src="item" alt="" @click="onCarousel(1, index)" />
           </div>
         </div>
+        <div v-else class="left-carousel"></div>
         <div class="left-next flex-row-center" @click="onCarousel(2)">
           <el-icon color="#6A7282" size="15">
             <ArrowDown />
@@ -14,6 +15,7 @@
         </div>
       </div>
       <img v-if="carousel && carousel.length > 0" :src="carousel[carouselIndex]" alt="" class="carousel-img" />
+      <div v-else class="carousel-img"></div>
       <el-affix :offset="0">
         <div class="head-right flex-column-between">
           <div>
@@ -137,20 +139,20 @@
         <div class="more1">更多推荐</div>
         <div class="more2">甄选大牌,优质好品</div>
       </div>
-      <div class="flex-row-start">
+      <!-- <div class="flex-row-start">
         <div class="more3">查看更多</div>
         <el-icon color="#364153" size="14">
           <ArrowRight />
         </el-icon>
-      </div>
+      </div> -->
     </div>
     <div class="data-bos">
-      <div v-for="(item, index) in 3" :key="index" class="data-list">
-        <img class="data-img" src="@/assets/images/login-background.jpg" alt="" />
-        <div class="data-title">格力KFR-72LW/定频冷暖空调柜机3P</div>
+      <div v-for="(item, index) in recommendList" :key="index" class="data-list" @click="onPath('/item?id=' + item.id)">
+        <img class="data-img" :src="item.productImage ? item.productImage.split(',')[0] : ''" alt="" />
+        <div class="data-title">{{ item.itemName || '' }}</div>
         <div class="money">
-          <span class="money1">¥1,299</span>
-          <span class="money2">¥1,899</span>
+          <span class="money1">¥{{ item.memberPrice || '' }}</span>
+          <span class="money2">¥{{ item.marketPrice || '' }}</span>
         </div>
       </div>
     </div>
@@ -183,6 +185,7 @@
 <script setup lang="ts">
 import { getToken } from '@/utils/auth';
 import { regionData } from 'element-china-area-data';
+import { onPath } from '@/utils/siteConfig';
 import {
   getProductDetail,
   addProductShoppingCart,
@@ -193,6 +196,8 @@ import {
   cancelProductCollect
 } from '@/api/goods/index';
 
+import { getRecommendedCategoryProductList } from '@/api/home/index-mro';
+
 const route = useRoute();
 const id = ref<any>(null);
 const dataInfo = ref<any>({});
@@ -204,7 +209,20 @@ const collection = ref<any>(null);
 const dialogVisible = ref<any>(false);
 const navList = ref<any>([{ title: '商品详情' }, { title: '售后服务说明' }, { title: '品牌信息' }, { title: '商品评价' }]);
 const navIndex = ref<any>(0);
+const recommendList = ref<any>([]);
+const num = ref<any>(1);
+const carouselIndex = ref<any>(0);
+
+watch(route, () => {
+  initData();
+});
+
 onMounted(() => {
+  initData();
+});
+
+// 核心修复:统一初始化函数
+const initData = () => {
   id.value = route.query.id;
   getInfo();
 
@@ -213,7 +231,7 @@ onMounted(() => {
     // 浏览记录
     addProductBrowsingHistory(id.value).then((res) => {});
   }
-});
+};
 
 // 商品详情
 const getInfo = () => {
@@ -277,10 +295,6 @@ const onNav = (index: any) => {
   navIndex.value = index;
 };
 
-const num = ref<any>(1);
-
-const carouselIndex = ref<any>(0);
-
 const onCarousel = (type: number, index?: any) => {
   if (type == 1) {
     carouselIndex.value = index;
@@ -308,6 +322,12 @@ const onCart = () => {
     }
   });
 };
+
+getRecommendedCategoryProductList({}).then((res) => {
+  if (res.code == 200) {
+    recommendList.value = res.data;
+  }
+});
 </script>
 <style lang="scss" scoped>
 .shop-pages {

+ 2 - 1
src/views/login.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="login">
-    <img class="head" src="@/assets/images/head.png" alt="" />
+    <img @click="onPath('/')" class="head" src="@/assets/images/head.png" alt="" />
     <div class="login-info flex-row-between">
       <div></div>
       <div class="login-bos">
@@ -226,6 +226,7 @@ onMounted(() => {
     height: 90px;
     margin-top: 53px;
     margin-left: 84px;
+    cursor: pointer;
   }
 
   .login-info {

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

@@ -37,7 +37,7 @@
           <div class="data-box">
             <div class="title ellipsis">{{ item.title }}</div>
             <div class="info ellipsis2">
-              {{ item.subtilte }}
+              {{ item.describe }}
             </div>
             <div class="time">2026-03-12</div>
           </div>

+ 33 - 55
src/views/plan_info/procure.vue

@@ -5,8 +5,11 @@
       <div v-for="(item, index) in dataList" :key="index">
         <!-- 标题 -->
         <div class="solve-title flex-row-between">
-          <div class="title1">{{ item.title }}</div>
-          <div class="title2">{{ item.subtitle }}</div>
+          <div class="title1">{{ item.info.brandName }}</div>
+          <div class="title2 flex-row-start">
+            更多
+            <el-icon><arrow-right /></el-icon>
+          </div>
         </div>
         <!-- 数据 -->
         <div class="data-bos">
@@ -21,16 +24,6 @@
               <div class="data-cat" @click.stop="onCart(item1)">加入购物车</div>
             </div>
           </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>
@@ -39,7 +32,7 @@
 
 <script setup lang="ts">
 import { onPath } from '@/utils/siteConfig';
-import { getProcurementProgramDetail, getProcurementProgramGroupList, getProcurementProgramGroupProductList } from '@/api/plan/index';
+import { getProductProgramDetail, getProductProgramProductList, getProductBrandDetail } from '@/api/plan/index';
 import { addProductShoppingCart } from '@/api/goods/index';
 const id = ref<any>(null);
 const dataInfo = ref<any>({});
@@ -53,61 +46,43 @@ onMounted(() => {
 
 const getInfo = async () => {
   try {
-    // 获取采购计划详情
-    const detailRes = await getProcurementProgramDetail(id.value);
-    if (detailRes.code === 200) {
-      dataInfo.value = detailRes.data;
-    }
+    const detailRes = await getProductProgramDetail(id.value);
+    dataInfo.value = detailRes.data;
+
+    const groupRes = await getProductProgramProductList(id.value);
 
-    // 获取采购计划分组列表
-    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 = await onGoods(item); // 等待每个分组的商品列表加载完成
-          item.hasMore = productList.length === item.pageSize;
-          return {
-            ...item,
-            list: productList // 将商品列表赋值给 item.List
-          };
-        })
-      );
-      dataList.value = updatedData; // 更新 dataList
-      console.log(dataList.value);
+      dataList.value = [];
+      for (const key in groupRes.data) {
+        const datas = {
+          list: groupRes.data[key],
+          info: await onGoods(key)
+        };
+        dataList.value.push(datas);
+      }
     }
+    console.log(dataList.value, '798798798');
   } catch (error) {
-    console.error('获取数据失败:', error);
+    console.error('获取采购计划详情失败:', error);
   }
 };
 
 // 修改 onGoods 返回 Promise
-const onGoods = async (item: any) => {
+const onGoods = async (key: any) => {
   try {
-    const res = await getProcurementProgramGroupProductList({ groupId: item.id, pageSize: item.pageSize, pageNum: item.pageNum });
+    const res = await getProductBrandDetail(key);
     if (res.code === 200) {
-      return res.rows; // 返回商品列表
+      return res.data; // 返回商品列表
     }
-    return []; // 如果请求失败,返回空数组
+    return {}; // 如果请求失败,返回空数组
   } catch (error) {
     console.error('获取商品列表失败:', error);
-    return []; // 异常情况下也返回空数组
+    return {}; // 异常情况下也返回空数组
   }
 };
 
-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.rows.length === row.pageSize;
-    }
-  });
-};
-
+import { cartStore } from '@/store/modules/cart';
+const cart = cartStore();
 //加入购物车
 const onCart = (row: any) => {
   addProductShoppingCart({
@@ -115,6 +90,7 @@ const onCart = (row: any) => {
     productNum: 1
   }).then((res) => {
     if (res.code == 200) {
+      cart.onCartCount();
       ElMessage.success('加入购物车成功');
     }
   });
@@ -138,12 +114,13 @@ const onCart = (row: any) => {
 
     .solve-title {
       width: 1200px;
-      padding: 10px 20px;
+      padding: 15px 20px;
       background-color: #ffffff;
       border-radius: 10px;
       margin-top: 20px;
       .title1 {
         font-size: 16px;
+        font-weight: bold;
       }
       .title2 {
         font-size: 14px;
@@ -155,14 +132,15 @@ const onCart = (row: any) => {
     .data-bos {
       background-color: #ffffff;
       padding: 20px 20px;
-      margin-top: 20px;
+      margin-top: 10px;
       border-radius: 10px;
       .data-box {
         display: flex;
         flex-wrap: wrap;
         gap: 20px;
         .data-list {
-          width: 20%;
+          flex: 0 0 calc((100% - 80px) / 5);
+          width: 0;
           background: #f4f4f4;
           border-radius: 10px;
           padding: 20px 20px 22px 20px;

+ 1 - 1
src/views/search/brand.vue

@@ -33,7 +33,7 @@
       <div class="head-bos head-bos1">
         <div class="head-title">品牌名称:</div>
         <div class="head-box">
-          <el-input @input="getList" v-model="httpObj.name" style="width: 240px" placeholder="请输入品牌名称" />
+          <el-input v-model="httpObj.name" style="width: 240px" placeholder="请输入品牌名称" />
         </div>
       </div>
     </div>

+ 41 - 15
src/views/search/index.vue

@@ -1,14 +1,18 @@
 <template>
   <div class="search-pages">
-    <div v-if="search && type == 1" class="breadcrumb flex-row-center">
+    <div v-if="httpObj.searchKeyword && type == 1" class="breadcrumb flex-row-center">
       <div class="breadcrumb-bos flex-row-start">
         <div class="home" @click="onPath('/index')">首页</div>
         <div class="nav-list">
-          <el-icon style="margin: 0 4px"><ArrowRight /></el-icon>
+          <el-icon style="margin: 0 4px">
+            <ArrowRight />
+          </el-icon>
           <div class="like">搜索结果</div>
         </div>
-        <el-icon style="margin: 0 4px"><ArrowRight /></el-icon>
-        <div>{{ search || '' }}</div>
+        <el-icon style="margin: 0 4px">
+          <ArrowRight />
+        </el-icon>
+        <div>{{ httpObj.searchKeyword || '' }}</div>
       </div>
     </div>
     <div v-else style="height: 15px"></div>
@@ -72,22 +76,34 @@
         <div class="head-sort flex-row-center" @click="onSort(1)" :class="sortField1 != '' ? 'hig' : ''">
           <div>智能匹配</div>
           <div class="sort-box">
-            <el-icon :color="sortField1 == 'Asc' ? '#E7000B' : '#333333'" class="icon1" :size="12"><CaretTop /></el-icon>
-            <el-icon :color="sortField1 == 'Desc' ? '#E7000B' : '#333333'" class="icon2" :size="12"><CaretBottom /></el-icon>
+            <el-icon :color="sortField1 == 'Asc' ? '#E7000B' : '#333333'" class="icon1" :size="12">
+              <CaretTop />
+            </el-icon>
+            <el-icon :color="sortField1 == 'Desc' ? '#E7000B' : '#333333'" class="icon2" :size="12">
+              <CaretBottom />
+            </el-icon>
           </div>
         </div>
         <div class="head-sort flex-row-center" @click="onSort(2)" :class="sortField2 != '' ? 'hig' : ''">
           <div>库存排序</div>
           <div class="sort-box">
-            <el-icon :color="sortField2 == 'Asc' ? '#E7000B' : '#333333'" class="icon1" :size="12"><CaretTop /></el-icon>
-            <el-icon :color="sortField2 == 'Desc' ? '#E7000B' : '#333333'" class="icon2" :size="12"><CaretBottom /></el-icon>
+            <el-icon :color="sortField2 == 'Asc' ? '#E7000B' : '#333333'" class="icon1" :size="12">
+              <CaretTop />
+            </el-icon>
+            <el-icon :color="sortField2 == 'Desc' ? '#E7000B' : '#333333'" class="icon2" :size="12">
+              <CaretBottom />
+            </el-icon>
           </div>
         </div>
         <div class="head-sort flex-row-center" @click="onSort(3)" :class="sortField3 != '' ? 'hig' : ''">
           <div>价格排序</div>
           <div class="sort-box">
-            <el-icon :color="sortField3 == 'Asc' ? '#E7000B' : '#333333'" class="icon1" :size="12"><CaretTop /></el-icon>
-            <el-icon :color="sortField3 == 'Desc' ? '#E7000B' : '#333333'" class="icon2" :size="12"><CaretBottom /></el-icon>
+            <el-icon :color="sortField3 == 'Asc' ? '#E7000B' : '#333333'" class="icon1" :size="12">
+              <CaretTop />
+            </el-icon>
+            <el-icon :color="sortField3 == 'Desc' ? '#E7000B' : '#333333'" class="icon2" :size="12">
+              <CaretBottom />
+            </el-icon>
           </div>
         </div>
         <el-checkbox-group v-model="checkList" style="margin-top: 15px" @change="getList">
@@ -137,8 +153,8 @@ const brandList = ref<any>([]);
 const categoryName = ref<any>('');
 const hasMore = ref(true); // 是否还有更多数据
 const way = ref<any>(1);
-const search = ref<any>('');
 const httpObj = ref<any>({
+  itemName: '',
   topCategoryId: '',
   middleCategoryId: '',
   bottomCategoryId: '',
@@ -312,14 +328,16 @@ const onSort = (type: number) => {
   getList();
 };
 
-onMounted(() => {
+// 核心修复:统一初始化函数
+const initData = () => {
+  httpObj.value.searchKeyword = '';
   httpObj.value.topCategoryId = '';
   type.value = route.query.type;
+  if (route.query.input) {
+    httpObj.value.searchKeyword = route.query.input;
+  }
   if (type.value == 1) {
     getBrand1();
-    if (route.query.input) {
-      search.value = route.query.input;
-    }
     if (route.query.topCategoryId) {
       httpObj.value.topCategoryId = route.query.topCategoryId;
     }
@@ -332,6 +350,14 @@ onMounted(() => {
     httpObj.value.bottomCategoryId = route.query.bottomCategoryId;
   }
   getClassify();
+};
+
+watch(route, () => {
+  initData();
+});
+
+onMounted(() => {
+  initData();
 });
 </script>
 

+ 107 - 2
src/views/solve/real.vue

@@ -7,7 +7,27 @@
         <div class="real-html" v-html="dataInfo.announcementContent"></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 class="flex-row-start related-huan" @click="getList">
+              <el-icon><Sort /></el-icon>
+              <div style="margin-left: 4px">换一换</div>
+            </div>
+          </div>
+          <div class="procure-bos">
+            <div v-for="(item, index) in dataList" :key="index" class="procure-list" @click="onPath('/plan_info?id=' + item.id)">
+              <img :src="item.coverImage" alt="" />
+              <div class="procure1">{{ item.title }}</div>
+              <div class="procure2">{{ item.releaseTime }}</div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </el-affix>
+    <!--
     <div class="real-shop">
       <div class="shop-head flex-row-start">
         <div class="head1">为您推荐</div>
@@ -23,19 +43,22 @@
           </div>
         </div>
       </div>
-    </div>
+    </div> -->
   </div>
 </template>
 
 <script setup lang="ts">
 import { onPath } from '@/utils/siteConfig';
 import { getYouYzXunInfo } from '@/api/plan/index';
+import { getProcurementProgramList } from '@/api/plan/index';
 const id = ref<any>(null);
 const dataInfo = ref<any>({});
 const route = useRoute();
+const dataList = ref<any>([]);
 onMounted(() => {
   id.value = route.query.id;
   getInfo();
+  getList();
 });
 
 const getInfo = () => {
@@ -45,6 +68,37 @@ const getInfo = () => {
     }
   });
 };
+
+// 封装获取列表的逻辑
+const getList = () => {
+  getProcurementProgramList({
+    pageSize: 10,
+    pageNum: 1
+  }).then((res) => {
+    if (res.code == 200) {
+      if (res.rows && res.rows.length > 0) {
+        if (res.rows.length <= 2) {
+          dataList.value = res.rows;
+        } else {
+          dataList.value = getRandomElements(res.rows, 3);
+        }
+      }
+    }
+  });
+};
+
+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>
 
 <style lang="scss" scoped>
@@ -155,5 +209,56 @@ const getInfo = () => {
       }
     }
   }
+
+  .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: 268px;
+          background: #ffffff;
+          cursor: pointer;
+          border-radius: 10px;
+          overflow: hidden;
+          border: 1px solid #d0d5dd;
+          img {
+            width: 100%;
+            height: 200px;
+            border-bottom: 1px solid #d0d5dd;
+          }
+          .procure1 {
+            padding: 12px 0 4px 20px;
+            font-weight: 600;
+            font-size: 14px;
+            color: #101828;
+          }
+          .procure2 {
+            padding-left: 20px;
+            font-size: 12px;
+            color: #364153;
+          }
+        }
+      }
+    }
+  }
 }
 </style>