Commit c9fecebbb4f5b1a82f11ac43f66d5b35740b0f0a

Authored by boyang
1 parent a4b10b4c

feat: 开发热销商品

components/Footer.vue
@@ -5,7 +5,9 @@ @@ -5,7 +5,9 @@
5 <v-col cols="12" lg="3" sm="12" md="6"> 5 <v-col cols="12" lg="3" sm="12" md="6">
6 <b>Solution</b> 6 <b>Solution</b>
7 <p><router-link to="/equipment">Lab Device</router-link></p> 7 <p><router-link to="/equipment">Lab Device</router-link></p>
8 - <p><router-link to="/customize">Customized BatterTesting</router-link></p> 8 + <p>
  9 + <router-link to="/customize">Customized BatterTesting</router-link>
  10 + </p>
9 <p><router-link to="/pack">Pack</router-link></p> 11 <p><router-link to="/pack">Pack</router-link></p>
10 </v-col> 12 </v-col>
11 <v-col cols="12" lg="3" sm="12" md="6"> 13 <v-col cols="12" lg="3" sm="12" md="6">
@@ -16,6 +18,18 @@ @@ -16,6 +18,18 @@
16 <v-col cols="12" lg="3" sm="12" md="6"> 18 <v-col cols="12" lg="3" sm="12" md="6">
17 <b>About</b> 19 <b>About</b>
18 <p><router-link to="/about">About us</router-link></p> 20 <p><router-link to="/about">About us</router-link></p>
  21 + <p>
  22 + <a
  23 + href="https://www.linkedin.com/company/canrd/"
  24 + rel="noopener noreferrer"
  25 + >LinkedIn</a
  26 + >
  27 + </p>
  28 + <p>
  29 + <a href="https://x.com/canrdenerge?s=11" rel="noopener noreferrer"
  30 + >Twitter</a
  31 + >
  32 + </p>
19 </v-col> 33 </v-col>
20 <v-col cols="12" lg="3" sm="12" md="6"> 34 <v-col cols="12" lg="3" sm="12" md="6">
21 <div class="tw-w-[250px] tw-float-left tw-mr-[8px]"> 35 <div class="tw-w-[250px] tw-float-left tw-mr-[8px]">
@@ -24,7 +38,12 @@ @@ -24,7 +38,12 @@
24 <p>Phone: +86 19867737979</p> 38 <p>Phone: +86 19867737979</p>
25 <p>Wechat: contactcanrd</p> 39 <p>Wechat: contactcanrd</p>
26 </div> 40 </div>
27 - <img class="tw-float-left" src="/wechat.jpg" alt="canrud-wechat" width="80" /> 41 + <img
  42 + class="tw-float-left"
  43 + src="/wechat.jpg"
  44 + alt="canrud-wechat"
  45 + width="80"
  46 + />
