Преглед изворни кода

refactor: 更新内容配置和首页展示逻辑

- 修改内容配置文件,统一字符串引号格式,增加推荐字段和推荐索引字段。
- 更新首页展示逻辑,调整产品展示部分,优化推荐产品的显示方式。
- 修改多语言内容,更新使用方法和标题的翻译,提升用户体验。
master
lizhuang пре 1 месец
родитељ
комит
7058c127fa
4 измењених фајлова са 207 додато и 557 уклоњено
  1. 25
    23
      content.config.ts
  2. 2
    2
      i18n/locales/zh.ts
  3. 173
    529
      pages/index.vue
  4. 7
    3
      scripts/generateRoutes.js

+ 25
- 23
content.config.ts Прегледај датотеку

@@ -1,8 +1,8 @@
import { defineCollection, z } from '@nuxt/content'
import { defineCollection, z } from "@nuxt/content";

// 定义 Category 集合
const categoryCollection = defineCollection({
type: 'page',
type: "page",
schema: z.object({
title: z.string(),
description: z.string().optional(),
@@ -14,14 +14,14 @@ const categoryCollection = defineCollection({
tags: z.array(z.string()),
audiences: z.array(z.string()),
body: z.object({
value: z.string()
})
})
})
value: z.string(),
}),
}),
});

// 定义 Product 集合
const productCollection = defineCollection({
type: 'page',
type: "page",
schema: z.object({
title: z.string(),
name: z.string(),
@@ -37,15 +37,17 @@ const productCollection = defineCollection({
summary: z.string(),
capacities: z.array(z.string()),
sort: z.number(),
recommend: z.boolean(),
recommendIndex: z.number(),
body: z.object({
value: z.string()
})
})
})
value: z.string(),
}),
}),
});

// 定义 Usage 集合
const usageCollection = defineCollection({
type: 'page',
type: "page",
schema: z.object({
title: z.string(),
description: z.string(),
@@ -53,13 +55,13 @@ const usageCollection = defineCollection({
category: z.string(),
products: z.array(z.string()),
summary: z.string(),
sort: z.number()
})
})
sort: z.number(),
}),
});

// 定义 FAQ 集合
const faqCollection = defineCollection({
type: 'page',
type: "page",
schema: z.object({
title: z.string(),
description: z.string().optional(),
@@ -67,19 +69,19 @@ const faqCollection = defineCollection({
category: z.string(),
question: z.string(),
answer: z.string(),
sort: z.number()
})
})
sort: z.number(),
}),
});

// 定义默认集合
const defaultCollection = defineCollection({
type: 'page'
})
type: "page",
});

export default {
categories: categoryCollection,
products: productCollection,
usages: usageCollection,
faq: faqCollection,
content: defaultCollection
}
content: defaultCollection,
};

+ 2
- 2
i18n/locales/zh.ts Прегледај датотеку

