Commit 70a1f94e251c21ee9f7cb97bd9018988393a79df
1 parent
0b26f917
feat: 开发手机端热销,prod2-35/36
Showing
6 changed files
with
564 additions
and
100 deletions
components/MobileProductDetail.vue
... | ... | @@ -259,6 +259,158 @@ |
259 | 259 | <!-- <v-window-item key="3" :value="3"> 2 </v-window-item> --> |
260 | 260 | </v-window> |
261 | 261 | </div> |
262 | + <div class="tw-pb-[64px] ma-4 rounded-lg" style="padding-bottom: 30px"> | |
263 | + <v-tabs | |
264 | + class="tabs" | |
265 | + v-model="tabProduct" | |
266 | + bg-color="#fff" | |
267 | + slider-color="#1d89e4" | |
268 | + selected-class="active" | |
269 | + style="margin-bottom: 20px" | |
270 | + > | |
271 | + <v-tab :value="1">Related Products</v-tab> | |
272 | + <v-tab :value="2">Best Sellers</v-tab> | |
273 | + <!-- <v-tab :value="3">商品百科</v-tab> --> | |
274 | + </v-tabs> | |
275 | + <v-window v-model="tabProduct"> | |
276 | + <v-window-item key="1" :value="1"> | |
277 | + <div class="tw-text-center" v-if="loading && isMobile()"> | |
278 | + <v-progress-circular | |
279 | + color="blue-lighten-2" | |
280 | + indeterminate | |
281 | + size="64" | |
282 | + class="tw-m-auto" | |
283 | + ></v-progress-circular> | |
284 | + </div> | |
285 | + <v-item-group multiple v-if="isMobile()"> | |
286 | + <v-row v-if="!loading"> | |
287 | + <v-col | |
288 | + v-for="(item, i) in recommendImages" | |
289 | + :key="i" | |
290 | + cols="6" | |
291 | + lg="3" | |
292 | + md="4" | |
293 | + sm="6" | |
294 | + > | |
295 | + <div v-if="item !== null"> | |
296 | + <v-card | |
297 | + :elevation="4" | |
298 | + class="mx-auto" | |
299 | + :href="item[0].productUrl" | |
300 | + > | |
301 | + <!-- 设置 eager 属性,确保图片直接加载 --> | |
302 | + <v-img | |
303 | + :src="item[0].url" | |
304 | + :alt="item[0].name" | |
305 | + eager | |
306 | + class="d-block" | |
307 | + /> | |
308 | + <v-card-text class="tw-text-left font-weight-medium title"> | |
309 | + <h4>{{ item[0].name }}</h4> | |
310 | + </v-card-text> | |
311 | + </v-card> | |
312 | + </div> | |
313 | + </v-col> | |
314 | + </v-row> | |
315 | + <!-- <div | |
316 | + v-if="!hotTotal" | |
317 | + class="text-medium-emphasis text-body-1 tw-text-center tw-m-[64px]" | |
318 | + > | |
319 | + no data | |
320 | + </div> --> | |
321 | + </v-item-group> | |
322 | + <v-row v-if="isMobile()"> | |
323 | + <v-col> | |
324 | + <v-pagination | |
325 | + :size="isMobile() ? 'small' : 'default'" | |
326 | + v-if="total" | |
327 | + v-model="currentIndex" | |
328 | + @update:modelValue="toggleRowMobile" | |
329 | + :length="length" | |
330 | + rounded="0" | |
331 | + class="tw-float-right tw-mt-[32px]" | |
332 | + total-visible="5" | |
333 | + ></v-pagination></v-col | |
334 | + ></v-row> | |
335 | + </v-window-item> | |
336 | + <!-- best sellers --> | |
337 | + <v-window-item key="2" :value="2"> | |
338 | + <div class="tw-text-center" v-if="hotLoading && isMobile()"> | |
339 | + <v-progress-circular | |
340 | + color="blue-lighten-2" | |
341 | + indeterminate | |
342 | + size="64" | |
343 | + class="tw-m-auto" | |
344 | + ></v-progress-circular> | |
345 | + </div> | |
346 | + <v-item-group multiple v-if="isMobile()"> | |
347 | + <v-row v-if="!hotLoading"> | |
348 | + <v-col | |
349 | + v-for="(item, i) in recommendImagesHot" | |
350 | + :key="i" | |
351 | + cols="6" | |
352 | + lg="3" | |
353 | + md="4" | |
354 | + sm="6" | |
355 | + > | |
356 | + <div v-if="item !== null"> | |
357 | + <v-card | |
358 | + :elevation="4" | |
359 | + class="mx-auto" | |
360 | + :href="item[0].productUrl" | |
361 | + > | |
362 | + <!-- 设置 eager 属性,确保图片直接加载 --> | |
363 | + <v-img | |
364 | + :src="item[0].url" | |
365 | + :alt="item[0].name" | |
366 | + eager | |
367 | + class="d-block" | |
368 | + /> | |
369 | + <v-card-text class="tw-text-left font-weight-medium title"> | |
370 | + <h4>{{ item[0].name }}</h4> | |
371 | + </v-card-text> | |
372 | + </v-card> | |
373 | + </div> | |
374 | + </v-col> | |
375 | + </v-row> | |
376 | + <!-- <div | |
377 | + v-if="!hotTotal" | |
378 | + class="text-medium-emphasis text-body-1 tw-text-center tw-m-[64px]" | |
379 | + > | |
380 | + no data | |
381 | + </div> --> | |
382 | + </v-item-group> | |
383 | + <v-row v-if="isMobile()"> | |
384 | + <v-col> | |
385 | + <v-pagination | |
386 | + :size="isMobile() ? 'small' : 'default'" | |
387 | + v-if="hotTotal" | |
388 | + v-model="currentIndexHot" | |
389 | + @update:modelValue="toggleRowMobileHot" | |
390 | + :length="hotLength" | |
391 | + rounded="0" | |
392 | + class="tw-float-right tw-mt-[32px]" | |
393 | + total-visible="5" | |
394 | + ></v-pagination></v-col | |
395 | + ></v-row> | |
396 | + </v-window-item> | |
397 | + <!-- <v-window-item key="3" :value="3"> 2 </v-window-item> --> | |
398 | + </v-window> | |
399 | + </div> | |
400 | + <!-- <div class="tw-pb-[64px] ma-4 rounded-lg"> | |
401 | + <v-tabs | |
402 | + class="tabs" | |
403 | + v-model="tabJournal" | |
404 | + bg-color="#fff" | |
405 | + slider-color="#1d89e4" | |
406 | + selected-class="active" | |
407 | + > | |
408 | + <v-tab :value="1">JOURNAL RECOMMENDATION</v-tab> | |
409 | + </v-tabs> | |
410 | + <v-window v-model="tab"> | |
411 | + <v-window-item key="1" :value="1"> </v-window-item> | |
412 | + </v-window> | |
413 | + </div> --> | |
262 | 414 | <!-- Basic use --> |
263 | 415 | <div class="social-share-container"> |
264 | 416 | <SocialShare |
... | ... | @@ -293,7 +445,8 @@ |
293 | 445 | |
294 | 446 | <script setup lang="ts"> |
295 | 447 | import type { Product } from "~/type"; |
296 | -import { ref } from "vue"; | |
448 | +import { isMobile } from "~/utils"; | |
449 | +import { defineProps, ref } from "vue"; | |
297 | 450 | import { useDialogStore } from "~/stores/dialog"; |
298 | 451 | const dialogStore = useDialogStore(); |
299 | 452 | |
... | ... | @@ -305,6 +458,8 @@ const info = props.info; |
305 | 458 | // dialogStore.updateDialog(true) |
306 | 459 | // }) |
307 | 460 | const tab = ref(0); |
461 | +const tabProduct = ref(0); | |
462 | +const tabJournal = ref(0); | |
308 | 463 | const slide = ref(0); |
309 | 464 | const router = useRouter(); |
310 | 465 | const href1 = ref("/products"); |
... | ... | @@ -312,6 +467,143 @@ const href2 = ref("/products"); |
312 | 467 | const routeQuery = useRouteQuery(); |
313 | 468 | const productStore = useProductListStore(); |
314 | 469 | const currentUrl = ref("https://www.canrud.com/products"); |
470 | +const hotLoading = ref(false); | |
471 | +const loading = ref(false); | |
472 | +const maxPage = ref(1); | |
473 | +const maxPageRe = ref(1); | |
474 | +const tabRecom = ref(); | |
475 | +const recommendList = ref(); | |
476 | +const recommendListHot = ref(); | |
477 | +const recommendImages = ref(); | |
478 | +const recommendImagesHot = ref(); | |
479 | +const currentIndex = ref(1); | |
480 | +const currentIndexHot = ref(1); | |
481 | +const total = ref(3); | |
482 | +const hotTotal = ref(10); | |
483 | +const isOrNotMobile = isMobile(); | |
484 | + | |
485 | +const loadProducts = async () => { | |
486 | + const pageSize = 4; | |
487 | + loading.value = true; | |
488 | + | |
489 | + let { data: resData } = await useAsyncData( | |
490 | + "list", | |
491 | + () => | |
492 | + $fetch("/shop/product/list", { | |
493 | + method: "GET", | |
494 | + params: { | |
495 | + pageNo: currentIndex.value, | |
496 | + pageSize: pageSize, | |
497 | + ids: info.relatedProductIds, | |
498 | + }, | |
499 | + }), | |
500 | + { | |
501 | + server: true, // 仅在服务器端获取数据 | |
502 | + } | |
503 | + ); | |
504 | + total.value = resData.value.data.total; | |
505 | + if (total.value > 30) { | |
506 | + total.value = 30; | |
507 | + } | |
508 | + recommendList.value = resData.value.data.records; | |
509 | + maxPageRe.value = resData.value.data.pages; | |
510 | + // recommendImages.value = recommendList.value.slice(0, 10).map((item) => { | |
511 | + recommendImages.value = Array.from({ length: 4 }).map((_, index) => { | |
512 | + const item = recommendList.value[index]; | |
513 | + if (!item) { | |
514 | + return null; | |
515 | + } | |
516 | + // 检查 productimageliststore 是否为字符串格式,如果是,则尝试解析 | |
517 | + if (typeof item.productimageliststore === "string") { | |
518 | + try { | |
519 | + item.productimageliststore = JSON.parse(item.productimageliststore); | |
520 | + } catch (error) { | |
521 | + item.productimageliststore = []; // 解析失败时,设置为空数组 | |
522 | + } | |
523 | + } | |
524 | + const ree = (item.productimageliststore = item?.productimageliststore.map( | |
525 | + (productItem: ProductImage) => ({ | |
526 | + ...productItem, | |
527 | + // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}`, | |
528 | + url: `https://www.canrud.com/api/show/image?fileKey=${productItem.fileKey}&psize=p256`, | |
529 | + name: item.name, | |
530 | + productUrl: `https://www.canrud.com/products/detail/${item.id}`, | |
531 | + }) | |
532 | + )); | |
533 | + return ree; | |
534 | + }); | |
535 | + loading.value = false; | |
536 | +}; | |
537 | +const loadHotProducts = async () => { | |
538 | + const pageSizeHot = ref(5); | |
539 | + if (isOrNotMobile) { | |
540 | + pageSizeHot.value = 4; | |
541 | + } | |
542 | + hotLoading.value = true; | |
543 | + | |
544 | + let { data: hotProducts } = await useAsyncData( | |
545 | + "hotProducts", | |
546 | + () => | |
547 | + $fetch("/shop/product/hotProducts", { | |
548 | + method: "GET", | |
549 | + params: { | |
550 | + pageNo: currentIndexHot.value, | |
551 | + pageSize: pageSizeHot.value, | |
552 | + }, | |
553 | + }), | |
554 | + { | |
555 | + server: true, // 仅在服务器端获取数据 | |
556 | + } | |
557 | + ); | |
558 | + hotTotal.value = hotProducts.value.data.total; | |
559 | + recommendListHot.value = hotProducts.value.data.records; | |
560 | + maxPage.value = hotProducts.value.data.pages; | |
561 | + // recommendImages.value = recommendList.value.slice(0, 10).map((item) => { | |
562 | + recommendImagesHot.value = Array.from({ length: pageSizeHot.value }).map( | |
563 | + (_, index) => { | |
564 | + const item = recommendListHot.value[index]; | |
565 | + if (!item) { | |
566 | + return null; | |
567 | + } | |
568 | + // 检查 productimageliststore 是否为字符串格式,如果是,则尝试解析 | |
569 | + if (typeof item.productimageliststore === "string") { | |
570 | + try { | |
571 | + item.productimageliststore = JSON.parse(item.productimageliststore); | |
572 | + } catch (error) { | |
573 | + item.productimageliststore = []; // 解析失败时,设置为空数组 | |
574 | + } | |
575 | + } | |
576 | + const ree = (item.productimageliststore = item?.productimageliststore.map( | |
577 | + (productItem: ProductImage) => ({ | |
578 | + ...productItem, | |
579 | + // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}`, | |
580 | + url: `https://www.canrud.com/api/show/image?fileKey=${productItem.fileKey}&psize=p256`, | |
581 | + name: item.name, | |
582 | + productUrl: `https://www.canrud.com/products/detail/${item.id}`, | |
583 | + }) | |
584 | + )); | |
585 | + return ree; | |
586 | + } | |
587 | + ); | |
588 | + hotLoading.value = false; | |
589 | +}; | |
590 | + | |
591 | +const toggleRowMobile = (value: number) => { | |
592 | + currentIndex.value = value; | |
593 | +}; | |
594 | +const toggleRowMobileHot = (value: number) => { | |
595 | + currentIndexHot.value = value; | |
596 | +}; | |
597 | + | |
598 | +watch(currentIndexHot, (newIndex) => { | |
599 | + loadHotProducts(); // Call loadHotProducts when currentIndex changes | |
600 | +}); | |
601 | +watch(currentIndex, (newIndex) => { | |
602 | + loadProducts(); // Call loadHotProducts when currentIndex changes | |
603 | +}); | |
604 | +// Initial load of hot products | |
605 | +await loadHotProducts(); // Load hot products the first time | |
606 | +await loadProducts(); // Load hot products the first time | |
315 | 607 | |
316 | 608 | interface BreadcrumbItem { |
317 | 609 | title: string; // 标题 |
... | ... | @@ -332,12 +624,12 @@ const items = ref<BreadcrumbItem[]>([ |
332 | 624 | href: href1.value, |
333 | 625 | }, |
334 | 626 | { |
335 | - title: "DEVICE TYPE", | |
627 | + title: "Not specified", | |
336 | 628 | disabled: false, |
337 | 629 | href: href2.value, |
338 | 630 | }, |
339 | 631 | { |
340 | - title: "FUNCTION", | |
632 | + title: "Not specified", | |
341 | 633 | disabled: false, |
342 | 634 | href: "/products", |
343 | 635 | }, |
... | ... | @@ -424,42 +716,11 @@ watchEffect(() => { |
424 | 716 | href2.value + "&function=" + info.productCrumbsVO.function; |
425 | 717 | } |
426 | 718 | } |
427 | - // recommendList.value = resData.value.data.records; | |
428 | - | |
429 | - // // recommendImages.value = recommendList.value.slice(0, 10).map((item) => { | |
430 | - // recommendImages.value = Array.from({ length: 10 }).map((_, index) => { | |
431 | - // const item = recommendList.value[index]; | |
432 | - // if (!item) { | |
433 | - // return null; | |
434 | - // } | |
435 | - // // 检查 productimageliststore 是否为字符串格式,如果是,则尝试解析 | |
436 | - // if (typeof item.productimageliststore === "string") { | |
437 | - // try { | |
438 | - // item.productimageliststore = JSON.parse(item.productimageliststore); | |
439 | - // } catch (error) { | |
440 | - // item.productimageliststore = []; // 解析失败时,设置为空数组 | |
441 | - // } | |
442 | - // } | |
443 | - // const ree = (item.productimageliststore = item?.productimageliststore.map( | |
444 | - // (productItem: ProductImage) => ({ | |
445 | - // ...productItem, | |
446 | - // // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}`, | |
447 | - // url: `/api/show/image?fileKey=${productItem.fileKey}&psize=p512`, | |
448 | - // name: item.name, | |
449 | - // productUrl: `http://www.canrud.com/products/detail/${item.id}`, | |
450 | - // }) | |
451 | - // )); | |
452 | - // return ree; | |
453 | - // }); | |
454 | 719 | }); |
455 | -function getTitle(network) { | |
456 | - const titles = { | |
457 | - facebook: "Facebook", | |
458 | - twitter: "Twitter", | |
459 | - linkedin: "LinkedIn", | |
460 | - }; | |
461 | - return titles[network] || "Share"; | |
462 | -} | |
720 | +const length = computed(() => (total.value ? Math.ceil(total.value / 4) : 0)); | |
721 | +const hotLength = computed(() => | |
722 | + hotTotal.value ? Math.ceil(hotTotal.value / 4) : 0 | |
723 | +); | |
463 | 724 | </script> |
464 | 725 | |
465 | 726 | <style lang="scss" scoped> |
... | ... | @@ -470,6 +731,13 @@ function getTitle(network) { |
470 | 731 | .active { |
471 | 732 | background-color: #fff; |
472 | 733 | } |
734 | +// .tabs { | |
735 | +// border-bottom: 2px solid #1f88e5; | |
736 | +// } | |
737 | + | |
738 | +// .active { | |
739 | +// background-color: #1086e8; | |
740 | +// } | |
473 | 741 | |
474 | 742 | .tw-sticky { |
475 | 743 | position: sticky; |
... | ... | @@ -512,4 +780,7 @@ function getTitle(network) { |
512 | 780 | font-size: 1rem; |
513 | 781 | font-weight: bold; |
514 | 782 | } |
783 | +.v-card { | |
784 | + transition: all 0.3s ease-in-out; | |
785 | +} | |
515 | 786 | </style> | ... | ... |
components/ProductDetail.vue
... | ... | @@ -262,7 +262,7 @@ |
262 | 262 | </div> |
263 | 263 | </div> |
264 | 264 | <div style="display: flex"> |
265 | - <div class="tw-pb-[64px]" style="width: 70%; margin-right: 10px;"> | |
265 | + <div class="tw-pb-[64px]" style="width: 70%; margin-right: 10px"> | |
266 | 266 | <v-tabs |
267 | 267 | class="tabs" |
268 | 268 | v-model="tab" |
... | ... | @@ -275,7 +275,11 @@ |
275 | 275 | <v-tab :value="2">Specification</v-tab> |
276 | 276 | <!-- <v-tab :value="3">商品百科</v-tab> --> |
277 | 277 | </v-tabs> |
278 | - <v-window v-model="tab" class="tw-p-[24px]" style="min-height: 500px; height: auto;" > | |
278 | + <v-window | |
279 | + v-model="tab" | |
280 | + class="tw-p-[24px]" | |
281 | + style="min-height: 500px; height: auto" | |
282 | + > | |
279 | 283 | <v-window-item key="1" :value="1"> |
280 | 284 | <div v-if="info.advantage" class="tw-mb-[24px]"> |
281 | 285 | <div class="text-h6">Advantage</div> |
... | ... | @@ -559,17 +563,17 @@ const loadHotProducts = async () => { |
559 | 563 | }); |
560 | 564 | }; |
561 | 565 | const toggleRowLeft = () => { |
562 | - if (currentIndex.value !== 1) { | |
563 | - currentIndex.value--; | |
564 | - } else if (currentIndex.value == 1) { | |
565 | - currentIndex.value = maxPage.value; | |
566 | + if (currentIndexHot.value !== 1) { | |
567 | + currentIndexHot.value--; | |
568 | + } else if (currentIndexHot.value == 1) { | |
569 | + currentIndexHot.value = maxPage.value; | |
566 | 570 | } |
567 | 571 | }; |
568 | 572 | const toggleRowRight = () => { |
569 | - if (currentIndex.value < maxPage.value) { | |
570 | - currentIndex.value++; | |
571 | - } else if (currentIndex.value == maxPage.value) { | |
572 | - currentIndex.value = 1; | |
573 | + if (currentIndexHot.value < maxPage.value) { | |
574 | + currentIndexHot.value++; | |
575 | + } else if (currentIndexHot.value == maxPage.value) { | |
576 | + currentIndexHot.value = 1; | |
573 | 577 | } |
574 | 578 | }; |
575 | 579 | watch(currentIndexHot, (newIndex) => { | ... | ... |
deploy/prod2.sh
pages/index.vue
... | ... | @@ -144,6 +144,76 @@ |
144 | 144 | /> |
145 | 145 | </div> |
146 | 146 | </div> |
147 | + <div style="padding-left: 16px; padding-right: 16px; padding-bottom: 30px"> | |
148 | + <v-tabs | |
149 | + class="tabs2" | |
150 | + ref="tabs2" | |
151 | + v-model="tabRecom" | |
152 | + style="margin-top: 25px; margin-bottom: 20px; width: 100%" | |
153 | + color="white" | |
154 | + bg-color="#eeeeee" | |
155 | + slider-color="blue-lighten-1" | |
156 | + selected-class="active" | |
157 | + v-if="isMobile()" | |
158 | + > | |
159 | + <v-tab :value="1">Best Sellers</v-tab> | |
160 | + <!-- <v-tab :value="3">商品百科</v-tab> --> | |
161 | + </v-tabs> | |
162 | + <div class="tw-text-center" v-if="hotLoading && isMobile()"> | |
163 | + <v-progress-circular | |
164 | + color="blue-lighten-2" | |
165 | + indeterminate | |
166 | + size="64" | |
167 | + class="tw-m-auto" | |
168 | + ></v-progress-circular> | |
169 | + </div> | |
170 | + <v-item-group multiple v-if="isMobile()"> | |
171 | + <v-row v-if="!hotLoading"> | |
172 | + <v-col | |
173 | + v-for="(item, i) in recommendImages" | |
174 | + :key="i" | |
175 | + cols="6" | |
176 | + lg="3" | |
177 | + md="4" | |
178 | + sm="6" | |
179 | + > | |
180 | + <div v-if="item !== null"> | |
181 | + <v-card :elevation="4" class="mx-auto" :href="item[0].productUrl"> | |
182 | + <!-- 设置 eager 属性,确保图片直接加载 --> | |
183 | + <v-img | |
184 | + :src="item[0].url" | |
185 | + :alt="item[0].name" | |
186 | + eager | |
187 | + class="d-block" | |
188 | + /> | |
189 | + <v-card-text class="tw-text-left font-weight-medium title"> | |
190 | + <h4>{{ item[0].name }}</h4> | |
191 | + </v-card-text> | |
192 | + </v-card> | |
193 | + </div> | |
194 | + </v-col> | |
195 | + </v-row> | |
196 | + <!-- <div | |
197 | + v-if="!hotTotal" | |
198 | + class="text-medium-emphasis text-body-1 tw-text-center tw-m-[64px]" | |
199 | + > | |
200 | + no data | |
201 | + </div> --> | |
202 | + </v-item-group> | |
203 | + <v-row v-if="isMobile()"> | |
204 | + <v-col> | |
205 | + <v-pagination | |
206 | + :size="isMobile() ? 'small' : 'default'" | |
207 | + v-if="hotTotal" | |
208 | + v-model="currentIndex" | |
209 | + @update:modelValue="toggleRowMobile" | |
210 | + :length="hotLength" | |
211 | + rounded="0" | |
212 | + class="tw-float-right tw-mt-[32px]" | |
213 | + total-visible="5" | |
214 | + ></v-pagination></v-col | |
215 | + ></v-row> | |
216 | + </div> | |
147 | 217 | </template> |
148 | 218 | |
149 | 219 | <script setup lang="ts"> |
... | ... | @@ -158,8 +228,16 @@ const tabRecom = ref(); |
158 | 228 | const recommendList = ref(); |
159 | 229 | const recommendImages = ref(); |
160 | 230 | const currentIndex = ref(1); |
161 | -const flag = ref(false); | |
231 | +const hotLoading = ref(false); | |
232 | +const hotTotal = ref(10); | |
233 | +const isOrNotMobile = isMobile(); | |
162 | 234 | const loadHotProducts = async () => { |
235 | + const pageSize = ref(5); | |
236 | + if (isOrNotMobile) { | |
237 | + pageSize.value = 4; | |
238 | + } | |
239 | + hotLoading.value = true; | |
240 | + | |
163 | 241 | let { data: hotProducts } = await useAsyncData( |
164 | 242 | "hotProducts", |
165 | 243 | () => |
... | ... | @@ -167,41 +245,46 @@ const loadHotProducts = async () => { |
167 | 245 | method: "GET", |
168 | 246 | params: { |
169 | 247 | pageNo: currentIndex.value, |
170 | - pageSize: 5, | |
248 | + pageSize: pageSize.value, | |
171 | 249 | }, |
172 | 250 | }), |
173 | 251 | { |
174 | 252 | server: true, // 仅在服务器端获取数据 |
175 | 253 | } |
176 | 254 | ); |
255 | + hotTotal.value = hotProducts.value.data.total; | |
177 | 256 | recommendList.value = hotProducts.value.data.records; |
178 | 257 | maxPage.value = hotProducts.value.data.pages; |
179 | 258 | // 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 = []; // 解析失败时,设置为空数组 | |
259 | + recommendImages.value = Array.from({ length: pageSize.value }).map( | |
260 | + (_, index) => { | |
261 | + const item = recommendList.value[index]; | |
262 | + if (!item) { | |
263 | + return null; | |
191 | 264 | } |
265 | + // 检查 productimageliststore 是否为字符串格式,如果是,则尝试解析 | |
266 | + if (typeof item.productimageliststore === "string") { | |
267 | + try { | |
268 | + item.productimageliststore = JSON.parse(item.productimageliststore); | |
269 | + } catch (error) { | |
270 | + item.productimageliststore = []; // 解析失败时,设置为空数组 | |
271 | + } | |
272 | + } | |
273 | + const ree = (item.productimageliststore = item?.productimageliststore.map( | |
274 | + (productItem: ProductImage) => ({ | |
275 | + ...productItem, | |
276 | + // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}`, | |
277 | + url: `https://www.canrud.com/api/show/image?fileKey=${productItem.fileKey}&psize=p256`, | |
278 | + name: item.name, | |
279 | + productUrl: `https://www.canrud.com/products/detail/${item.id}`, | |
280 | + }) | |
281 | + )); | |
282 | + return ree; | |
192 | 283 | } |
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 | - }); | |
284 | + ); | |
285 | + hotLoading.value = false; | |
204 | 286 | }; |
287 | + | |
205 | 288 | const toggleRowLeft = () => { |
206 | 289 | if (currentIndex.value !== 1) { |
207 | 290 | currentIndex.value--; |
... | ... | @@ -216,10 +299,13 @@ const toggleRowRight = () => { |
216 | 299 | currentIndex.value = 1; |
217 | 300 | } |
218 | 301 | }; |
302 | +const toggleRowMobile = (value: number) => { | |
303 | + currentIndex.value = value; | |
304 | +}; | |
305 | + | |
219 | 306 | watch(currentIndex, (newIndex) => { |
220 | 307 | loadHotProducts(); // Call loadHotProducts when currentIndex changes |
221 | 308 | }); |
222 | - | |
223 | 309 | // Initial load of hot products |
224 | 310 | await loadHotProducts(); // Load hot products the first time |
225 | 311 | |
... | ... | @@ -354,6 +440,9 @@ const packs = [ |
354 | 440 | href: "/pack", |
355 | 441 | }, |
356 | 442 | ]; |
443 | +const hotLength = computed(() => | |
444 | + hotTotal.value ? Math.ceil(hotTotal.value / 4) : 0 | |
445 | +); | |
357 | 446 | </script> |
358 | 447 | <style scoped> |
359 | 448 | @media screen and (min-width: 1537px) { |
... | ... | @@ -499,4 +588,11 @@ button .recommendButton { |
499 | 588 | .recommend-img-right:hover { |
500 | 589 | cursor: pointer; |
501 | 590 | } |
591 | +.image-grid { | |
592 | + padding: 16px; | |
593 | +} | |
594 | + | |
595 | +.v-card { | |
596 | + transition: all 0.3s ease-in-out; | |
597 | +} | |
502 | 598 | </style> | ... | ... |
pages/products/detail/[id]/index.vue
1 | 1 | <template> |
2 | - <MobileProductDetail v-if="isMobile()" :info="info" /> | |
2 | + <MobileProductDetail v-if="isMobile()" :info="info" :res="resData" /> | |
3 | 3 | <ProductDetail v-else :info="info" /> |
4 | 4 | </template> |
5 | 5 | |
... | ... | @@ -10,6 +10,7 @@ import MobileProductDetail from "~/components/MobileProductDetail.vue"; |
10 | 10 | import type { Product, ProductImage } from "~/type"; |
11 | 11 | import { useRoute, useRouter } from "vue-router"; |
12 | 12 | |
13 | +const productStore = useProductListStore(); | |
13 | 14 | const route = useRoute(); |
14 | 15 | const router = useRouter(); |
15 | 16 | const info = ref<Partial<Product>>({ | ... | ... |
pages/products/index.vue
... | ... | @@ -130,6 +130,74 @@ |
130 | 130 | /> |
131 | 131 | </div> |
132 | 132 | </div> |
133 | + <v-tabs | |
134 | + class="tabs2" | |
135 | + ref="tabs2" | |
136 | + v-model="tabRecom" | |
137 | + style="margin-top: 25px; margin-bottom: 20px" | |
138 | + color="white" | |
139 | + bg-color="#eeeeee" | |
140 | + slider-color="blue-lighten-1" | |
141 | + selected-class="active" | |
142 | + v-if="isMobile()" | |
143 | + > | |
144 | + <v-tab :value="1">Best Sellers</v-tab> | |
145 | + <!-- <v-tab :value="3">商品百科</v-tab> --> | |
146 | + </v-tabs> | |
147 | + <div class="tw-text-center" v-if="hotLoading && isMobile()"> | |
148 | + <v-progress-circular | |
149 | + color="blue-lighten-2" | |
150 | + indeterminate | |
151 | + size="64" | |
152 | + class="tw-m-auto" | |
153 | + ></v-progress-circular> | |
154 | + </div> | |
155 | + <v-item-group multiple v-if="isMobile()"> | |
156 | + <v-row v-if="!hotLoading"> | |
157 | + <v-col | |
158 | + v-for="(item, i) in recommendImages" | |
159 | + :key="i" | |
160 | + cols="6" | |
161 | + lg="3" | |
162 | + md="4" | |
163 | + sm="6" | |
164 | + > | |
165 | + <div v-if="item !== null"> | |
166 | + <v-card :elevation="4" class="mx-auto" :href="item[0].productUrl"> | |
167 | + <!-- 设置 eager 属性,确保图片直接加载 --> | |
168 | + <v-img | |
169 | + :src="item[0].url" | |
170 | + :alt="item[0].name" | |
171 | + eager | |
172 | + class="d-block" | |
173 | + /> | |
174 | + <v-card-text class="tw-text-left font-weight-medium title"> | |
175 | + <h4>{{ item[0].name }}</h4> | |
176 | + </v-card-text> | |
177 | + </v-card> | |
178 | + </div> | |
179 | + </v-col> | |
180 | + </v-row> | |
181 | + <!-- <div | |
182 | + v-if="!hotTotal" | |
183 | + class="text-medium-emphasis text-body-1 tw-text-center tw-m-[64px]" | |
184 | + > | |
185 | + no data | |
186 | + </div> --> | |
187 | + </v-item-group> | |
188 | + <v-row v-if="isMobile()"> | |
189 | + <v-col> | |
190 | + <v-pagination | |
191 | + :size="isMobile() ? 'small' : 'default'" | |
192 | + v-if="hotTotal" | |
193 | + v-model="currentIndex" | |
194 | + @update:modelValue="toggleRowMobile" | |
195 | + :length="hotLength" | |
196 | + rounded="0" | |
197 | + class="tw-float-right tw-mt-[32px]" | |
198 | + total-visible="5" | |
199 | + ></v-pagination></v-col | |
200 | + ></v-row> | |
133 | 201 | </v-container> |
134 | 202 | </div> |
135 | 203 | </template> |
... | ... | @@ -146,6 +214,7 @@ import { watchEffect, computed, ref } from "vue"; |
146 | 214 | const productStore = useProductListStore(); |
147 | 215 | const categoryStore = useCategoryStore(); |
148 | 216 | const loading = ref(false); |
217 | +const hotLoading = ref(false); | |
149 | 218 | const route = useRoute(); // 获取路由信息 |
150 | 219 | const router = useRouter(); // 获取路由信息 |
151 | 220 | const title = ref(""); |
... | ... | @@ -155,8 +224,16 @@ const tabRecom = ref(); |
155 | 224 | const recommendList = ref(); |
156 | 225 | const recommendImages = ref(); |
157 | 226 | const currentIndex = ref(1); |
227 | +const hotTotal = ref(10); | |
158 | 228 | const isOrNotMobile = isMobile(); |
229 | + | |
159 | 230 | const loadHotProducts = async () => { |
231 | + const pageSize = ref(5); | |
232 | + if (isOrNotMobile) { | |
233 | + pageSize.value = 4; | |
234 | + } | |
235 | + hotLoading.value = true; | |
236 | + | |
160 | 237 | let { data: hotProducts } = await useAsyncData( |
161 | 238 | "hotProducts", |
162 | 239 | () => |
... | ... | @@ -164,41 +241,46 @@ const loadHotProducts = async () => { |
164 | 241 | method: "GET", |
165 | 242 | params: { |
166 | 243 | pageNo: currentIndex.value, |
167 | - pageSize: 5, | |
244 | + pageSize: pageSize.value, | |
168 | 245 | }, |
169 | 246 | }), |
170 | 247 | { |
171 | 248 | server: true, // 仅在服务器端获取数据 |
172 | 249 | } |
173 | 250 | ); |
251 | + hotTotal.value = hotProducts.value.data.total; | |
174 | 252 | recommendList.value = hotProducts.value.data.records; |
175 | 253 | maxPage.value = hotProducts.value.data.pages; |
176 | 254 | // 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 = []; // 解析失败时,设置为空数组 | |
255 | + recommendImages.value = Array.from({ length: pageSize.value }).map( | |
256 | + (_, index) => { | |
257 | + const item = recommendList.value[index]; | |
258 | + if (!item) { | |
259 | + return null; | |
188 | 260 | } |
261 | + // 检查 productimageliststore 是否为字符串格式,如果是,则尝试解析 | |
262 | + if (typeof item.productimageliststore === "string") { | |
263 | + try { | |
264 | + item.productimageliststore = JSON.parse(item.productimageliststore); | |
265 | + } catch (error) { | |
266 | + item.productimageliststore = []; // 解析失败时,设置为空数组 | |
267 | + } | |
268 | + } | |
269 | + const ree = (item.productimageliststore = item?.productimageliststore.map( | |
270 | + (productItem: ProductImage) => ({ | |
271 | + ...productItem, | |
272 | + // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}`, | |
273 | + url: `https://www.canrud.com/api/show/image?fileKey=${productItem.fileKey}&psize=p256`, | |
274 | + name: item.name, | |
275 | + productUrl: `https://www.canrud.com/products/detail/${item.id}`, | |
276 | + }) | |
277 | + )); | |
278 | + return ree; | |
189 | 279 | } |
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 | - }); | |
280 | + ); | |
281 | + hotLoading.value = false; | |
201 | 282 | }; |
283 | + | |
202 | 284 | const toggleRowLeft = () => { |
203 | 285 | if (currentIndex.value !== 1) { |
204 | 286 | currentIndex.value--; |
... | ... | @@ -213,10 +295,13 @@ const toggleRowRight = () => { |
213 | 295 | currentIndex.value = 1; |
214 | 296 | } |
215 | 297 | }; |
298 | +const toggleRowMobile = (value: number) => { | |
299 | + currentIndex.value = value; | |
300 | +}; | |
301 | + | |
216 | 302 | watch(currentIndex, (newIndex) => { |
217 | 303 | loadHotProducts(); // Call loadHotProducts when currentIndex changes |
218 | 304 | }); |
219 | - | |
220 | 305 | // Initial load of hot products |
221 | 306 | await loadHotProducts(); // Load hot products the first time |
222 | 307 | |
... | ... | @@ -404,6 +489,9 @@ watchEffect(async () => { |
404 | 489 | const length = computed(() => |
405 | 490 | productStore.total ? Math.ceil(productStore.total / productStore.pageSize) : 0 |
406 | 491 | ); |
492 | +const hotLength = computed(() => | |
493 | + hotTotal.value ? Math.ceil(hotTotal.value / 4) : 0 | |
494 | +); | |
407 | 495 | </script> |
408 | 496 | |
409 | 497 | <style scoped> |
... | ... | @@ -529,4 +617,8 @@ button .recommendButton { |
529 | 617 | .recommend-img-right:hover { |
530 | 618 | cursor: pointer; |
531 | 619 | } |
620 | + | |
621 | +.v-card { | |
622 | + transition: all 0.3s ease-in-out; | |
623 | +} | |
532 | 624 | </style> | ... | ... |