28 </v-col> 47 </v-col>
29 </v-row> 48 </v-row>
30 </v-container> 49 </v-container>
components/MobileCategoryList.vue
@@ -200,7 +200,7 @@ watchEffect(async () =&gt; { @@ -200,7 +200,7 @@ watchEffect(async () =&gt; {
200 const foundFuncCategory = funcCategoryList.value.find( 200 const foundFuncCategory = funcCategoryList.value.find(
201 (func) => func.name === functionName 201 (func) => func.name === functionName
202 ); 202 );
203 - console.log(foundFuncCategory, "5656functionName"); 203 + console.log(foundFuncCategory, "functionName");
204 204
205 if (foundFuncCategory) { 205 if (foundFuncCategory) {
206 const funcCategoryId = foundFuncCategory.id; 206 const funcCategoryId = foundFuncCategory.id;
components/ProductDetail.vue
@@ -208,30 +208,15 @@ @@ -208,30 +208,15 @@
208 <div 208 <div
209 v-for="(imageObj, index) in recommendImages.slice(0, 5)" 209 v-for="(imageObj, index) in recommendImages.slice(0, 5)"
210 :key="'row1-' + index" 210 :key="'row1-' + index"
211 - style="  
212 - display: inline-block;  
213 - margin: 0 5px;  
214 - text-align: center;  
215 - width: 200px;  
216 - " 211 + class="imageTotal"
217 > 212 >
218 <a v-if="imageObj" :href="imageObj[0]?.productUrl" target="_blank"> 213 <a v-if="imageObj" :href="imageObj[0]?.productUrl" target="_blank">
219 <img 214 <img
220 :src="imageObj[0]?.url" 215 :src="imageObj[0]?.url"
221 :alt="'Image ' + (index + 1)" 216 :alt="'Image ' + (index + 1)"
222 - style="width: 200px; height: 200px; margin-right: 10px" 217 + class="item-imgHot"
223 /> 218 />
224 - <span  
225 - style="  
226 - display: block;  
227 - margin-top: 5px;  
228 - font-size: 16px;  
229 - width: 180px;  
230 - color: #555;  
231 - text-align: left;  
232 - margin-left: 10px;  
233 - "  
234 - > 219 + <span class="image-name">
235 {{ imageObj[0]?.name }} 220 {{ imageObj[0]?.name }}
236 </span> 221 </span>
237 </a> 222 </a>
@@ -249,30 +234,15 @@ @@ -249,30 +234,15 @@
249 <div 234 <div
250 v-for="(imageObj, index) in recommendImages.slice(5, 10)" 235 v-for="(imageObj, index) in recommendImages.slice(5, 10)"
251 :key="'row2-' + index" 236 :key="'row2-' + index"
252 - style="  
253 - display: inline-block;  
254 - margin: 0 5px;  
255 - text-align: center;  
256 - width: 200px;  
257 - " 237 + class="imageTotal"
258 > 238 >
259 <a v-if="imageObj" :href="imageObj[0]?.productUrl" target="_blank"> 239 <a v-if="imageObj" :href="imageObj[0]?.productUrl" target="_blank">
260 <img 240 <img
261 :src="imageObj[0]?.url" 241 :src="imageObj[0]?.url"
262 :alt="'Image ' + (index + 6)" 242 :alt="'Image ' + (index + 6)"
263 - style="width: 200px; height: 200px; margin-right: 10px" 243 + class="item-imgHot"
264 /> 244 />
265 - <span  
266 - style="  
267 - display: block;  
268 - margin-top: 5px;  
269 - font-size: 16px;  
270 - color: #555;  
271 - text-align: left;  
272 - width: 180px;  
273 - margin-left: 10px;  
274 - "  
275 - > 245 + <span class="image-name">
276 {{ imageObj[0]?.name }} 246 {{ imageObj[0]?.name }}
277 </span> 247 </span>
278 </a> 248 </a>
@@ -291,63 +261,6 @@ @@ -291,63 +261,6 @@
291 /> 261 />
292 </div> 262 </div>
293 </div> 263 </div>
294 -  
295 - <!-- <div class="tw-pb-[64px]">  
296 - <v-tabs  
297 - class="tabs"  
298 - v-model="tab"  
299 - color="white"  
300 - bg-color="#eeeeee"  
301 - slider-color="blue-lighten-1"  
302 - selected-class="active"  
303 - >  
304 - <v-tab :value="1">Product Details</v-tab>  
305 - <v-tab :value="2">Specification</v-tab>  
306 - </v-tabs>  
307 - <v-window v-model="tab" class="tw-p-[24px]">  
308 - <v-window-item key="1" :value="1">  
309 - <div v-if="info.advantage" class="tw-mb-[24px]">  
310 - <div class="text-h6">Advantage</div>  
311 - <v-divider class="tw-mb-[12px]"></v-divider>  
312 - <div v-html="info.advantage"></div>  
313 - </div>  
314 - <div v-if="info.physicalproperty" class="tw-mb-[24px]">  
315 - <div class="text-h6">Physical Property</div>  
316 - <v-divider class="tw-mb-[12px]"></v-divider>  
317 - <div v-html="info.physicalproperty"></div>  
318 - </div>  
319 - <div v-if="info.storage" class="tw-mb-[24px]">  
320 - <div class="text-h6">Storage</div>  
321 - <v-divider class="tw-mb-[12px]"></v-divider>  
322 - <div v-html="info.storage"></div>  
323 - </div>  
324 - <div v-if="info.introduction" class="tw-mb-[24px]">  
325 - <div class="text-h6">Introduction</div>  
326 - <v-divider class="tw-mb-[12px]"></v-divider>  
327 - <div v-html="info.introduction"></div>  
328 - </div>  
329 - <div v-if="info.description" class="tw-mb-[24px]">  
330 - <div class="text-h6">Description</div>  
331 - <v-divider class="tw-mb-[12px]"></v-divider>  
332 - <div v-html="info.description"></div>  
333 - </div>  
334 - </v-window-item>  
335 - <v-window-item key="2" :value="2">  
336 - <v-table density="compact" class="table2">  
337 - <tbody>  
338 - <tr  
339 - class="tr"  
340 - v-for="item in info.productAttributeList || []"  
341 - :key="item.name"  
342 - >  
343 - <td class="td tw-w-[400px]">{{ item.name }}</td>  
344 - <td class="td">{{ item.value }}</td>  
345 - </tr>  
346 - </tbody>  
347 - </v-table>  
348 - </v-window-item>  
349 - </v-window>  
350 - </div> -->  
351 <div style="display: flex"> 264 <div style="display: flex">
352 <div class="tw-pb-[64px]" style="width: 70%; margin-right: 10px"> 265 <div class="tw-pb-[64px]" style="width: 70%; margin-right: 10px">
353 <v-tabs 266 <v-tabs
@@ -416,81 +329,82 @@ @@ -416,81 +329,82 @@
416 selected-class="active" 329 selected-class="active"
417 grow 330 grow
418 > 331 >
419 - <v-tab value="one">Blog recommendation</v-tab> 332 + <v-tab value="one">Journal Recommendation</v-tab>
420 </v-tabs> 333 </v-tabs>
421 -<!-- <v-list lines="three">-->  
422 -<!-- <v-list-item-->  
423 -<!-- v-for="item in info.journals"-->  
424 -<!-- :key="item.id"-->  
425 -<!-- :subtitle="item.title"-->  
426 -<!-- @click="navigateToUrl(item.link)"-->  
427 -<!-- lines="three"-->  
428 -<!-- style="font-size: 24px"-->  
429 -<!-- ></v-list-item>-->  
430 -<!-- </v-list>-->  
431 <v-list> 334 <v-list>
432 <v-list-item 335 <v-list-item
433 - v-for="item in info.journals"  
434 - :key="item.id"  
435 - @click="navigateToUrl(item.link)"  
436 - @mouseenter="hoveredItem = item.id"  
437 - @mouseleave="hoveredItem = null" 336 + v-for="item in info.journals"
  337 + :key="item.id"
  338 + @click="navigateToUrl(item.link)"
  339 + @mouseenter="hoveredItem = item.id"
  340 + @mouseleave="hoveredItem = null"
438 > 341 >
439 <v-list-item-title> 342 <v-list-item-title>
440 - <span  
441 - :class="['title', { 'full-title': hoveredItem === item.id }]"  
442 - >  
443 - {{ item.title }}  
444 - </span> 343 + <span
  344 + :class="['title', { 'full-title': hoveredItem === item.id }]"
  345 + >
  346 + {{ item.title }}
  347 + </span>
445 </v-list-item-title> 348 </v-list-item-title>
446 </v-list-item> 349 </v-list-item>
447 </v-list> 350 </v-list>
448 - <!-- <v-list rounded>  
449 - <v-list-item-group v-model="selectedItem" color="primary">  
450 - <v-list-item  
451 - v-for="item in info.journals"  
452 - :key="item.id"  
453 - @click="navigateToUrl(item.link)"  
454 - >  
455 - <v-list-item-content>  
456 - <v-list-item-title v-text="item.title"></v-list-item-title>  
457 - </v-list-item-content>  
458 - </v-list-item>  
459 - </v-list-item-group>  
460 - </v-list> -->  
461 -<!-- <v-list rounded>-->  
462 -<!-- <v-list-item-group v-model="selectedItem" color="primary">-->  
463 -<!-- <v-list-item-->  
464 -<!-- v-for="item in info.journals"-->  
465 -<!-- :key="item.id"-->  
466 -<!-- @click="navigateToUrl(item.link)"-->  
467 -<!-- >-->  
468 -<!-- <v-list-item-content>-->  
469 -<!-- <v-hover v-slot:default="{ isHovering, props }">-->  
470 -<!-- <div v-bind="props">-->  
471 -<!-- <v-list-item-title-->  
472 -<!-- v-text="item.title"-->  
473 -<!-- style="-->  
474 -<!-- overflow: hidden;-->  
475 -<!-- text-overflow: ellipsis;-->  
476 -<!-- white-space: nowrap;-->  
477 -<!-- "-->  
478 -<!-- ></v-list-item-title>-->  
479 -<!-- <span v-if="isHovering" bottom style="color: #1e88e5">{{-->  
480 -<!-- item.title-->  
481 -<!-- }}</span>-->  
482 -<!-- <v-tooltip v-if="isHovering" bottom>-->  
483 -<!-- <template v-slot:activator="{ props: tooltipProps }">-->  
484 -<!-- <span v-bind="tooltipProps">{{ item.title }}</span>-->  
485 -<!-- </template>-->  
486 -<!-- <span>{{ item.title }}</span>-->  
487 -<!-- </v-tooltip>-->  
488 -<!-- </div>-->  
489 -<!-- </v-hover>-->  
490 -<!-- </v-list-item-content>-->  
491 -<!-- </v-list-item>-->  
492 -<!-- </v-list-item-group>-->  
493 -<!-- </v-list>--> 351 + </div>
  352 + </div>
  353 + <v-tabs
  354 + class="tabs"
  355 + v-model="tabRecomHot"
  356 + style="margin-top: 25px"
  357 + color="white"
  358 + bg-color="#eeeeee"
  359 + slider-color="blue-lighten-1"
  360 + selected-class="active"
  361 + v-if="recommendImagesHot[0] !== null"
  362 + >
  363 + <v-tab :value="1">Best Sellers</v-tab>
  364 + <!-- <v-tab :value="3">商品百科</v-tab> -->
  365 + </v-tabs>
  366 + <div id="image-container" v-if="recommendImagesHot[0] !== null">
  367 + <div class="recommend-left-box">
  368 + <v-img
  369 + src="https://m-canrd.oss-cn-shenzhen.aliyuncs.com/crmebimage/public/maintain/2024/09/14/76c987e13a334be481a346c19c2284f3qy4tjnxps7.png"
  370 + alt="往左移"
  371 + class="recommend-img-left"
  372 + @click="toggleRowLeft"
  373 + />
  374 + </div>
  375 + <div class="image-row" id="row1">
  376 + <!-- <img
  377 + v-for="(imageObj, index) in recommendImages.slice(0, 5)"
  378 + :key="'row1-' + index"
  379 + :src="imageObj[0]?.url"
  380 + :alt="'Image ' + (index + 1)"
  381 + style="margin: 0 5px; width: 200px; height: 200px"
  382 + /> -->
  383 + <div
  384 + v-for="(imageObj, index) in recommendImagesHot"
  385 + :key="'row1-' + index"
  386 + class="imageTotal"
  387 + >
  388 + <a v-if="imageObj" :href="imageObj[0]?.productUrl" target="_blank">
  389 + <img
  390 + :src="imageObj[0]?.url"
  391 + :alt="'Image ' + (index + 1)"
  392 + class="item-imgHot"
  393 + />
  394 + <span class="image-name">
  395 + {{ imageObj[0]?.name }}
  396 + </span>
  397 + </a>
  398 + <div v-else style="width: 200px; height: 200px"></div>
  399 + </div>
  400 + </div>
  401 + <div class="recommend-right-box">
  402 + <v-img
  403 + src="https://m-canrd.oss-cn-shenzhen.aliyuncs.com/crmebimage/public/maintain/2024/09/14/b5daa0a8f2f140f5b406e984c730a453iznzlekysg.png"
  404 + alt="往右移"
  405 + class="recommend-img-right"
  406 + @click="toggleRowRight"
  407 + />
494 </div> 408 </div>
495 </div> 409 </div>
496 <div class="social-share-container"> 410 <div class="social-share-container">
@@ -545,21 +459,12 @@ const href1 = ref(&quot;/products&quot;); @@ -545,21 +459,12 @@ const href1 = ref(&quot;/products&quot;);
545 const href2 = ref("/products"); 459 const href2 = ref("/products");
546 const idHref = ref("/products"); 460 const idHref = ref("/products");
547 const hoveredItem = ref(null); 461 const hoveredItem = ref(null);
548 -const itemsss = [  
549 - {  
550 - name: "Item #1",  
551 - id: 1,  
552 - },  
553 - {  
554 - name: "Item #2",  
555 - id: 2,  
556 - },  
557 - {  
558 - name: "Item #3",  
559 - id: 3,  
560 - },  
561 -];  
562 const productStore = useProductListStore(); 462 const productStore = useProductListStore();
  463 +const maxPage = ref(1);
  464 +const tabRecomHot = ref();
  465 +const recommendListHot = ref();
  466 +const recommendImagesHot = ref();
  467 +const currentIndexHot = ref(1);
563 const currentUrl = ref("https://www.canrud.com/products"); 468 const currentUrl = ref("https://www.canrud.com/products");
564 // 定义单个 item 的接口 469 // 定义单个 item 的接口
565 interface BreadcrumbItem { 470 interface BreadcrumbItem {
@@ -610,6 +515,70 @@ let { data: resData } = await useAsyncData( @@ -610,6 +515,70 @@ let { data: resData } = await useAsyncData(
610 server: true, // 仅在服务器端获取数据 515 server: true, // 仅在服务器端获取数据
611 } 516 }
612 ); 517 );
  518 +const loadHotProducts = async () => {
  519 + let { data: hotProducts } = await useAsyncData(
  520 + "hotProducts",
  521 + () =>
  522 + $fetch("/shop/product/hotProducts", {
  523 + method: "GET",
  524 + params: {
  525 + pageNo: currentIndexHot.value,
  526 + pageSize: 5,
  527 + },
  528 + }),
  529 + {
  530 + server: true, // 仅在服务器端获取数据
  531 + }
  532 + );
  533 + recommendListHot.value = hotProducts.value.data.records;
  534 + maxPage.value = hotProducts.value.data.pages;
  535 + // recommendImages.value = recommendList.value.slice(0, 10).map((item) => {
  536 + recommendImagesHot.value = Array.from({ length: 5 }).map((_, index) => {
  537 + const item = recommendListHot.value[index];
  538 + if (!item) {
  539 + return null;
  540 + }
  541 + // 检查 productimageliststore 是否为字符串格式,如果是,则尝试解析
  542 + if (typeof item.productimageliststore === "string") {
  543 + try {
  544 + item.productimageliststore = JSON.parse(item.productimageliststore);
  545 + } catch (error) {
  546 + item.productimageliststore = []; // 解析失败时,设置为空数组
  547 + }
  548 + }
  549 + const ree = (item.productimageliststore = item?.productimageliststore.map(
  550 + (productItem: ProductImage) => ({
  551 + ...productItem,
  552 + // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}`,
  553 + url: `https://www.canrud.com/api/show/image?fileKey=${productItem.fileKey}&psize=p256`,
  554 + name: item.name,
  555 + productUrl: `https://www.canrud.com/products/detail/${item.id}`,
  556 + })
  557 + ));
  558 + return ree;
  559 + });
  560 +};
  561 +const toggleRowLeft = () => {
  562 + if (currentIndex.value !== 1) {
  563 + currentIndex.value--;
  564 + } else if (currentIndex.value == 1) {
  565 + currentIndex.value = maxPage.value;
  566 + }
  567 +};
  568 +const toggleRowRight = () => {
  569 + if (currentIndex.value < maxPage.value) {
  570 + currentIndex.value++;
  571 + } else if (currentIndex.value == maxPage.value) {
  572 + currentIndex.value = 1;
  573 + }
  574 +};
  575 +watch(currentIndexHot, (newIndex) => {
  576 + loadHotProducts(); // Call loadHotProducts when currentIndex changes
  577 +});
  578 +
  579 +// Initial load of hot products
  580 +await loadHotProducts(); // Load hot products the first time
  581 +
613 watchEffect(() => { 582 watchEffect(() => {
614 currentUrl.value = "https://www.canrud.com/products/detail/" + info.id; 583 currentUrl.value = "https://www.canrud.com/products/detail/" + info.id;
615 if (info?.productCrumbsVO?.category1 && productStore.keyword) { 584 if (info?.productCrumbsVO?.category1 && productStore.keyword) {
@@ -712,9 +681,9 @@ watchEffect(() =&gt; { @@ -712,9 +681,9 @@ watchEffect(() =&gt; {
712 (productItem: ProductImage) => ({ 681 (productItem: ProductImage) => ({
713 ...productItem, 682 ...productItem,
714 // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}`, 683 // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}`,
715 - url: `/api/show/image?fileKey=${productItem.fileKey}&psize=p512`, 684 + url: `https://www.canrud.com/api/show/image?fileKey=${productItem.fileKey}&psize=p256`,
716 name: item.name, 685 name: item.name,
717 - productUrl: `http://www.canrud.com/products/detail/${item.id}`, 686 + productUrl: `https://www.canrud.com/products/detail/${item.id}`,
718 }) 687 })
719 )); 688 ));
720 return ree; 689 return ree;
@@ -804,23 +773,80 @@ th { @@ -804,23 +773,80 @@ th {
804 color: #1e88e5; 773 color: #1e88e5;
805 } 774 }
806 775
807 -#image-container {  
808 - display: flex;  
809 - align-items: center;  
810 - justify-content: center;  
811 - height: 320px;  
812 - margin-top: 10px;  
813 - margin-bottom: 10px; 776 +@media screen and (min-width: 1537px) {
  777 + #image-container {
  778 + display: flex;
  779 + align-items: center;
  780 + justify-content: center;
  781 + height: 320px;
  782 + margin: 10px auto 100px;
  783 + width: 80%;
  784 + }
  785 +}
  786 +@media screen and (max-width: 1536px) and (min-width: 1281px) {
  787 + #image-container {
  788 + display: flex;
  789 + align-items: center;
  790 + justify-content: center;
  791 + height: 320px;
  792 + margin: 10px auto 0px;
  793 + width: 80%;
  794 + padding: 0;
  795 + }
814 } 796 }
815 -  
816 .image-row { 797 .image-row {
817 display: flex; 798 display: flex;
818 height: 245px; 799 height: 245px;
819 } 800 }
820 -  
821 -.image-row img {  
822 - width: 120px;  
823 - height: 120px; 801 +@media screen and (min-width: 1537px) {
  802 + .imageTotal {
  803 + display: inline-block;
  804 + margin: 0 5px;
  805 + text-align: center;
  806 + width: 315px;
  807 + }
  808 +}
  809 +@media screen and (max-width: 1536px) and (min-width: 1281px) {
  810 + .imageTotal {
  811 + display: inline-block;
  812 + margin: 0 5px;
  813 + text-align: center;
  814 + width: 200px;
  815 + }
  816 +}
  817 +@media screen and (min-width: 1537px) {
  818 + .image-row img {
  819 + width: 240px;
  820 + height: 240px;
  821 + }
  822 +}
  823 +@media screen and (max-width: 1536px) and (min-width: 1281px) {
  824 + .image-row img {
  825 + width: 140px;
  826 + height: 140px;
  827 + }
  828 +}
  829 +@media screen and (min-width: 1537px) {
  830 + .image-name {
  831 + display: block;
  832 + margin-top: 5px;
  833 + font-size: 16px;
  834 + width: 180px;
  835 + color: #555;
  836 + text-align: left;
  837 + margin-left: 60px;
  838 + }
  839 +}
  840 +@media screen and (max-width: 1536px) and (min-width: 1281px) {
  841 + .image-name {
  842 + display: block;
  843 + margin-top: 5px;
  844 + font-size: 16px;
  845 + width: 180px;
  846 + color: #555;
  847 + text-align: left;
  848 + margin-left: 10px;
  849 + }
824 } 850 }
825 851
826 button .recommendButton { 852 button .recommendButton {
@@ -857,6 +883,7 @@ button .recommendButton { @@ -857,6 +883,7 @@ button .recommendButton {
857 display: flex; 883 display: flex;
858 flex-direction: row; /* 父容器横向排列 */ 884 flex-direction: row; /* 父容器横向排列 */
859 gap: 1rem; /* 设置每个分享项之间的间距 */ 885 gap: 1rem; /* 设置每个分享项之间的间距 */
  886 + margin-top: 40px;
860 } 887 }
861 888
862 .social-share-item { 889 .social-share-item {
@@ -873,11 +900,11 @@ button .recommendButton { @@ -873,11 +900,11 @@ button .recommendButton {
873 900
874 .title { 901 .title {
875 font-size: 15px; /* 设置字体大小为24px */ 902 font-size: 15px; /* 设置字体大小为24px */
876 - color: black; /* 设置字体颜色为黑色 */ 903 + color: black; /* 设置字体颜色为黑色 */
877 display: -webkit-box; /* 使用盒模型显示多行文本 */ 904 display: -webkit-box; /* 使用盒模型显示多行文本 */
878 -webkit-line-clamp: 2; /* 限制为两行 */ 905 -webkit-line-clamp: 2; /* 限制为两行 */
879 -webkit-box-orient: vertical; /* 盒子方向为垂直 */ 906 -webkit-box-orient: vertical; /* 盒子方向为垂直 */
880 - overflow: hidden; /* 超过部分隐藏 */ 907 + overflow: hidden; /* 超过部分隐藏 */
881 transition: max-height 0.3s ease; /* 过渡效果 */ 908 transition: max-height 0.3s ease; /* 过渡效果 */
882 } 909 }
883 910
@@ -885,6 +912,6 @@ button .recommendButton { @@ -885,6 +912,6 @@ button .recommendButton {
885 display: block; /* 当悬浮时显示为块级元素 */ 912 display: block; /* 当悬浮时显示为块级元素 */
886 color: #1e88e5; 913 color: #1e88e5;
887 white-space: normal; /* 允许换行 */ 914 white-space: normal; /* 允许换行 */
888 - overflow: visible; /* 显示全部内容 */ 915 + overflow: visible; /* 显示全部内容 */
889 } 916 }
890 </style> 917 </style>
nuxt.config.ts
@@ -41,8 +41,8 @@ export default defineNuxtConfig({ @@ -41,8 +41,8 @@ export default defineNuxtConfig({
41 nitro: { 41 nitro: {
42 devProxy: { 42 devProxy: {
43 "/shop": { 43 "/shop": {
44 - target: "http://47.89.254.121:8002/shop", // 线上代理地址  
45 - // target: "http://127.0.0.1:8002/shop", 44 + // target: "http://47.89.254.121:8002/shop", // 线上代理地址
  45 + target: "http://127.0.0.1:8002/shop",
46 // target: process.env.BASE_URL || 'http://39.108.227.113:8002/shop', // 目标接口域名 46 // target: process.env.BASE_URL || 'http://39.108.227.113:8002/shop', // 目标接口域名
47 changeOrigin: true, // 表示是否跨域 47 changeOrigin: true, // 表示是否跨域
48 }, 48 },
@@ -50,8 +50,8 @@ export default defineNuxtConfig({ @@ -50,8 +50,8 @@ export default defineNuxtConfig({
50 // 该配置用于服务端请求转发 50 // 该配置用于服务端请求转发
51 routeRules: { 51 routeRules: {
52 "/shop/**": { 52 "/shop/**": {
53 - proxy: "http://47.89.254.121:8002/shop/**",  
54 - // proxy: "http://127.0.0.1:8002/shop/**", 53 + // proxy: "http://47.89.254.121:8002/shop/**",
  54 + proxy: "http://127.0.0.1:8002/shop/**",
55 // proxy: process.env.BASE_URL || 'http://39.108.227.113:8002/shop/**' 55 // proxy: process.env.BASE_URL || 'http://39.108.227.113:8002/shop/**'
56 }, 56 },
57 }, 57 },
pages/index.vue
1 <template> 1 <template>
2 <v-rows class="tw-w-full"> 2 <v-rows class="tw-w-full">
3 - <v-carousel hide-delimiter-background show-arrows="hover" height="auto" v-if="!isMobile()">  
4 - <v-carousel-item v-for="banner in banners" :src="banner" cover alt="canrud" :key="banner"> 3 + <v-carousel
  4 + hide-delimiter-background
  5 + show-arrows="hover"
  6 + height="auto"
  7 + v-if="!isMobile()"
  8 + >
  9 + <v-carousel-item
  10 + v-for="banner in banners"
  11 + :src="banner"
  12 + cover
  13 + alt="canrud"
  14 + :key="banner"
  15 + >
5 </v-carousel-item> 16 </v-carousel-item>
6 </v-carousel> 17 </v-carousel>
7 - <v-carousel hide-delimiter-background show-arrows="hover" height="auto" v-if="isMobile()">  
8 - <v-carousel-item v-for="banner in mobileBanners" :src="banner" cover alt="canrud" :key="banner"> 18 + <v-carousel
  19 + hide-delimiter-background
  20 + show-arrows="hover"
  21 + height="auto"
  22 + v-if="isMobile()"
  23 + >
  24 + <v-carousel-item
  25 + v-for="banner in mobileBanners"
  26 + :src="banner"
  27 + cover
  28 + alt="canrud"
  29 + :key="banner"
  30 + >
9 </v-carousel-item> 31 </v-carousel-item>
10 </v-carousel> 32 </v-carousel>
11 </v-rows> 33 </v-rows>
12 <!-- 能源材料 --> 34 <!-- 能源材料 -->
13 <div class="tw-py-8 py-sm-16"> 35 <div class="tw-py-8 py-sm-16">
14 <v-container> 36 <v-container>
15 - <MainTitleListOdd title="Material Reagents" :list="materials"  
16 - desc="Leading global provider of energy storage research materials and providing other professional/universal experimental materials. " /> 37 + <MainTitleListOdd
  38 + title="Material Reagents"
  39 + :list="materials"
  40 + desc="Leading global provider of energy storage research materials and providing other professional/universal experimental materials. "
  41 + />
17 </v-container> 42 </v-container>
18 </div> 43 </div>
19 44
20 <!-- 设备 --> 45 <!-- 设备 -->
21 <div class="bg-grey-lighten-5 tw-py-8 py-sm-16"> 46 <div class="bg-grey-lighten-5 tw-py-8 py-sm-16">
22 <v-container> 47 <v-container>
23 - <MainTitleList title="Lab Device" listType="equipment" 48 + <MainTitleList
  49 + title="Lab Device"
  50 + listType="equipment"
24 :list="lab.list.map((item) => ({ ...item, href: '/products' }))" 51 :list="lab.list.map((item) => ({ ...item, href: '/products' }))"
25 desc="Self-built High-precision Machining Center with Powerful Design and Manufacturing Capabilities. " 52 desc="Self-built High-precision Machining Center with Powerful Design and Manufacturing Capabilities. "
26 - href="/equipment" /> 53 + href="/equipment"
  54 + />
27 </v-container> 55 </v-container>
28 </div> 56 </div>
29 57
30 <!-- Customized Battery --> 58 <!-- Customized Battery -->
31 <div class="tw-py-8 py-sm-16"> 59 <div class="tw-py-8 py-sm-16">
32 <v-container> 60 <v-container>
33 - <MainTitleList :title="('Customized Battery')" :list="batteries" href="/customize"  
34 - desc="200mAh~10Ah, Winding/Stacking, Unfilled/Filled Electrolyte Cells, Three-Electrode, and More. " /> 61 + <MainTitleList
  62 + :title="'Customized Battery'"
  63 + :list="batteries"
  64 + href="/customize"
  65 + desc="200mAh~10Ah, Winding/Stacking, Unfilled/Filled Electrolyte Cells, Three-Electrode, and More. "
  66 + />
35 </v-container> 67 </v-container>
36 </div> 68 </div>
37 <!-- Testing --> 69 <!-- Testing -->
38 <div class="bg-grey-lighten-5 tw-py-8 py-sm-16"> 70 <div class="bg-grey-lighten-5 tw-py-8 py-sm-16">
39 <v-container> 71 <v-container>
40 - <MainTitleList title="Testing" :list="tests" href="/test"  
41 - desc="Self built testing center and signed strategic cooperation with ATL, Tsinghua and other units. " /> 72 + <MainTitleList
  73 + title="Testing"
  74 + :list="tests"
  75 + href="/test"
  76 + desc="Self built testing center and signed strategic cooperation with ATL, Tsinghua and other units. "
  77 + />
42 </v-container> 78 </v-container>
43 </div> 79 </div>
44 <!-- Pack --> 80 <!-- Pack -->
45 <div class="pt-8 pb-8 pt pt-sm-16 pb-sm-32"> 81 <div class="pt-8 pb-8 pt pt-sm-16 pb-sm-32">
46 <v-container> 82 <v-container>
47 - <MainTitleList title="Pack" href="/pack" :list="packs"  
48 - desc="Focusing on energy materials/new energy storage systems/modules and other fields, mastering advanced technologies to provide high-quality services. " /> 83 + <MainTitleList
  84 + title="Pack"
  85 + href="/pack"
  86 + :list="packs"
  87 + desc="Focusing on energy materials/new energy storage systems/modules and other fields, mastering advanced technologies to provide high-quality services. "
  88 + />
49 </v-container> 89 </v-container>
50 </div> 90 </div>
  91 + <v-tabs
  92 + class="tabs"
  93 + v-model="tabRecom"
  94 + color="white"
  95 + bg-color="#eeeeee"
  96 + slider-color="blue-lighten-1"
  97 + selected-class="active"
  98 + v-if="recommendImages[0] !== null && !isMobile()"
  99 + >
  100 + <v-tab :value="1">Best Sellers</v-tab>
  101 + <!-- <v-tab :value="3">商品百科</v-tab> -->
  102 + </v-tabs>
  103 + <div id="image-container" v-if="recommendImages[0] !== null && !isMobile()">
  104 + <div class="recommend-left-box">
  105 + <v-img
  106 + src="https://m-canrd.oss-cn-shenzhen.aliyuncs.com/crmebimage/public/maintain/2024/09/14/76c987e13a334be481a346c19c2284f3qy4tjnxps7.png"
  107 + alt="往左移"
  108 + class="recommend-img-left"
  109 + @click="toggleRowLeft"
  110 + />
  111 + </div>
  112 + <div class="image-row" id="row1">
  113 + <!-- <img
  114 + v-for="(imageObj, index) in recommendImages.slice(0, 5)"
  115 + :key="'row1-' + index"
  116 + :src="imageObj[0]?.url"
  117 + :alt="'Image ' + (index + 1)"
  118 + style="margin: 0 5px; width: 200px; height: 200px"
  119 + /> -->
  120 + <div
  121 + v-for="(imageObj, index) in recommendImages"
  122 + :key="'row1-' + index"
  123 + class="imageTotal"
  124 + >
  125 + <a v-if="imageObj" :href="imageObj[0]?.productUrl" target="_blank">
  126 + <img
  127 + :src="imageObj[0]?.url"
  128 + :alt="'Image ' + (index + 1)"
  129 + class="item-imgHot"
  130 + />
  131 + <span class="image-name">
  132 + {{ imageObj[0]?.name }}
  133 + </span>
  134 + </a>
  135 + <div v-else style="width: 200px; height: 200px"></div>
  136 + </div>
  137 + </div>
  138 + <div class="recommend-right-box">
  139 + <v-img
  140 + src="https://m-canrd.oss-cn-shenzhen.aliyuncs.com/crmebimage/public/maintain/2024/09/14/b5daa0a8f2f140f5b406e984c730a453iznzlekysg.png"
  141 + alt="往右移"
  142 + class="recommend-img-right"
  143 + @click="toggleRowRight"
  144 + />
  145 + </div>
  146 + </div>
51 </template> 147 </template>
52 148
53 <script setup lang="ts"> 149 <script setup lang="ts">
54 -import MainTitleList from '../components/MainTitleList.vue'  
55 -import MainTitleListOdd from '../components/MainTitleListOdd.vue'  
56 -import { useCategoryStore } from '../stores/category'  
57 -import { computed } from 'vue'  
58 -import { isMobile } from '../utils' 150 +import MainTitleList from "../components/MainTitleList.vue";
  151 +import MainTitleListOdd from "../components/MainTitleListOdd.vue";
  152 +import { useCategoryStore } from "../stores/category";
  153 +import { computed, onMounted, reactive, ref } from "vue";
  154 +import { isMobile } from "../utils";
  155 +
  156 +const maxPage = ref(1);
  157 +const tabRecom = ref();
  158 +const recommendList = ref();
  159 +const recommendImages = ref();
  160 +const currentIndex = ref(1);
  161 +const flag = ref(false);
  162 +const loadHotProducts = async () => {
  163 + let { data: hotProducts } = await useAsyncData(
  164 + "hotProducts",
  165 + () =>
  166 + $fetch("/shop/product/hotProducts", {
  167 + method: "GET",
  168 + params: {
  169 + pageNo: currentIndex.value,
  170 + pageSize: 5,
  171 + },
  172 + }),
  173 + {
  174 + server: true, // 仅在服务器端获取数据
  175 + }
  176 + );
  177 + recommendList.value = hotProducts.value.data.records;
  178 + maxPage.value = hotProducts.value.data.pages;
  179 + // recommendImages.value = recommendList.value.slice(0, 10).map((item) => {
  180 + recommendImages.value = Array.from({ length: 5 }).map((_, index) => {
  181 + const item = recommendList.value[index];
  182 + if (!item) {
  183 + return null;
  184 + }
  185 + // 检查 productimageliststore 是否为字符串格式,如果是,则尝试解析
  186 + if (typeof item.productimageliststore === "string") {
  187 + try {
  188 + item.productimageliststore = JSON.parse(item.productimageliststore);
  189 + } catch (error) {
  190 + item.productimageliststore = []; // 解析失败时,设置为空数组
  191 + }
  192 + }
  193 + const ree = (item.productimageliststore = item?.productimageliststore.map(
  194 + (productItem: ProductImage) => ({
  195 + ...productItem,
  196 + // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}`,
  197 + url: `https://www.canrud.com/api/show/image?fileKey=${productItem.fileKey}&psize=p256`,
  198 + name: item.name,
  199 + productUrl: `https://www.canrud.com/products/detail/${item.id}`,
  200 + })
  201 + ));
  202 + return ree;
  203 + });
  204 +};
  205 +const toggleRowLeft = () => {
  206 + if (currentIndex.value !== 1) {
  207 + currentIndex.value--;
  208 + } else if (currentIndex.value == 1) {
  209 + currentIndex.value = maxPage.value;
  210 + }
  211 +};
  212 +const toggleRowRight = () => {
  213 + if (currentIndex.value < maxPage.value) {
  214 + currentIndex.value++;
  215 + } else if (currentIndex.value == maxPage.value) {
  216 + currentIndex.value = 1;
  217 + }
  218 +};
  219 +watch(currentIndex, (newIndex) => {
  220 + loadHotProducts(); // Call loadHotProducts when currentIndex changes
  221 +});
  222 +
  223 +// Initial load of hot products
  224 +await loadHotProducts(); // Load hot products the first time
  225 +
59 onMounted(() => { 226 onMounted(() => {
60 - console.log('%c [ onMounted ]-10', 'font-size:13px; background:pink; color:#bf2c9f;', 111)  
61 -}) 227 + console.log(
  228 + "%c [ onMounted ]-10",
  229 + "font-size:13px; background:pink; color:#bf2c9f;",
  230 + 111
  231 + );
  232 +});
62 useHead({ 233 useHead({
63 - title: 'canrud', 234 + title: "canrud",
64 meta: [ 235 meta: [
65 { 236 {
66 - name: 'title',  
67 - content: "科路得,助您科研之路势在必得。Canrd aims to be the world's leading one-stop service provider in new energy research. With a dedication to excellence, we offer Material Reagents, Lab Devices, Customized Batteries, Testing, and Advanced Packaging for energy materials and storage systems. We master advanced technologies to provide high-quality solutions. Our team's quick responses ensure tailored and professional services to meet your unique needs. Contact us at contact@canrd.com or call +86 19867737979 to explore our innovative offerings. Together, let's shape a greener, brighter world!"  
68 - }, {  
69 - name: 'keywords', 237 + name: "title",
70 content: 238 content:
71 - '科路得,canrd,canrud,Energy Storage Research,Lithium Batteries Research,Material Reagents,Lab Device,Customized Battery,Testing,Pack',  
72 - }, { 239 + "科路得,助您科研之路势在必得。Canrd aims to be the world's leading one-stop service provider in new energy research. With a dedication to excellence, we offer Material Reagents, Lab Devices, Customized Batteries, Testing, and Advanced Packaging for energy materials and storage systems. We master advanced technologies to provide high-quality solutions. Our team's quick responses ensure tailored and professional services to meet your unique needs. Contact us at contact@canrd.com or call +86 19867737979 to explore our innovative offerings. Together, let's shape a greener, brighter world!",
  240 + },
  241 + {
  242 + name: "keywords",
  243 + content:
  244 + "科路得,canrd,canrud,Energy Storage Research,Lithium Batteries Research,Material Reagents,Lab Device,Customized Battery,Testing,Pack",
  245 + },
  246 + {
73 name: "description", 247 name: "description",
74 content: 248 content:
75 - "科路得,助您科研之路势在必得。Canrd aims to be the world's leading one-stop service provider in new energy research. With a dedication to excellence, we offer Material Reagents, Lab Devices, Customized Batteries, Testing, and Advanced Packaging for energy materials and storage systems. We master advanced technologies to provide high-quality solutions. Our team's quick responses ensure tailored and professional services to meet your unique needs. Contact us at contact@canrd.com or call +86 19867737979 to explore our innovative offerings. Together, let's shape a greener, brighter world!"  
76 - }],  
77 - link: [  
78 - { rel: 'preload', href: '/banner/banner1.jpg', as: 'image' }  
79 - ]  
80 -}) 249 + "科路得,助您科研之路势在必得。Canrd aims to be the world's leading one-stop service provider in new energy research. With a dedication to excellence, we offer Material Reagents, Lab Devices, Customized Batteries, Testing, and Advanced Packaging for energy materials and storage systems. We master advanced technologies to provide high-quality solutions. Our team's quick responses ensure tailored and professional services to meet your unique needs. Contact us at contact@canrd.com or call +86 19867737979 to explore our innovative offerings. Together, let's shape a greener, brighter world!",
  250 + },
  251 + ],
  252 + link: [{ rel: "preload", href: "/banner/banner1.jpg", as: "image" }],
  253 +});
81 254
82 // useAsyncData(async ({ app }) => { 255 // useAsyncData(async ({ app }) => {
83 // console.log('%c [ app ]-70', 'font-size:13px; background:pink; color:#bf2c9f;', app) 256 // console.log('%c [ app ]-70', 'font-size:13px; background:pink; color:#bf2c9f;', app)
@@ -85,76 +258,245 @@ useHead({ @@ -85,76 +258,245 @@ useHead({
85 // app.head.meta = [ 258 // app.head.meta = [
86 // { 259 // {
87 // name: 'description', 260 // name: 'description',
88 -// content: "科路得,助您科研之路势在必得。Canrd aims to be the world's leading one-stop service provider in new energy research. With a dedication to excellence, we offer Material Reagents, Lab Devices, Customized Batteries, Testing, and Advanced Packaging for energy materials and storage systems. We master advanced technologies to provide high-quality solutions. Our team's quick responses ensure tailored and professional services to meet your unique needs. Contact us at 261 +// content: "科路得,助您科研之路势在必得。Canrd aims to be the world's leading one-stop service provider in new energy research. With a dedication to excellence, we offer Material Reagents, Lab Devices, Customized Batteries, Testing, and Advanced Packaging for energy materials and storage systems. We master advanced technologies to provide high-quality solutions. Our team's quick responses ensure tailored and professional services to meet your unique needs. Contact us at
89 // }] 262 // }]
90 // }) 263 // })
91 -console.log(11) 264 +console.log(11);
92 265
93 -const store = useCategoryStore() 266 +const store = useCategoryStore();
94 267
95 const lab = computed( 268 const lab = computed(
96 () => 269 () =>
97 store?.list?.[3] || { 270 store?.list?.[3] || {
98 - categoryDisplayName: '',  
99 - list: [] 271 + categoryDisplayName: "",
  272 + list: [],
100 } 273 }
101 -) 274 +);
102 275
103 const banners = [ 276 const banners = [
104 - '/banner/banner1.jpg',  
105 - '/banner/banner2.jpg',  
106 - '/banner/banner3.jpg',  
107 - '/banner/banner5.jpg'  
108 -] 277 + "/banner/banner1.jpg",
  278 + "/banner/banner2.jpg",
  279 + "/banner/banner3.jpg",
  280 + "/banner/banner5.jpg",
  281 +];
109 282
110 const mobileBanners = [ 283 const mobileBanners = [
111 - '/mobile/banner-index1.png',  
112 - '/mobile/banner-index2.png',  
113 - '/mobile/banner-index3.png'  
114 -] 284 + "/mobile/banner-index1.png",
  285 + "/mobile/banner-index2.png",
  286 + "/mobile/banner-index3.png",
  287 +];
115 288
116 const materials = [ 289 const materials = [
117 - { name: 'Energy materials', imageUrl: '/home/1.jpg', href: '/products' }, 290 + { name: "Energy materials", imageUrl: "/home/1.jpg", href: "/products" },
118 { 291 {
119 - name: 'Laboratory consumables',  
120 - imageUrl: '/home/2-Universal-consumables.png',  
121 - href: '/products' 292 + name: "Laboratory consumables",
  293 + imageUrl: "/home/2-Universal-consumables.png",
  294 + href: "/products",
122 }, 295 },
123 { 296 {
124 - name: 'Low-dimensional materials',  
125 - imageUrl: '/home/3-Low-dimensional-materials.png',  
126 - href: '/products'  
127 - }  
128 -] 297 + name: "Low-dimensional materials",
  298 + imageUrl: "/home/3-Low-dimensional-materials.png",
  299 + href: "/products",
  300 + },
  301 +];
129 302
130 const tests = [ 303 const tests = [
131 { 304 {
132 - name: 'Electrochemical performance',  
133 - imageUrl: '/home/8_Electrochemical_performance.svg',  
134 - href: '/test' 305 + name: "Electrochemical performance",
  306 + imageUrl: "/home/8_Electrochemical_performance.svg",
  307 + href: "/test",
  308 + },
  309 + {
  310 + name: "Reliability testing",
  311 + imageUrl: "/home/9 Reliability testing.svg",
  312 + href: "/test",
135 }, 313 },
136 - { name: 'Reliability testing', imageUrl: '/home/9 Reliability testing.svg', href: '/test' },  
137 - { name: 'Material testing', imageUrl: '/home/10 Material testing.svg', href: '/test' },  
138 - { name: 'Calibration', imageUrl: '/home/11 Calibration.svg', href: '/test' }  
139 -] 314 + {
  315 + name: "Material testing",
  316 + imageUrl: "/home/10 Material testing.svg",
  317 + href: "/test",
  318 + },
  319 + { name: "Calibration", imageUrl: "/home/11 Calibration.svg", href: "/test" },
  320 +];
140 const batteries = [ 321 const batteries = [
141 - { name: 'Material evaluation', imageUrl: '/home/4-Material-evaluation.png', href: '/customize' },  
142 - { name: 'R&D foundry', imageUrl: '/home/5-R&D-foundry.png', href: '/customize' },  
143 - { name: 'Chemical system', imageUrl: '/home/6-Chemical-system.png', href: '/customize' },  
144 { 322 {
145 - name: 'Semi product customization',  
146 - imageUrl: '/home/7-Semi-product-customization.png',  
147 - href: '/customize'  
148 - }  
149 -] 323 + name: "Material evaluation",
  324 + imageUrl: "/home/4-Material-evaluation.png",
  325 + href: "/customize",
  326 + },
  327 + {
  328 + name: "R&D foundry",
  329 + imageUrl: "/home/5-R&D-foundry.png",
  330 + href: "/customize",
  331 + },
  332 + {
  333 + name: "Chemical system",
  334 + imageUrl: "/home/6-Chemical-system.png",
  335 + href: "/customize",
  336 + },
  337 + {
  338 + name: "Semi product customization",
  339 + imageUrl: "/home/7-Semi-product-customization.png",
  340 + href: "/customize",
  341 + },
  342 +];
150 const packs = [ 343 const packs = [
151 - { name: 'Power bank', imageUrl: '/home/12-power-bank.png', href: '/pack' },  
152 - { name: 'Energy storage', imageUrl: '/home/13-Energy-storage.png', href: '/pack' },  
153 - { name: 'power tool', imageUrl: '/home/3-powertool.png', href: '/pack' }, 344 + { name: "Power bank", imageUrl: "/home/12-power-bank.png", href: "/pack" },
154 { 345 {
155 - name: 'portable energy storage',  
156 - imageUrl: '/home/4-portableenergystorage.png',  
157 - href: '/pack'  
158 - }  
159 -] 346 + name: "Energy storage",
  347 + imageUrl: "/home/13-Energy-storage.png",
  348 + href: "/pack",
  349 + },
  350 + { name: "power tool", imageUrl: "/home/3-powertool.png", href: "/pack" },
  351 + {
  352 + name: "portable energy storage",
  353 + imageUrl: "/home/4-portableenergystorage.png",
  354 + href: "/pack",
  355 + },
  356 +];
160 </script> 357 </script>
  358 +<style scoped>
  359 +@media screen and (min-width: 1537px) {
  360 + .tabs {
  361 + border-bottom: 2px solid #1f88e5;
  362 + margin: 10px auto 100px;
  363 + width: 85%;
  364 + }
  365 +}
  366 +@media screen and (max-width: 1536px) and (min-width: 1281px) {
  367 + .tabs {
  368 + border-bottom: 2px solid #1f88e5;
  369 + margin: 10px auto 20px;
  370 + width: 80%;
  371 + }
  372 +}
  373 +
  374 +.active {
  375 + background-color: #1086e8;
  376 +}
  377 +
  378 +/* #image-container {
  379 + display: flex;
  380 + align-items: center;
  381 + justify-content: center;
  382 + height: 320px;
  383 + margin: 10px auto 50px;
  384 + width: 80%;
  385 +} */
  386 +
  387 +@media screen and (min-width: 1537px) {
  388 + #image-container {
  389 + display: flex;
  390 + align-items: center;
  391 + justify-content: center;
  392 + height: 320px;
  393 + margin: 10px auto 100px;
  394 + width: 80%;
  395 + }
  396 +}
  397 +@media screen and (max-width: 1536px) and (min-width: 1281px) {
  398 + #image-container {
  399 + display: flex;
  400 + align-items: center;
  401 + justify-content: center;
  402 + height: 320px;
  403 + margin: 10px auto 0px;
  404 + width: 80%;
  405 + }
  406 +}
  407 +.image-row {
  408 + display: flex;
  409 + height: 305px;
  410 +}
  411 +@media screen and (min-width: 1537px) {
  412 + .image-row {
  413 + display: flex;
  414 + height: 305px;
  415 + }
  416 +}
  417 +@media screen and (max-width: 1536px) and (min-width: 1281px) {
  418 + .image-row {
  419 + display: flex;
  420 + height: 245px;
  421 + }
  422 +}
  423 +@media screen and (min-width: 1537px) {
  424 + .imageTotal {
  425 + display: inline-block;
  426 + margin: 0 5px;
  427 + text-align: center;
  428 + width: 290px;
  429 + }
  430 +}
  431 +@media screen and (max-width: 1536px) and (min-width: 1281px) {
  432 + .imageTotal {
  433 + display: inline-block;
  434 + margin: 0 5px;
  435 + text-align: center;
  436 + width: 210px;
  437 + }
  438 +}
  439 +@media screen and (min-width: 1537px) {
  440 + .image-row img {
  441 + width: 240px;
  442 + height: 240px;
  443 + }
  444 +}
  445 +@media screen and (max-width: 1536px) and (min-width: 1281px) {
  446 + .image-row img {
  447 + width: 140px;
  448 + height: 140px;
  449 + }
  450 +}
  451 +@media screen and (min-width: 1537px) {
  452 + .image-name {
  453 + display: block;
  454 + margin-top: 5px;
  455 + font-size: 16px;
  456 + width: 180px;
  457 + color: #555;
  458 + text-align: left;
  459 + margin-left: 50px;
  460 + }
  461 +}
  462 +@media screen and (max-width: 1536px) and (min-width: 1281px) {
  463 + .image-name {
  464 + display: block;
  465 + margin-top: 5px;
  466 + font-size: 16px;
  467 + width: 180px;
  468 + color: #555;
  469 + text-align: left;
  470 + margin-left: 10px;
  471 + }
  472 +}
  473 +button .recommendButton {
  474 + margin: 0 0px;
  475 + cursor: pointer;
  476 +}
  477 +
  478 +.recommend-left-box {
  479 +}
  480 +
  481 +.recommend-img-left {
  482 + width: 26px;
  483 + height: 27px;
  484 + margin-right: 30px;
  485 +}
  486 +
  487 +.recommend-img-left:hover {
  488 + cursor: pointer;
  489 +}
  490 +.recommend-right-box {
  491 +}
  492 +
  493 +.recommend-img-right {
  494 + width: 26px;
  495 + height: 27px;
  496 + margin-left: 30px;
  497 +}
  498 +
  499 +.recommend-img-right:hover {
  500 + cursor: pointer;
  501 +}
  502 +</style>
pages/products/index.vue
1 <template> 1 <template>
2 - <div class="tw-m-auto tw-pb-[64px]"> 2 + <div class="tw-m-auto tw-pb-[64px]" style="padding-bottom: 0px">
3 <CategoryList v-if="categoryStore.categoryVisible && !isMobile()" /> 3 <CategoryList v-if="categoryStore.categoryVisible && !isMobile()" />
4 <MobileCategoryList v-if="categoryStore.categoryVisible && isMobile()" /> 4 <MobileCategoryList v-if="categoryStore.categoryVisible && isMobile()" />
5 <v-container class=""> 5 <v-container class="">
@@ -70,11 +70,72 @@ @@ -70,11 +70,72 @@
70 total-visible="5" 70 total-visible="5"
71 ></v-pagination></v-col 71 ></v-pagination></v-col
72 ></v-row> 72 ></v-row>
  73 + <v-tabs
  74 + class="tabs"
  75 + v-model="tabRecom"
  76 + style="margin-top: 25px"
  77 + color="white"
  78 + bg-color="#eeeeee"
  79 + slider-color="blue-lighten-1"
  80 + selected-class="active"
  81 + v-if="recommendImages[0] !== null && !isMobile()"
  82 + >
  83 + <v-tab :value="1">Best Sellers</v-tab>
  84 + <!-- <v-tab :value="3">商品百科</v-tab> -->
  85 + </v-tabs>
  86 + <div
  87 + id="image-container"
  88 + v-if="recommendImages[0] !== null && !isMobile()"
  89 + >
  90 + <div class="recommend-left-box">
  91 + <v-img
  92 + src="https://m-canrd.oss-cn-shenzhen.aliyuncs.com/crmebimage/public/maintain/2024/09/14/76c987e13a334be481a346c19c2284f3qy4tjnxps7.png"
  93 + alt="往左移"
  94 + class="recommend-img-left"
  95 + @click="toggleRowLeft"
  96 + />
  97 + </div>
  98 + <div class="image-row" id="row1">
  99 + <!-- <img
  100 + v-for="(imageObj, index) in recommendImages.slice(0, 5)"
  101 + :key="'row1-' + index"
  102 + :src="imageObj[0]?.url"
  103 + :alt="'Image ' + (index + 1)"
  104 + style="margin: 0 5px; width: 200px; height: 200px"
  105 + /> -->
  106 + <div
  107 + v-for="(imageObj, index) in recommendImages"
  108 + :key="'row1-' + index"
  109 + class="imageTotal"
  110 + >
  111 + <a v-if="imageObj" :href="imageObj[0]?.productUrl" target="_blank">
  112 + <img
  113 + :src="imageObj[0]?.url"
  114 + :alt="'Image ' + (index + 1)"
  115 + class="item-imgHot"
  116 + />
  117 + <span class="image-name">
  118 + {{ imageObj[0]?.name }}
  119 + </span>
  120 + </a>
  121 + <div v-else style="width: 200px; height: 200px"></div>
  122 + </div>
  123 + </div>
  124 + <div class="recommend-right-box">
  125 + <v-img
  126 + src="https://m-canrd.oss-cn-shenzhen.aliyuncs.com/crmebimage/public/maintain/2024/09/14/b5daa0a8f2f140f5b406e984c730a453iznzlekysg.png"
  127 + alt="往右移"
  128 + class="recommend-img-right"
  129 + @click="toggleRowRight"
  130 + />
  131 + </div>
  132 + </div>
73 </v-container> 133 </v-container>
74 </div> 134 </div>
75 </template> 135 </template>
76 136
77 <script setup lang="ts"> 137 <script setup lang="ts">
  138 +import type { ProductImage } from "~/type";
78 import { isMobile, isEqual } from "~/utils"; 139 import { isMobile, isEqual } from "~/utils";
79 import { useProductListStore } from "~/stores/product_list"; 140 import { useProductListStore } from "~/stores/product_list";
80 import { useCategoryStore } from "~/stores/category"; 141 import { useCategoryStore } from "~/stores/category";
@@ -89,7 +150,76 @@ const route = useRoute(); // 获取路由信息 @@ -89,7 +150,76 @@ const route = useRoute(); // 获取路由信息
89 const router = useRouter(); // 获取路由信息 150 const router = useRouter(); // 获取路由信息
90 const title = ref(""); 151 const title = ref("");
91 const keywordTitle = ref(""); 152 const keywordTitle = ref("");
  153 +const maxPage = ref(1);
  154 +const tabRecom = ref();
  155 +const recommendList = ref();
  156 +const recommendImages = ref();
  157 +const currentIndex = ref(1);
92 const isOrNotMobile = isMobile(); 158 const isOrNotMobile = isMobile();
  159 +const loadHotProducts = async () => {
  160 + let { data: hotProducts } = await useAsyncData(
  161 + "hotProducts",
  162 + () =>
  163 + $fetch("/shop/product/hotProducts", {
  164 + method: "GET",
  165 + params: {
  166 + pageNo: currentIndex.value,
  167 + pageSize: 5,
  168 + },
  169 + }),
  170 + {
  171 + server: true, // 仅在服务器端获取数据
  172 + }
  173 + );
  174 + recommendList.value = hotProducts.value.data.records;
  175 + maxPage.value = hotProducts.value.data.pages;
  176 + // recommendImages.value = recommendList.value.slice(0, 10).map((item) => {
  177 + recommendImages.value = Array.from({ length: 5 }).map((_, index) => {
  178 + const item = recommendList.value[index];
  179 + if (!item) {
  180 + return null;
  181 + }
  182 + // 检查 productimageliststore 是否为字符串格式,如果是,则尝试解析
  183 + if (typeof item.productimageliststore === "string") {
  184 + try {
  185 + item.productimageliststore = JSON.parse(item.productimageliststore);
  186 + } catch (error) {
  187 + item.productimageliststore = []; // 解析失败时,设置为空数组
  188 + }
  189 + }
  190 + const ree = (item.productimageliststore = item?.productimageliststore.map(
  191 + (productItem: ProductImage) => ({
  192 + ...productItem,
  193 + // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}`,
  194 + url: `https://www.canrud.com/api/show/image?fileKey=${productItem.fileKey}&psize=p256`,
  195 + name: item.name,
  196 + productUrl: `https://www.canrud.com/products/detail/${item.id}`,
  197 + })
  198 + ));
  199 + return ree;
  200 + });
  201 +};
  202 +const toggleRowLeft = () => {
  203 + if (currentIndex.value !== 1) {
  204 + currentIndex.value--;
  205 + } else if (currentIndex.value == 1) {
  206 + currentIndex.value = maxPage.value;
  207 + }
  208 +};
  209 +const toggleRowRight = () => {
  210 + if (currentIndex.value < maxPage.value) {
  211 + currentIndex.value++;
  212 + } else if (currentIndex.value == maxPage.value) {
  213 + currentIndex.value = 1;
  214 + }
  215 +};
  216 +watch(currentIndex, (newIndex) => {
  217 + loadHotProducts(); // Call loadHotProducts when currentIndex changes
  218 +});
  219 +
  220 +// Initial load of hot products
  221 +await loadHotProducts(); // Load hot products the first time
  222 +
93 watchEffect(() => { 223 watchEffect(() => {
94 // 遍历数组 224 // 遍历数组
95 if (router.currentRoute.value.query.categories) { 225 if (router.currentRoute.value.query.categories) {
@@ -285,4 +415,118 @@ const length = computed(() =&gt; @@ -285,4 +415,118 @@ const length = computed(() =&gt;
285 -webkit-line-clamp: 2; 415 -webkit-line-clamp: 2;
286 -webkit-box-orient: vertical; 416 -webkit-box-orient: vertical;
287 } 417 }
  418 +
  419 +.tabs {
  420 + border-bottom: 2px solid #1f88e5;
  421 +}
  422 +
  423 +.active {
  424 + background-color: #1086e8;
  425 +}
  426 +
  427 +@media screen and (min-width: 1537px) {
  428 + #image-container {
  429 + display: flex;
  430 + align-items: center;
  431 + justify-content: center;
  432 + height: 320px;
  433 + margin: 10px auto 100px;
  434 + width: 80%;
  435 + }
  436 +}
  437 +@media screen and (max-width: 1536px) and (min-width: 1281px) {
  438 + #image-container {
  439 + display: flex;
  440 + align-items: center;
  441 + justify-content: center;
  442 + height: 320px;
  443 + margin: 10px auto 0px;
  444 + width: 80%;
  445 + padding: 0;
  446 + }
  447 +}
  448 +.image-row {
  449 + display: flex;
  450 + height: 245px;
  451 +}
  452 +@media screen and (min-width: 1537px) {
  453 + .imageTotal {
  454 + display: inline-block;
  455 + margin: 0 5px;
  456 + text-align: center;
  457 + width: 300px;
  458 + }
  459 +}
  460 +@media screen and (max-width: 1536px) and (min-width: 1281px) {
  461 + .imageTotal {
  462 + display: inline-block;
  463 + margin: 0 5px;
  464 + text-align: center;
  465 + width: 200px;
  466 + }
  467 +}
  468 +@media screen and (min-width: 1537px) {
  469 + .image-row img {
  470 + width: 240px;
  471 + height: 240px;
  472 + }
  473 +}
  474 +@media screen and (max-width: 1536px) and (min-width: 1281px) {
  475 + .image-row img {
  476 + width: 140px;
  477 + height: 140px;
  478 + }
  479 +}
  480 +@media screen and (min-width: 1537px) {
  481 + .image-name {
  482 + display: block;
  483 + margin-top: 5px;
  484 + font-size: 16px;
  485 + width: 180px;
  486 + color: #555;
  487 + text-align: left;
  488 + margin-left: 50px;
  489 + }
  490 +}
  491 +@media screen and (max-width: 1536px) and (min-width: 1281px) {
  492 + .image-name {
  493 + display: block;
  494 + margin-top: 5px;
  495 + font-size: 16px;
  496 + width: 180px;
  497 + color: #555;
  498 + text-align: left;
  499 + margin-left: 10px;
  500 + }
  501 +}
  502 +
  503 +button .recommendButton {
  504 + margin: 0 0px;
  505 + cursor: pointer;
  506 +}
  507 +
  508 +.recommend-left-box {
  509 +}
  510 +
  511 +.recommend-img-left {
  512 + width: 26px;
  513 + height: 27px;
  514 + margin-right: 30px;
  515 +}
  516 +
  517 +.recommend-img-left:hover {
  518 + cursor: pointer;
  519 +}
  520 +.recommend-right-box {
  521 +}
  522 +
  523 +.recommend-img-right {
  524 + width: 26px;
  525 + height: 27px;
  526 + margin-left: 30px;
  527 +}
  528 +
  529 +.recommend-img-right:hover {
  530 + cursor: pointer;
  531 +}
288 </style> 532 </style>