@@ -137,8 +137,8 @@ export default {
viewDetails: "查看详情",
consultation: "欢迎进行产品咨询,我们将在第一时间回复您",
consultation_button: "联系我们",
usage: "使用方法选择存储",
usage_title: "根据用途选择存储产品。",
usage: "推荐商品",
usage_title: "精心挑选的高品质产品。",
support: "支持",
support_description: "拥有10年以上经验的团队提供专业技术支持",
development: "独立开发和制造",

+ 173
- 529
pages/index.vue Прегледај датотеку

@@ -35,7 +35,7 @@
:parallax="true"
class="h-[320px] sm:h-[320px] md:h-[768px] lg:h-[900px] swiper-container-1"
>
<SwiperSlide>
<SwiperSlide>
<div
class="max-w-screen-2xl mx-auto h-full relative"
:style="{
@@ -97,8 +97,8 @@
</div>
</div>
</div>
</SwiperSlide>
<SwiperSlide v-if="!isMobile">
</SwiperSlide>
<SwiperSlide v-if="!isMobile">
<div class="max-w-screen-2xl mx-auto h-full relative">
<video
:src="homeA3Webp"
@@ -217,7 +217,6 @@
</div>
</div>
</SwiperSlide>

<div class="max-w-screen-2xl mx-auto relative">
<div
@@ -227,158 +226,149 @@
</Swiper>
</section>

<!-- 按分类栏目展示 -->
<!-- 按推荐产品展示 -->
<section class="max-w-full mb-12 md:mb-32 xl:px-8 lg:px-6 md:px-4 px-4">
<div class="max-w-screen-2xl mx-auto relative">
<div
class="justify-center text-cyan-400 text-base font-normal leading-tight mb-4"
>
{{ t("home.useCategoryTitle") }}
{{ t("products.usage") }}
</div>
<div
class="justify-center text-white font-normal mb-8 md:mb-16 text-xl sm:text-2xl md:text-4xl lg:text-6xl"
>
{{ t("home.useCategoryDescription") }}
{{ t("products.usage_title") }}
</div>
</div>
<div class="max-w-screen-2xl mx-auto relative">
<!-- 企业产品 -->
<div
class="flex w-full relative mb-10 xl:mb-40 gap-10 flex-col xl:flex-row"
>
<div
class="select-none relative w-full xl:w-[65%] h-[280px] md:h-[320px] xl:h-[520px] animate-gradient-bg [background:linear-gradient(180deg,#444B55_0%,#98A3B4_95%)] from-zinc-900 to-zinc-800"
<div class="max-w-screen-2xl mx-auto">
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 md:gap-10 w-full">
<nuxt-link
v-for="(recommend, index) in recommendList"
:key="index"
:to="`${homepagePath}products/${recommend.name}`"
class="w-full h-full"
>
<img
src="@/assets/images/business.webp"
alt="business"
class="max-h-full pl-0 md:pl-4 pt-0 md:pt-4 relative z-10"
/>
<span
class="text-transparent bg-clip-text bg-gradient-to-r from-[rgba(255,255,255,0.05)] to-[rgba(255,255,255,0.08)] absolute hidden xl:block z-0 top-[35%] left-[62%] translate-y-[-50%] translate-x-[-50%] text-[170px] font-bold select-none"
>Business</span
>

<div
class="w-full absolute xl:hidden top-0 left-0 right-0 bottom-0 z-10 flex flex-col justify-center items-end p-4 md:p-8"
class="w-full h-full bg-zinc-900 rounded-2xl p-4 flex flex-col items-center justify-start relative overflow-hidden group hover:bg-zinc-800 transition-all duration-300 hover:shadow-lg hover:shadow-cyan-400/10"
>
<!-- 图片容器 -->
<div
class="text-white text-4xl md:text-7xl font-bold mb-2 select-none"
class="w-full aspect-square relative transition-all duration-300 overflow-hidden rounded-lg"
>
{{ t("home.business.title") }}
</div>
<div class="justify-center text-white text-xl font-normal mb-8">
{{ t("home.business.description") }}
</div>
<nuxt-link
:to="`${homepagePath}/products?audiences=1`"
class="h-14 px-8 py-3.5 bg-white/10 hover:bg-white/15 rounded-[10px] outline outline-1 outline-white/20 backdrop-blur-md inline-flex flex-col justify-start items-start gap-3.5"
>
<div class="inline-flex justify-start items-center gap-2.5">
{{ t("home.business.view_details") }}
<i class="icon-arrow-right text-xs ml-2"></i>
<img
:src="recommend?.image"
class="w-full h-full object-cover transition-all duration-500 group-hover:scale-110"
loading="lazy"
alt="Product image"
/>
<div
class="absolute inset-0 bg-gradient-to-t from-black/60 via-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"
></div>
<!-- 添加查看详情按钮 -->
<div
class="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-all duration-300 transform translate-y-4 group-hover:translate-y-0"
>
<div
class="px-4 py-2 bg-cyan-400/20 backdrop-blur-sm rounded-full text-white text-sm font-medium border border-cyan-400/30 transform transition-transform duration-300 group-hover:scale-105"
>
{{ t("products.view_details") }}
</div>
</div>
</nuxt-link>
</div>
</div>
<div class="relative hidden xl:flex flex-1 flex-col gap-10">
<div class="w-full">
<div class="text-white text-6xl mb-4 mt-4 select-none">
{{ t("home.business.title") }}
</div>
<div class="justify-center text-white text-xl font-normal mb-8">
{{ t("home.business.description") }}
</div>
<nuxt-link
:to="`${homepagePath}/products?audiences=1`"
class="h-14 px-8 py-3.5 bg-white/10 hover:bg-white/15 rounded-[10px] outline outline-1 outline-white/20 backdrop-blur-md inline-flex flex-col justify-start items-start gap-3.5"
>
<div class="inline-flex justify-start items-center gap-2.5">
{{ t("home.business.view_details") }}
<i class="icon-arrow-right text-xs ml-2"></i>
</div>
</nuxt-link>
</div>
</div>

<div
class="bg-zinc-900/60 backdrop-blur-[120px] right-0 bottom-[-30%] static xl:absolute"
>
<div class="w-full h-full grid grid-cols-2">
<nuxt-link
v-for="category in categoryList"
:key="category.id"
class="flex flex-col gap-4 items-center p-4 md:p-12 hover:bg-zinc-900/50 hover:scale-95 transition-all duration-300"
v-show="category.audiences === 1"
:to="`${category.link}`"
<!-- 文字内容 -->
<div
class="w-full mt-4 min-h-[80px] transition-all duration-300 transform"
>
<img
class="w-[200px] h-[200px] object-contain"
:src="category.image"
/>
<div
class="text-center flex justify-center text-white text-base font-bold"
class="text-center text-white text-base font-bold leading-tight mb-2 line-clamp-2 group-hover:text-cyan-400 transition-colors duration-300"
>
{{ category.title }}
{{ recommend.title }}
</div>
<div
class="text-center text-white/50 text-sm max-w-[280px] line-clamp-2"
class="text-center text-zinc-400 text-sm font-normal leading-tight line-clamp-2 group-hover:text-zinc-300 transition-colors duration-300"
>
{{ category.description }}
{{ recommend.summary }}
</div>
</nuxt-link>
</div>
</div>
</div>
</nuxt-link>
</div>
</div>
</section>

<!-- 个人产品 -->
<!-- 按分类栏目展示 -->
<section class="max-w-full mb-12 md:mb-32 xl:px-8 lg:px-6 md:px-4 px-4">
<div class="max-w-screen-2xl mx-auto relative">
<div
class="flex w-full gap-8 relative mb-0 xl:mb-60 flex-col xl:flex-row"
class="justify-center text-cyan-400 text-base font-normal leading-tight mb-4"
>
<div
class="flex-1 hidden xl:flex flex-col gap-10 relative top-[-80px]"
>
<div class="w-full">
<div
class="justify-center text-white text-6xl whitespace-nowrap mb-4 mt-20 select-none"
>
{{ t("home.personal.title") }}
</div>
<div class="justify-center text-white text-xl font-normal mb-8">
{{ t("home.personal.description") }}
</div>
<nuxt-link
:to="`${homepagePath}/products?audiences=0`"
class="h-14 px-8 py-3.5 bg-white/10 hover:bg-white/15 rounded-[10px] outline outline-1 outline-white/20 backdrop-blur-md inline-flex flex-col justify-start items-start gap-3.5"
{{ t("home.useCategoryTitle") }}
</div>
<div
class="justify-center text-white font-normal mb-8 md:mb-16 text-xl sm:text-2xl md:text-4xl lg:text-6xl"
>
{{ t("home.useCategoryDescription") }}
</div>
</div>
<div class="max-w-screen-2xl mx-auto relative">
<div class="w-full grid grid-cols-1 md:grid-cols-2 gap-8">
<!-- 企业产品 -->
<div class="flex h-[240px] lg:h-[440px] xl:h-[540px] relative">
<div
class="select-none relative w-full animate-gradient-bg [background:linear-gradient(180deg,#444B55_0%,#98A3B4_95%)] from-zinc-900 to-zinc-800"
>
<img
src="@/assets/images/business.webp"
alt="business"
class="max-h-[90%] absolute bottom-0 left-0 z-10"
/>
<span
class="text-transparent bg-clip-text bg-gradient-to-r from-[rgba(255,255,255,0.05)] to-[rgba(255,255,255,0.08)] absolute hidden xl:block z-0 top-[35%] left-[50%] translate-y-[-50%] translate-x-[-50%] text-[170px] font-bold select-none"
>Business</span
>
<div class="inline-flex justify-start items-center gap-2.5">
{{ t("home.personal.view_details") }}
<i class="icon-arrow-right text-xs ml-2"></i>
</div>
<div
class="xl:flex flex-1 flex-col gap-10 absolute top-[10%] right-0"
>
<div class="w-full p-8">
<div class="text-white text-4xl mb-4 mt-4 select-none hidden lg:block">
{{ t("home.business.title") }}
</div>
</nuxt-link>
<div class="justify-center text-white text-xl font-normal mb-8">
{{ t("home.business.description") }}
</div>
<nuxt-link
:to="`${homepagePath}/products?audiences=1`"
class="h-14 px-8 py-3.5 whitespace-nowrap bg-white/10 hover:bg-white/15 rounded-[10px] outline outline-1 outline-white/20 backdrop-blur-md inline-flex flex-col justify-start items-start gap-3.5"
>
<div class="inline-flex justify-start items-center gap-2.5">
{{ t("home.business.view_details") }}
<i class="icon-arrow-right text-xs ml-2"></i>
</div>
</nuxt-link>
</div>
</div>
</div>

<div
class="w-full xl:w-[75%] h-[280px] md:h-[320px] xl:h-[480px] select-none relative z-0 flex justify-end items-end animate-gradient-bg [background:linear-gradient(180deg,#0086f4_0%,#88d5fa_80%)]"
>
<img
src="@/assets/images/personal.webp"
alt="personal"
class="pt-0 md:pt-20 max-h-full relative z-10"
/>
<span
class="absolute text-transparent bg-clip-text bg-gradient-to-r from-[rgba(255,255,255,0.08)] to-[rgba(255,255,255,0.05)] hidden xl:block z-0 top-[25%] left-[35%] translate-y-[-50%] translate-x-[-50%] text-[160px] font-bold select-none"
>Personal</span
<div class="flex h-[240px] lg:h-[440px] xl:h-[540px] relative">
<div
class="select-none overflow-hidden relative w-full animate-gradient-bg [background:linear-gradient(180deg,#0086f4_0%,#88d5fa_80%)] from-zinc-900 to-zinc-800"
>

<img
src="@/assets/images/personal.webp"
alt="personal"
class="max-h-[90%] absolute bottom-0 right-[-80px] z-10"
/>
<span
class="text-transparent bg-clip-text bg-gradient-to-r from-[rgba(255,255,255,0.05)] to-[rgba(255,255,255,0.08)] absolute hidden xl:block z-0 top-[35%] left-[50%] translate-y-[-50%] translate-x-[-50%] text-[170px] font-bold select-none"
>Personal</span
>
</div>
<div
class="flex xl:hidden flex-col gap-10 absolute top-0 left-0 right-0 bottom-0 z-10 justify-center p-4 md:p-8"
class="xl:flex flex-1 flex-col gap-10 absolute top-[10%] left-0"
>
<div class="w-full">
<div
class="justify-center text-white text-4xl md:text-7xl font-bold mb-2 select-none"
>
<div class="w-full p-8">
<div class="text-white text-4xl mb-4 mt-4 select-none hidden lg:block">
{{ t("home.personal.title") }}
</div>
<div class="justify-center text-white text-xl font-normal mb-8">
@@ -386,7 +376,7 @@
</div>
<nuxt-link
:to="`${homepagePath}/products?audiences=0`"
class="h-14 px-8 py-3.5 bg-white/10 hover:bg-white/15 rounded-[10px] outline outline-1 outline-white/20 backdrop-blur-md inline-flex flex-col justify-start items-start gap-3.5"
class="h-14 px-8 py-3.5 whitespace-nowrap bg-white/10 hover:bg-white/15 rounded-[10px] outline outline-1 outline-white/20 backdrop-blur-md inline-flex flex-col justify-start items-start gap-3.5"
>
<div class="inline-flex justify-start items-center gap-2.5">
{{ t("home.personal.view_details") }}
@@ -397,202 +387,77 @@
</div>
</div>

<!-- 个人产品 -->
<div
class="bg-zinc-900/60 backdrop-blur-[120px] static left-0 bottom-[-32%] xl:absolute z-2"
v-if="false"
class="flex w-full gap-8 relative mb-0 xl:mb-60 flex-col xl:flex-row"
>
<div class="w-full h-full grid grid-cols-2 lg:grid-cols-3">
<nuxt-link
v-for="category in categoryList"
:key="category.id"
class="flex flex-col gap-4 items-center py-4 px-4 md:py-12 md:px-6 hover:bg-zinc-900/50 hover:scale-95 transition-all duration-300"
v-show="category.audiences === 0"
:to="`${category.link}`"
>
<img
class="w-[200px] h-[200px] object-contain"
:src="category.image"
/>
<div
class="flex-1 hidden xl:flex flex-col gap-10 relative top-[-80px]"
>
<div class="w-full">
<div
class="text-center flex justify-center text-white text-base font-bold"
class="justify-center text-white text-6xl whitespace-nowrap mb-4 mt-20 select-none"
>
{{ category.title }}
{{ t("home.personal.title") }}
</div>
<div
class="text-center text-white/50 text-sm max-w-[280px] line-clamp-2"
>
{{ category.description }}
<div class="justify-center text-white text-xl font-normal mb-8">
{{ t("home.personal.description") }}
</div>
</nuxt-link>
<nuxt-link
:to="`${homepagePath}/products?audiences=0`"
class="h-14 px-8 py-3.5 bg-white/10 hover:bg-white/15 rounded-[10px] outline outline-1 outline-white/20 backdrop-blur-md inline-flex flex-col justify-start items-start gap-3.5"
>
<div class="inline-flex justify-start items-center gap-2.5">
{{ t("home.personal.view_details") }}
<i class="icon-arrow-right text-xs ml-2"></i>
</div>
</nuxt-link>
</div>
</div>
</div>
</div>
</div>
</section>

<!-- 按用途产品展示 -->
<section
v-if="false"
class="max-w-full mb-12 md:mb-32 xl:px-8 lg:px-6 md:px-4 px-4"
>
<div class="max-w-screen-2xl mx-auto relative">
<div
class="justify-center text-cyan-400 text-base font-normal leading-tight mb-4"
>
{{ t("products.usage") }}
</div>
<div
class="justify-center text-white font-normal mb-8 md:mb-16 text-xl sm:text-2xl md:text-4xl lg:text-6xl"
>
{{ t("products.usage_title") }}
</div>
</div>
<div class="max-w-screen-2xl mx-auto">
<div class="w-full mb-8">
<div class="flex flex-wrap items-center gap-2">
<div
v-for="(usage, index) in typedUsageList"
:key="usage.id"
class="cursor-pointer select-none px-4 sm:px-7 py-2 sm:py-3 rounded-full border border-zinc-700 text-white transition-all duration-300 relative group"
:class="{
'bg-cyan-400 border-zinc-900 pointer-events-none text-zinc-900':
activeIndex === index,
'hover:border-zinc-600': activeIndex !== index,
}"
@click="handleUsageClick(usage.id)"
class="w-full select-none relative z-0 flex justify-end items-end animate-gradient-bg [background:linear-gradient(180deg,#0086f4_0%,#88d5fa_80%)]"
>
<div
class="usage-name text-center text-xs sm:text-sm font-normal leading-tight md:text-base transition-colors duration-300 relative z-10"
>
{{ usage.name }}
<!-- 用途名称 -->
</div>
<div
class="absolute inset-0 rounded-full bg-cyan-400/20 scale-0 transition-transform duration-300 group-hover:scale-100"
></div>
</div>
<div class="flex items-center justify-center gap-4 ml-auto">
<div
class="swiper-button-prev-2 bg-zinc-700 w-8 h-8 sm:w-10 sm:h-10 rounded-full flex items-center justify-center cursor-pointer hover:bg-zinc-600 transition-colors duration-200"
<img
src="@/assets/images/personal.webp"
alt="personal"
class="pt-0 md:pt-20 max-h-full relative z-10"
/>
<span
class="absolute text-transparent bg-clip-text bg-gradient-to-r from-[rgba(255,255,255,0.08)] to-[rgba(255,255,255,0.05)] hidden xl:block z-0 top-[25%] left-[35%] translate-y-[-50%] translate-x-[-50%] text-[160px] font-bold select-none"
>Personal</span
>
<i
class="icon-arrow-left text-zinc-300 text-xs sm:text-sm font-normal"
></i>
</div>

<div
class="swiper-button-next-2 bg-zinc-700 w-8 h-8 sm:w-10 sm:h-10 rounded-full flex items-center justify-center cursor-pointer hover:bg-zinc-600 transition-colors duration-200"
class="flex xl:hidden flex-col gap-10 absolute top-0 left-0 right-0 bottom-0 z-10 justify-center p-4 md:p-8"
>
<i
class="icon-arrow-right text-zinc-300 text-xs sm:text-sm font-normal"
></i>
<div class="w-full">
<div
class="justify-center text-white text-4xl md:text-7xl font-bold mb-2 select-none"
>
{{ t("home.personal.title") }}
</div>
<div
class="justify-center text-white text-xl font-normal mb-8"
>
{{ t("home.personal.description") }}
</div>
<nuxt-link
:to="`${homepagePath}/products?audiences=0`"
class="h-14 px-8 py-3.5 bg-white/10 hover:bg-white/15 rounded-[10px] outline outline-1 outline-white/20 backdrop-blur-md inline-flex flex-col justify-start items-start gap-3.5"
>
<div class="inline-flex justify-start items-center gap-2.5">
{{ t("home.personal.view_details") }}
<i class="icon-arrow-right text-xs ml-2"></i>
</div>
</nuxt-link>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="max-w-screen-2xl mx-auto">
<div
class="w-full h-[420px] sm:h-[300px] md:h-[350px] lg:h-[360px] xl:h-[380px] 2xl:h-[460px] relative"
>
<TransitionGroup name="slide-fade" tag="div" class="relative h-full">
<div :key="activeUsageId" class="w-full h-full">
<Swiper
:modules="[Navigation]"
:spaceBetween="30"
:slidesPerView="4"
:breakpoints="{
320: { slidesPerView: 1, spaceBetween: 10 },
448: { slidesPerView: 2, spaceBetween: 10 },
640: { slidesPerView: 3, spaceBetween: 20 },
1024: { slidesPerView: 4, spaceBetween: 20 },
1280: { slidesPerView: 4, spaceBetween: 30 },
1536: { slidesPerView: 4, spaceBetween: 30 },
}"
:navigation="{
prevEl: '.swiper-button-prev-2',
nextEl: '.swiper-button-next-2',
}"
class="h-full"
>
<SwiperSlide
v-for="product in activeProducts"
:key="product.id"
class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4"
>
<div class="w-full h-full p-2">
<nuxt-link :to="product.link" class="block w-full h-full">
<div
class="w-full h-full bg-zinc-900 rounded-2xl p-4 flex flex-col items-center justify-start relative overflow-hidden group hover:bg-zinc-800 transition-all duration-300 hover:shadow-lg hover:shadow-cyan-400/10"
>
<!-- 图片加载占位 -->
<div
v-if="!isImageLoaded[product.id]"
class="absolute inset-0 bg-gradient-to-br from-zinc-800 via-zinc-700 to-zinc-800 animate-gradient"
>
<div
class="absolute inset-0 flex items-center justify-center"
>
<div
class="w-8 h-8 border-4 border-cyan-400/20 border-t-cyan-400 rounded-full animate-spin"
></div>
</div>
</div>
<!-- 图片容器 -->
<div
class="w-full aspect-square relative transition-all duration-300 overflow-hidden rounded-lg"
:class="{ 'opacity-0': !isImageLoaded[product.id] }"
>
<img
:src="product.image"
:alt="product.title"
class="w-full h-full object-cover transition-all duration-500 group-hover:scale-110"
@load="handleImageLoad(product.id)"
@error="handleImageError(product.id)"
loading="lazy"
/>
<div
class="absolute inset-0 bg-gradient-to-t from-black/60 via-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"
></div>
<!-- 添加查看详情按钮 -->
<div
class="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-all duration-300 transform translate-y-4 group-hover:translate-y-0"
>
<div
class="px-4 py-2 bg-cyan-400/20 backdrop-blur-sm rounded-full text-white text-sm font-medium border border-cyan-400/30 transform transition-transform duration-300 group-hover:scale-105"
>
{{ t("products.view_details") }}
</div>
</div>
</div>
<!-- 文字内容 -->
<div
class="w-full mt-4 min-h-[80px] transition-all duration-300 transform"
:class="{
'opacity-0 translate-y-4':
!isImageLoaded[product.id],
'opacity-100 translate-y-0':
isImageLoaded[product.id],
}"
>
<div
class="text-center text-white text-base font-bold leading-tight mb-2 line-clamp-2 group-hover:text-cyan-400 transition-colors duration-300"
>
{{ product.title }}
</div>
<div
class="text-center text-zinc-400 text-sm font-normal leading-tight line-clamp-2 group-hover:text-zinc-300 transition-colors duration-300"
>
{{ product.description }}
</div>
</div>
</div>
</nuxt-link>
</div>
</SwiperSlide>
</Swiper>
</div>
</TransitionGroup>
</div>
</div>
</section>

<!-- 核心展示 -->
@@ -813,25 +678,9 @@ interface Product {
link: string;
description?: string;
name?: string;
}

interface Usage {
id: number;
name: string;
category?: string;
products: Product[];
}

interface Category {
id: number;
title: string;
description: string;
capacities: string[];
summary: string;
sort: number;
image: string;
link: string;
audiences: number;
recommend: boolean;
recommendIndex: number;
summary?: string;
}

// 使用i18n
@@ -850,231 +699,26 @@ const videoSrc = ref(video);
/**
* 使用计算属性获取当前语言的数据文件URL
*/
const usageDataUrl = computed(() => {
return `/data/usages-${locale.value}.json`;
});

const categoryDataUrl = computed(() => {
return `/data/categories-${locale.value}.json`;
});

const productsDataUrl = computed(() => {
return `/data/products-${locale.value}.json`;
});

const homepagePath = computed(() => {
return locale.value === "zh" ? "" : `/${locale.value}`;
});

// 获取按用途产品数据
const usageList = ref<Usage[]>([]);
const isLoadingUsage = ref(true);
const activeUsageId = ref(1);

// 获取按分类栏目数据
const categoryList = ref<Category[]>([]);
const isLoadingCategory = ref(true);

// 加载用途数据
const loadUsageData = async () => {
try {
isLoadingUsage.value = true;
// 使用fetch获取数据
if (process.client) {
const response = await fetch(usageDataUrl.value);
if (!response.ok) {
throw new Error(`加载用途数据失败: ${response.status}`);
}
const usageData = await response.json();
// 加载对应的产品数据
const productsResponse = await fetch(productsDataUrl.value);
if (!productsResponse.ok) {
throw new Error(`加载产品数据失败: ${productsResponse.status}`);
}

const productsData = await productsResponse.json();
// 处理数据
usageList.value = usageData
.map((usage: any) => {
// 为每种用途找到对应的产品
const usageProducts = [];

// 按照正确的设计模式:使用用途的title与产品的usage数组进行匹配
// 找出所有usage属性包含当前用途title的产品
const matchedProducts = productsData.filter(
(product: any) =>
product.usage &&
Array.isArray(product.usage) &&
product.usage.includes(usage.title)
);

// 将匹配的产品添加到列表
if (matchedProducts.length > 0) {
matchedProducts.forEach((product: any) => {
usageProducts.push({
id: product.id,
title: product.title,
image: product.image,
link: `${homepagePath.value}/products/${product.name}`,
description: product.summary,
});
});
} else {
// 如果没有找到匹配的产品,添加一个占位产品
const recommendList = ref<Product[]>([]);

usageProducts.push({
id: `placeholder-${usage.id}`,
title: usage.title,
image: ``,
link: `${homepagePath.value}/products?usage=${encodeURIComponent(
usage.title
)}`,
description: "",
});
}

return {
id: parseInt(usage.id) || 0,
name: usage.title,
category: usage.category || "",
products: usageProducts,
};
})
.sort((a: any, b: any) => a.id - b.id);

// 设置默认选中的用途
if (usageList.value.length > 0) {
activeUsageId.value = usageList.value[0].id;
}
}
} catch (error) {
console.error("Error loading usage data:", error);
} finally {
isLoadingUsage.value = false;
}
};

// 加载分类数据
const loadCategoryData = async () => {
try {
isLoadingCategory.value = true;
// 使用fetch获取数据
if (process.client) {
const response = await fetch(categoryDataUrl.value);
if (!response.ok) {
throw new Error(`加载分类数据失败: ${response.status}`);
}

const data = await response.json();

// 处理数据
categoryList.value = data
.map((category: any) => {
return {
id: parseInt(category.id) || 0,
title: category.title || "",
description: category.description || "",
image: category.image || "",
link: `${homepagePath.value}/products?category=${encodeURIComponent(
category.title
)}&audiences=${category.audiences}`,
capacities: category.capacities || [],
summary: category.summary || "",
sort: category.sort || 0,
audiences: category.audiences,
};
})
.sort((a: any, b: any) => a.id - b.id);
}
} catch (error) {
console.error("Error loading category data:", error);
} finally {
isLoadingCategory.value = false;
}
const loadRecommendData = async () => {
const productsResponse = await fetch(productsDataUrl.value);
const productsData = await productsResponse.json();
recommendList.value = productsData
.filter((product: Product) => product.recommend)
.sort((a: Product, b: Product) => b.recommendIndex - a.recommendIndex);
};

// 当页面加载或语言改变时加载数据
onMounted(() => {
loadUsageData();
loadCategoryData();
});

watch(locale, () => {
loadUsageData();
loadCategoryData();
});

// 计算当前用途列表
const typedUsageList = computed(() => {
return usageList.value as Usage[];
});

// 计算当前激活的索引
const activeIndex = computed(() => {
return typedUsageList.value.findIndex(
(item) => item.id === activeUsageId.value
);
});

const activeProducts = computed(() => {
const currentUsage = typedUsageList.value.find(
(item: Usage) => item.id === activeUsageId.value
);
return currentUsage?.products || [];
});

// 图片加载状态
const isImageLoaded = ref<Record<number, boolean>>({});
const imageErrors = ref<Record<number, boolean>>({});

// 监听图片加载状态
watch(
activeProducts,
(newProducts: Product[]) => {
if (process.client) {
newProducts.forEach((product: Product) => {
if (product.image) {
const img = new window.Image();
img.onload = () => handleImageLoad(product.id);
img.onerror = () => handleImageError(product.id);
img.src = product.image;
}
});
}
},
{ immediate: true }
);

// 处理图片加载
const handleImageLoad = (id: number) => {
if (process.client) {
isImageLoaded.value[id] = true;
imageErrors.value[id] = false;
}
};

// 处理图片加载错误
const handleImageError = (id: number) => {
if (process.client) {
console.error(`Failed to load image for product ${id}`);
isImageLoaded.value[id] = true;
imageErrors.value[id] = true;
}
};

// 处理用途点击
const handleUsageClick = (id: number) => {
if (process.client) {
// 重置图片加载状态
isImageLoaded.value = {};
imageErrors.value = {};
activeUsageId.value = id;
}
};

// 计算当前分类列表
const typedCategoryList = computed(() => {
return categoryList.value as Category[];
loadRecommendData();
});

// SEO优化

+ 7
- 3
scripts/generateRoutes.js Прегледај датотеку

@@ -39,7 +39,9 @@ function readMarkdownFrontmatter(filePath) {
capacities: Array.isArray(metadata.capacities) ? metadata.capacities : [],
products: Array.isArray(metadata.products) ? metadata.products : [],
audiences: metadata.audiences,
id: metadata.id || ''
id: metadata.id || '',
recommend: metadata.recommend || false,
recommendIndex: metadata.recommendIndex || -1
};
} catch (err) {
console.error(`解析frontmatter失败: ${filePath}`, err);
@@ -76,7 +78,7 @@ function getProducts(locale) {
return productFiles.map(file => {
const filePath = path.join(productsDir, file);
const metadata = readMarkdownFrontmatter(filePath);
console.log(`metadata: ${JSON.stringify(metadata)}`);
console.log(`metadata: ${JSON.stringify(metadata)} ${metadata}`);
const id = path.basename(file, '.md');
// 使用原始图片路径,如果没有则使用默认路径
@@ -93,7 +95,9 @@ function getProducts(locale) {
summary: metadata.summary || '',
series: metadata.series || [],
gallery: metadata.gallery || [],
capacities: metadata.capacities || []
capacities: metadata.capacities || [],
recommend: metadata.recommend || false,
recommendIndex: metadata.recommendIndex || -1
};
});
} catch (err) {

Loading…
Откажи
Сачувај