Hanye官网
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.vue 36KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092
  1. <template>
  2. <div>
  3. <div class="w-full h-[88px] md:h-[0]"></div>
  4. <!-- 轮播图 -->
  5. <section class="max-w-full mb-12 md:mb-32 xl:px-8 lg:px-6 md:px-4 px-4">
  6. <Swiper
  7. :modules="[Pagination, Autoplay, EffectCreative]"
  8. :slides-per-view="1"
  9. :space-between="30"
  10. :loop="true"
  11. :pagination="{ el: '.swiper-pagination-1', clickable: true }"
  12. :autoplay="{
  13. delay: 5000,
  14. disableOnInteraction: false,
  15. pauseOnMouseEnter: true,
  16. waitForTransition: true,
  17. }"
  18. effect="creative"
  19. :creativeEffect="{
  20. prev: {
  21. shadow: true,
  22. translate: ['-120%', 0, -500],
  23. rotate: [0, 0, -90],
  24. opacity: 0,
  25. },
  26. next: {
  27. shadow: true,
  28. translate: ['120%', 0, -500],
  29. rotate: [0, 0, 90],
  30. opacity: 0,
  31. },
  32. }"
  33. :speed="1000"
  34. :grabCursor="true"
  35. :parallax="true"
  36. class="h-[320px] sm:h-[320px] md:h-[768px] lg:h-[900px] swiper-container-1"
  37. >
  38. <SwiperSlide>
  39. <div
  40. class="max-w-screen-2xl mx-auto h-full relative"
  41. :style="{
  42. backgroundImage: `url(${homeA1Webp})`,
  43. backgroundSize: 'cover',
  44. backgroundPosition: 'center',
  45. backgroundRepeat: 'no-repeat',
  46. }"
  47. >
  48. <div
  49. class="w-full h-full flex-col justify-center hidden md:flex relative z-10"
  50. >
  51. <div
  52. class="rounded border border-white w-11 h-6 leading-none justify-center flex items-center text-white text-sm font-normal"
  53. >
  54. SSD
  55. </div>
  56. <div class="justify-center">
  57. <span class="text-white text-6xl font-normal leading-[78px]">{{
  58. t("home.carousel.one.title")
  59. }}</span>
  60. <span class="text-white text-6xl font-normal leading-[78px]">{{
  61. t("home.carousel.one.description")
  62. }}</span>
  63. <br />
  64. <span class="text-white text-6xl font-normal leading-[78px]">{{
  65. t("home.carousel.one.description2")
  66. }}</span
  67. ><br />
  68. <span
  69. class="text-cyan-400 text-6xl font-normal leading-[78px]"
  70. >{{ t("home.carousel.one.description3") }}</span
  71. >
  72. </div>
  73. <div class="flex flex-col gap-2 mt-4">
  74. <div
  75. class="flex items-center gap-2 text-stone-50 text-xl font-normal"
  76. >
  77. <div class="w-2 h-2 bg-white rounded-full"></div>
  78. {{ t("home.carousel.one.description4") }}
  79. </div>
  80. <div
  81. class="flex items-center gap-2 text-stone-50 text-xl font-normal"
  82. >
  83. <div class="w-2 h-2 bg-white rounded-full"></div>
  84. {{ t("home.carousel.one.description5") }}
  85. </div>
  86. </div>
  87. <div
  88. class="w-36 h-14 mt-12 flex items-center justify-center bg-[#35F1FF] rounded-lg hover:bg-[#35F1FF]/80 transition-colors duration-300"
  89. >
  90. <nuxt-link
  91. :to="`${homepagePath}/products/HE80-2TNLHS`"
  92. class="w-full h-full !flex items-center justify-center text-zinc-900"
  93. >
  94. {{ t("products.view_details") }}
  95. </nuxt-link>
  96. </div>
  97. </div>
  98. </div>
  99. </SwiperSlide>
  100. <SwiperSlide v-if="!isMobile">
  101. <div class="max-w-screen-2xl mx-auto h-full relative">
  102. <video
  103. :src="homeA3Webp"
  104. autoplay
  105. muted
  106. loop
  107. class="w-full h-full object-cover"
  108. ></video>
  109. <div
  110. class="w-full h-full flex-col justify-center hidden md:flex absolute top-0 left-0 z-10"
  111. >
  112. <div class="justify-center">
  113. <span class="text-white text-6xl font-normal leading-[78px]">{{
  114. t("home.carousel.three.title")
  115. }}</span>
  116. <span class="text-white text-6xl font-normal leading-[78px]">{{
  117. t("home.carousel.three.description")
  118. }}</span>
  119. <br />
  120. <span class="text-white text-6xl font-normal leading-[78px]">{{
  121. t("home.carousel.three.description2")
  122. }}</span
  123. ><br />
  124. <span
  125. class="text-cyan-400 text-6xl font-normal leading-[78px]"
  126. >{{ t("home.carousel.three.description3") }}</span
  127. >
  128. </div>
  129. <div class="flex flex-col gap-2 mt-4">
  130. <div
  131. class="flex items-center gap-2 text-stone-50 text-xl font-normal"
  132. >
  133. <div class="w-2 h-2 bg-white rounded-full"></div>
  134. {{ t("home.carousel.three.description4") }}
  135. </div>
  136. <div
  137. class="flex items-center gap-2 text-stone-50 text-xl font-normal"
  138. >
  139. <div class="w-2 h-2 bg-white rounded-full"></div>
  140. {{ t("home.carousel.three.description5") }}
  141. </div>
  142. </div>
  143. <div
  144. class="w-36 h-14 mt-12 flex items-center justify-center bg-[#35F1FF] rounded-lg hover:bg-[#35F1FF]/80 transition-colors duration-300"
  145. >
  146. <nuxt-link
  147. :to="`${homepagePath}/about`"
  148. class="w-full h-full !flex items-center justify-center text-zinc-900"
  149. >
  150. {{ t("products.view_details") }}
  151. </nuxt-link>
  152. </div>
  153. </div>
  154. </div>
  155. </SwiperSlide>
  156. <div class="max-w-screen-2xl mx-auto relative">
  157. <div
  158. class="swiper-pagination swiper-pagination-1 text-left bottom-1 top-0"
  159. ></div>
  160. </div>
  161. </Swiper>
  162. </section>
  163. <!-- 按用途产品展示 -->
  164. <section class="max-w-full mb-12 md:mb-32 xl:px-8 lg:px-6 md:px-4 px-4">
  165. <div class="max-w-screen-2xl mx-auto relative">
  166. <div
  167. class="justify-center text-cyan-400 text-base font-normal leading-tight mb-4"
  168. >
  169. {{ t("products.usage") }}
  170. </div>
  171. <div
  172. class="justify-center text-white font-normal mb-8 md:mb-16 text-xl sm:text-2xl md:text-4xl lg:text-6xl"
  173. >
  174. {{ t("products.usage_title") }}
  175. </div>
  176. </div>
  177. <div class="max-w-screen-2xl mx-auto">
  178. <div class="w-full mb-8">
  179. <div class="flex flex-wrap items-center gap-2">
  180. <div
  181. v-for="(usage, index) in typedUsageList"
  182. :key="usage.id"
  183. 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"
  184. :class="{
  185. 'bg-cyan-400 border-zinc-900 pointer-events-none text-zinc-900':
  186. activeIndex === index,
  187. 'hover:border-zinc-600': activeIndex !== index,
  188. }"
  189. @click="handleUsageClick(usage.id)"
  190. >
  191. <div
  192. class="usage-name text-center text-xs sm:text-sm font-normal leading-tight md:text-base transition-colors duration-300 relative z-10"
  193. >
  194. {{ usage.name }}
  195. <!-- 用途名称 -->
  196. </div>
  197. <div
  198. class="absolute inset-0 rounded-full bg-cyan-400/20 scale-0 transition-transform duration-300 group-hover:scale-100"
  199. ></div>
  200. </div>
  201. <div class="flex items-center justify-center gap-4 ml-auto">
  202. <div
  203. 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"
  204. >
  205. <i
  206. class="icon-arrow-left text-zinc-300 text-xs sm:text-sm font-normal"
  207. ></i>
  208. </div>
  209. <div
  210. 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"
  211. >
  212. <i
  213. class="icon-arrow-right text-zinc-300 text-xs sm:text-sm font-normal"
  214. ></i>
  215. </div>
  216. </div>
  217. </div>
  218. </div>
  219. </div>
  220. <div class="max-w-screen-2xl mx-auto">
  221. <div
  222. class="w-full h-[380px] sm:h-[300px] md:h-[350px] lg:h-[360px] xl:h-[380px] 2xl:h-[460px] relative"
  223. >
  224. <TransitionGroup name="slide-fade" tag="div" class="relative h-full">
  225. <div :key="activeUsageId" class="w-full h-full">
  226. <Swiper
  227. :modules="[Navigation]"
  228. :spaceBetween="30"
  229. :slidesPerView="4"
  230. :breakpoints="{
  231. 320: { slidesPerView: 1, spaceBetween: 10 },
  232. 448: { slidesPerView: 2, spaceBetween: 10 },
  233. 640: { slidesPerView: 3, spaceBetween: 20 },
  234. 1024: { slidesPerView: 4, spaceBetween: 20 },
  235. 1280: { slidesPerView: 4, spaceBetween: 30 },
  236. 1536: { slidesPerView: 4, spaceBetween: 30 },
  237. }"
  238. :navigation="{
  239. prevEl: '.swiper-button-prev-2',
  240. nextEl: '.swiper-button-next-2',
  241. }"
  242. class="h-full"
  243. >
  244. <SwiperSlide
  245. v-for="product in activeProducts"
  246. :key="product.id"
  247. class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4"
  248. >
  249. <div class="w-full h-full p-2">
  250. <nuxt-link :to="product.link" class="block w-full h-full">
  251. <div
  252. 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"
  253. >
  254. <!-- 图片加载占位 -->
  255. <div
  256. v-if="!isImageLoaded[product.id]"
  257. class="absolute inset-0 bg-gradient-to-br from-zinc-800 via-zinc-700 to-zinc-800 animate-gradient"
  258. >
  259. <div
  260. class="absolute inset-0 flex items-center justify-center"
  261. >
  262. <div
  263. class="w-8 h-8 border-4 border-cyan-400/20 border-t-cyan-400 rounded-full animate-spin"
  264. ></div>
  265. </div>
  266. </div>
  267. <!-- 图片容器 -->
  268. <div
  269. class="w-full aspect-square relative transition-all duration-300 overflow-hidden rounded-lg"
  270. :class="{ 'opacity-0': !isImageLoaded[product.id] }"
  271. >
  272. <img
  273. :src="product.image"
  274. :alt="product.title"
  275. class="w-full h-full object-cover transition-all duration-500 group-hover:scale-110"
  276. @load="handleImageLoad(product.id)"
  277. @error="handleImageError(product.id)"
  278. loading="lazy"
  279. />
  280. <div
  281. 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"
  282. ></div>
  283. <!-- 添加查看详情按钮 -->
  284. <div
  285. 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"
  286. >
  287. <div
  288. 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"
  289. >
  290. {{ t("products.view_details") }}
  291. </div>
  292. </div>
  293. </div>
  294. <!-- 文字内容 -->
  295. <div
  296. class="w-full mt-4 min-h-[80px] transition-all duration-300 transform"
  297. :class="{
  298. 'opacity-0 translate-y-4':
  299. !isImageLoaded[product.id],
  300. 'opacity-100 translate-y-0':
  301. isImageLoaded[product.id],
  302. }"
  303. >
  304. <div
  305. 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"
  306. >
  307. {{ product.title }}
  308. </div>
  309. <div
  310. class="text-center text-zinc-400 text-sm font-normal leading-tight line-clamp-2 group-hover:text-zinc-300 transition-colors duration-300"
  311. >
  312. {{ product.description }}
  313. </div>
  314. </div>
  315. </div>
  316. </nuxt-link>
  317. </div>
  318. </SwiperSlide>
  319. </Swiper>
  320. </div>
  321. </TransitionGroup>
  322. </div>
  323. </div>
  324. </section>
  325. <!-- 按分类栏目展示 -->
  326. <section class="max-w-full mb-12 md:mb-32 xl:px-8 lg:px-6 md:px-4 px-4">
  327. <div class="max-w-screen-2xl mx-auto relative">
  328. <div
  329. class="justify-center text-cyan-400 text-base font-normal leading-tight mb-4"
  330. >
  331. {{ t("home.useCategoryTitle") }}
  332. </div>
  333. <div
  334. class="justify-center text-white font-normal mb-8 md:mb-16 text-xl sm:text-2xl md:text-4xl lg:text-6xl"
  335. >
  336. {{ t("home.useCategoryDescription") }}
  337. </div>
  338. </div>
  339. <div class="max-w-screen-2xl mx-auto">
  340. <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
  341. <nuxt-link
  342. v-for="category in typedCategoryList"
  343. :key="category.id"
  344. :to="category.link"
  345. class="bg-zinc-950/10 backdrop-blur-[50px] border border-white/10 rounded-lg flex gap-8 p-4 sm:p-8 justify-between category-item group hover:border-cyan-400/30 transition-all duration-300"
  346. >
  347. <div class="col-span-1 flex flex-col gap-4">
  348. <div
  349. class="flex flex-col gap-2 opacity-80 group-hover:opacity-100 transition-opacity duration-300"
  350. >
  351. <div
  352. v-for="capacitie in category.capacities"
  353. :key="capacitie"
  354. class="text-white text-sm md:text-base font-normal leading-tight flex gap-2 items-center group-hover:text-cyan-400 transition-colors duration-300"
  355. >
  356. <i
  357. class="icon-star text-sm group-hover:scale-110 transition-transform duration-300"
  358. ></i>
  359. <span>{{ capacitie }}</span>
  360. </div>
  361. </div>
  362. <div
  363. class="p-2 sm:p-4 mt-auto bg-zinc-500/20 rounded-lg outline outline-1 outline-offset-[-1px] outline-white/10 backdrop-blur-xl inline-flex justify-center items-center gap-3 overflow-hidden group-hover:bg-cyan-400/10 group-hover:outline-cyan-400/30 transition-all duration-300"
  364. >
  365. <div
  366. class="justify-start text-neutral-200 text-xs md:text-sm font-medium uppercase leading-relaxed group-hover:text-cyan-400 transition-colors duration-300"
  367. >
  368. {{ category.title }}
  369. </div>
  370. </div>
  371. </div>
  372. <div
  373. class="w-32 h-32 md:w-44 md:h-44 relative overflow-hidden rounded-lg"
  374. >
  375. <img
  376. :src="category.image"
  377. :alt="category.title"
  378. class="w-full h-full object-contain transition-transform duration-500 group-hover:scale-110"
  379. />
  380. </div>
  381. </nuxt-link>
  382. </div>
  383. </div>
  384. </section>
  385. <!-- 核心展示 -->
  386. <section class="max-w-full mb-12 md:mb-32 xl:px-8 lg:px-6 md:px-4 px-4">
  387. <div
  388. class="max-w-screen-2xl mx-auto grid grid-cols-1 lg:grid-cols-3 md:grid-cols-2 sm:grid-cols-1 gap-4"
  389. >
  390. <div class="inline-flex justify-start items-center gap-5">
  391. <div
  392. class="w-12 h-12 md:w-16 md:h-16 relative bg-gradient-to-b from-neutral-600 to-slate-400 rounded-xl border-b-[1.50px] border-neutral-700 overflow-hidden"
  393. >
  394. <div class="w-full h-full flex items-center justify-center">
  395. <i class="icon-h1 text-white text-5xl"></i>
  396. </div>
  397. </div>
  398. <div class="inline-flex flex-col justify-center items-start flex-1">
  399. <div
  400. class="justify-start text-white font-medium text-1xl md:text-lg"
  401. >
  402. {{ t("products.support") }}
  403. </div>
  404. <div
  405. class="justify-start text-zinc-300 text-xs font-normal md:text-sm"
  406. >
  407. {{ t("products.support_description") }}
  408. </div>
  409. </div>
  410. </div>
  411. <div class="inline-flex justify-start items-center gap-5">
  412. <div
  413. class="w-12 h-12 md:w-16 md:h-16 relative bg-gradient-to-b from-neutral-600 to-slate-400 rounded-xl border-b-[1.50px] border-neutral-700 overflow-hidden"
  414. >
  415. <div class="w-full h-full flex items-center justify-center">
  416. <i class="icon-h2 text-white text-5xl"></i>
  417. </div>
  418. </div>
  419. <div class="inline-flex flex-col justify-center items-start flex-1">
  420. <div
  421. class="justify-start text-white font-medium text-1xl md:text-lg"
  422. >
  423. {{ t("products.development") }}
  424. </div>
  425. <div
  426. class="justify-start text-zinc-300 text-xs font-normal md:text-sm"
  427. >
  428. {{ t("products.development_description") }}
  429. </div>
  430. </div>
  431. </div>
  432. <div class="inline-flex justify-start items-center gap-5">
  433. <div
  434. class="w-12 h-12 md:w-16 md:h-16 relative bg-gradient-to-b from-neutral-600 to-slate-400 rounded-xl border-b-[1.50px] border-neutral-700 overflow-hidden"
  435. >
  436. <div class="w-full h-full flex items-center justify-center">
  437. <i class="icon-h3 text-white text-5xl"></i>
  438. </div>
  439. </div>
  440. <div class="inline-flex flex-col justify-center items-start flex-1">
  441. <div
  442. class="justify-start text-white font-medium text-1xl md:text-lg"
  443. >
  444. {{ t("products.develop") }}
  445. </div>
  446. <div
  447. class="justify-start text-zinc-300 text-xs font-normal md:text-sm"
  448. >
  449. {{ t("products.develop_description") }}
  450. </div>
  451. </div>
  452. </div>
  453. </div>
  454. </section>
  455. <!-- 关于我们 -->
  456. <section class="max-w-full mb-12 md:mb-32 xl:px-8 lg:px-6 md:px-4 px-4">
  457. <div class="max-w-screen-2xl mx-auto relative">
  458. <div
  459. class="justify-center text-cyan-400 text-base font-normal leading-tight mb-4"
  460. >
  461. {{ t("products.strong_point") }}
  462. </div>
  463. <div
  464. class="justify-center text-white font-normal mb-8 md:mb-16 text-xl sm:text-2xl md:text-4xl lg:text-6xl"
  465. >
  466. {{ t("products.strong_point_title") }}
  467. </div>
  468. <div
  469. class="absolute right-0 top-1/2 -translate-y-1/2 z-10 lg:block hidden"
  470. >
  471. <div class="flex items-center justify-center gap-4">
  472. <div
  473. class="swiper-button-prev-3 bg-zinc-700 w-10 h-10 rounded-full flex items-center justify-center cursor-pointer hover:bg-zinc-600 transition-colors duration-200"
  474. >
  475. <i class="icon-arrow-left text-zinc-300 text-sm font-normal"></i>
  476. </div>
  477. <div
  478. class="swiper-button-next-3 bg-zinc-700 w-10 h-10 rounded-full flex items-center justify-center cursor-pointer hover:bg-zinc-600 transition-colors duration-200"
  479. >
  480. <i class="icon-arrow-right text-zinc-300 text-sm font-normal"></i>
  481. </div>
  482. </div>
  483. </div>
  484. </div>
  485. <div class="max-w-screen-2xl mx-auto">
  486. <div class="w-full relative max-w-full">
  487. <Swiper
  488. :modules="[Navigation, Pagination]"
  489. slides-per-view="auto"
  490. :space-between="30"
  491. :pagination="{ el: '.swiper-pagination-3', clickable: true }"
  492. :navigation="{
  493. prevEl: '.swiper-button-prev-3',
  494. nextEl: '.swiper-button-next-3',
  495. }"
  496. class="h-[320px] sm:h-[320px] md:h-[480px] lg:h-[720px] max-w-full"
  497. >
  498. <SwiperSlide class="!max-w-screen-2xl !w-full">
  499. <div
  500. class="w-full h-full flex items-center px-0 md:px-20"
  501. :style="{
  502. backgroundImage: `url(${homeC1Webp})`,
  503. backgroundSize: 'cover',
  504. backgroundPosition: 'center',
  505. backgroundRepeat: 'no-repeat',
  506. }"
  507. >
  508. <div
  509. class="w-full lg:w-[50%] h-full md:h-auto bg-white/5 rounded-0 md:rounded-2xl backdrop-blur-[50px] px-4 py-8 md:py-12 md:px-8 flex flex-col gap-8 border border-white/10"
  510. >
  511. <div
  512. class="opacity-90 justify-start text-white text-2xl font-normal md:text-4xl"
  513. >
  514. {{ t("about.companyInfo.name") }}
  515. </div>
  516. <div
  517. class="opacity-70 justify-start text-white text-base md:text-lg font-normal leading-relaxed whitespace-pre-wrap"
  518. >
  519. {{ t("about.companyInfo.description") }}
  520. </div>
  521. </div>
  522. </div>
  523. </SwiperSlide>
  524. </Swiper>
  525. </div>
  526. <div class="max-w-screen-2xl mx-auto relative">
  527. <div class="swiper-pagination swiper-pagination-3"></div>
  528. </div>
  529. </div>
  530. </section>
  531. <!-- 产品咨询 -->
  532. <section class="max-w-full h-[240px] md:h-[480px] bg-black/80 md:block">
  533. <div class="h-full relative">
  534. <div
  535. class="absolute top-0 left-0 w-full h-full flex flex-col gap-6 items-center justify-center z-10"
  536. >
  537. <h1
  538. class="text-center justify-start text-white font-normal text-xl sm:text-2xl md:text-3xl px-2"
  539. >
  540. {{ t("products.consultation") }}
  541. </h1>
  542. <nuxt-link
  543. :to="`${homepagePath}/contact`"
  544. class="w-32 h-10 md:w-40 md:h-11 bg-zinc-300/10 rounded-lg outline outline-1 flex items-center justify-center gap-2 outline-white/20 backdrop-blur-[10px] cursor-pointer hover:bg-zinc-300/20 transition-colors duration-200"
  545. >
  546. <span class="text-xs md:text-sm font-normal">{{
  547. t("products.consultation_button")
  548. }}</span>
  549. <i class="icon-arrow-right text-sm font-normal"></i>
  550. </nuxt-link>
  551. </div>
  552. <img
  553. v-if="isMobile"
  554. :src="videoWebp"
  555. alt="video"
  556. class="w-full h-full object-cover opacity-20"
  557. />
  558. <video
  559. v-else
  560. :src="videoSrc"
  561. autoplay
  562. muted
  563. loop
  564. :poster="videoWebp"
  565. class="w-full h-full object-cover opacity-20"
  566. ></video>
  567. </div>
  568. </section>
  569. </div>
  570. </template>
  571. <script setup lang="ts">
  572. import { Swiper, SwiperSlide } from "swiper/vue";
  573. import {
  574. Navigation,
  575. Pagination,
  576. Autoplay,
  577. EffectCreative,
  578. } from "swiper/modules";
  579. import "swiper/css";
  580. import "swiper/css/navigation";
  581. import "swiper/css/pagination";
  582. import { useBreakpoints, breakpointsTailwind } from "@vueuse/core";
  583. import { useI18n, useRoute, useAsyncData, queryCollection } from "#imports";
  584. import video from "@/assets/videos/video.mp4";
  585. import videoWebp from "@/assets/videos/video.webp";
  586. import homeA1Webp from "@/assets/images/home-a-1.webp";
  587. import homeA2Webp from "@/assets/images/home-a-2.webp";
  588. import homeA3Webp from "@/assets/videos/banner03.mp4";
  589. import homeC1Webp from "@/assets/images/home-c-1.webp";
  590. // 数据类型定义
  591. interface Product {
  592. id: number;
  593. title: string;
  594. image: string;
  595. link: string;
  596. description?: string;
  597. }
  598. interface Usage {
  599. id: number;
  600. name: string;
  601. category?: string;
  602. products: Product[];
  603. }
  604. interface Category {
  605. id: number;
  606. title: string;
  607. description: string;
  608. capacities: string[];
  609. summary: string;
  610. sort: number;
  611. image: string;
  612. link: string;
  613. }
  614. // 使用i18n
  615. const { t, locale } = useI18n();
  616. // 默认语言
  617. const defaultLocale = "zh";
  618. // 获取路由
  619. const route = useRoute();
  620. // 断点
  621. const breakpoints = useBreakpoints(breakpointsTailwind);
  622. // 是否移动端
  623. const isMobile = breakpoints.smaller("md");
  624. const videoSrc = ref(video);
  625. /**
  626. * 使用计算属性获取当前语言的数据文件URL
  627. */
  628. const usageDataUrl = computed(() => {
  629. return `/data/usages-${locale.value}.json`;
  630. });
  631. const categoryDataUrl = computed(() => {
  632. return `/data/categories-${locale.value}.json`;
  633. });
  634. const productsDataUrl = computed(() => {
  635. return `/data/products-${locale.value}.json`;
  636. });
  637. const homepagePath = computed(() => {
  638. return locale.value === "zh" ? "" : `/${locale.value}`;
  639. });
  640. // 获取按用途产品数据
  641. const usageList = ref<Usage[]>([]);
  642. const isLoadingUsage = ref(true);
  643. const activeUsageId = ref(1);
  644. // 获取按分类栏目数据
  645. const categoryList = ref<Category[]>([]);
  646. const isLoadingCategory = ref(true);
  647. // 加载用途数据
  648. const loadUsageData = async () => {
  649. try {
  650. isLoadingUsage.value = true;
  651. // 使用fetch获取数据
  652. if (process.client) {
  653. const response = await fetch(usageDataUrl.value);
  654. if (!response.ok) {
  655. throw new Error(`加载用途数据失败: ${response.status}`);
  656. }
  657. const usageData = await response.json();
  658. // 加载对应的产品数据
  659. const productsResponse = await fetch(productsDataUrl.value);
  660. if (!productsResponse.ok) {
  661. throw new Error(`加载产品数据失败: ${productsResponse.status}`);
  662. }
  663. const productsData = await productsResponse.json();
  664. // 处理数据
  665. usageList.value = usageData
  666. .map((usage: any) => {
  667. // 为每种用途找到对应的产品
  668. const usageProducts = [];
  669. // 按照正确的设计模式:使用用途的title与产品的usage数组进行匹配
  670. // 找出所有usage属性包含当前用途title的产品
  671. const matchedProducts = productsData.filter(
  672. (product: any) =>
  673. product.usage &&
  674. Array.isArray(product.usage) &&
  675. product.usage.includes(usage.title)
  676. );
  677. // 将匹配的产品添加到列表
  678. if (matchedProducts.length > 0) {
  679. matchedProducts.forEach((product: any) => {
  680. usageProducts.push({
  681. id: product.id,
  682. title: product.title,
  683. image: product.image,
  684. link: `${homepagePath.value}/products/${product.id}`,
  685. description: product.summary,
  686. });
  687. });
  688. } else {
  689. // 如果没有找到匹配的产品,添加一个占位产品
  690. usageProducts.push({
  691. id: `placeholder-${usage.id}`,
  692. title: usage.title,
  693. image: ``,
  694. link: `${homepagePath.value}/products?usage=${encodeURIComponent(
  695. usage.title
  696. )}`,
  697. description: "",
  698. });
  699. }
  700. return {
  701. id: parseInt(usage.id) || 0,
  702. name: usage.title,
  703. category: usage.category || "",
  704. products: usageProducts,
  705. };
  706. })
  707. .sort((a: any, b: any) => a.id - b.id);
  708. // 设置默认选中的用途
  709. if (usageList.value.length > 0) {
  710. activeUsageId.value = usageList.value[0].id;
  711. }
  712. }
  713. } catch (error) {
  714. console.error("Error loading usage data:", error);
  715. } finally {
  716. isLoadingUsage.value = false;
  717. }
  718. };
  719. // 加载分类数据
  720. const loadCategoryData = async () => {
  721. try {
  722. isLoadingCategory.value = true;
  723. // 使用fetch获取数据
  724. if (process.client) {
  725. const response = await fetch(categoryDataUrl.value);
  726. if (!response.ok) {
  727. throw new Error(`加载分类数据失败: ${response.status}`);
  728. }
  729. const data = await response.json();
  730. // 处理数据
  731. categoryList.value = data
  732. .map((category: any) => {
  733. return {
  734. id: parseInt(category.id) || 0,
  735. title: category.title || "",
  736. description: category.description || "",
  737. image: category.image || "",
  738. link: `${homepagePath.value}/products?category=${encodeURIComponent(
  739. category.title
  740. )}`,
  741. capacities: category.capacities || [],
  742. summary: category.summary || "",
  743. sort: category.sort || 0,
  744. };
  745. })
  746. .sort((a: any, b: any) => a.id - b.id);
  747. }
  748. } catch (error) {
  749. console.error("Error loading category data:", error);
  750. } finally {
  751. isLoadingCategory.value = false;
  752. }
  753. };
  754. // 当页面加载或语言改变时加载数据
  755. onMounted(() => {
  756. loadUsageData();
  757. loadCategoryData();
  758. });
  759. watch(locale, () => {
  760. loadUsageData();
  761. loadCategoryData();
  762. });
  763. // 计算当前用途列表
  764. const typedUsageList = computed(() => {
  765. return usageList.value as Usage[];
  766. });
  767. // 计算当前激活的索引
  768. const activeIndex = computed(() => {
  769. return typedUsageList.value.findIndex(
  770. (item) => item.id === activeUsageId.value
  771. );
  772. });
  773. const activeProducts = computed(() => {
  774. const currentUsage = typedUsageList.value.find(
  775. (item: Usage) => item.id === activeUsageId.value
  776. );
  777. return currentUsage?.products || [];
  778. });
  779. // 图片加载状态
  780. const isImageLoaded = ref<Record<number, boolean>>({});
  781. const imageErrors = ref<Record<number, boolean>>({});
  782. // 监听图片加载状态
  783. watch(
  784. activeProducts,
  785. (newProducts: Product[]) => {
  786. if (process.client) {
  787. newProducts.forEach((product: Product) => {
  788. if (product.image) {
  789. const img = new window.Image();
  790. img.onload = () => handleImageLoad(product.id);
  791. img.onerror = () => handleImageError(product.id);
  792. img.src = product.image;
  793. }
  794. });
  795. }
  796. },
  797. { immediate: true }
  798. );
  799. // 处理图片加载
  800. const handleImageLoad = (id: number) => {
  801. if (process.client) {
  802. isImageLoaded.value[id] = true;
  803. imageErrors.value[id] = false;
  804. }
  805. };
  806. // 处理图片加载错误
  807. const handleImageError = (id: number) => {
  808. if (process.client) {
  809. console.error(`Failed to load image for product ${id}`);
  810. isImageLoaded.value[id] = true;
  811. imageErrors.value[id] = true;
  812. }
  813. };
  814. // 处理用途点击
  815. const handleUsageClick = (id: number) => {
  816. if (process.client) {
  817. // 重置图片加载状态
  818. isImageLoaded.value = {};
  819. imageErrors.value = {};
  820. activeUsageId.value = id;
  821. }
  822. };
  823. // 计算当前分类列表
  824. const typedCategoryList = computed(() => {
  825. return categoryList.value as Category[];
  826. });
  827. // SEO优化
  828. useHead({
  829. title: t("home.title") + " - Hanye",
  830. meta: [
  831. {
  832. name: "description",
  833. content: t("home.description"),
  834. },
  835. {
  836. name: "keywords",
  837. content: t("home.keywords"),
  838. },
  839. ],
  840. });
  841. </script>
  842. <style lang="scss" scoped>
  843. :deep(.swiper-pagination-3) {
  844. margin: auto;
  845. position: static;
  846. padding: 1rem 0;
  847. }
  848. :deep(.swiper-pagination-bullet) {
  849. background-color: var(--color-bg); /* Example color */
  850. border: 2px solid var(--color-text);
  851. }
  852. :deep(.swiper-pagination-bullet-active) {
  853. background-color: var(--color-text); /* Example color */
  854. }
  855. .category-item {
  856. background-image: url("@/assets/images/home-b-1.webp");
  857. background-size: cover;
  858. background-position: center;
  859. background-repeat: no-repeat;
  860. opacity: 0.8;
  861. transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  862. position: relative;
  863. overflow: hidden;
  864. &:hover {
  865. opacity: 1;
  866. transform: translateY(-2px);
  867. box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
  868. &::before {
  869. opacity: 1;
  870. }
  871. }
  872. &::before {
  873. content: "";
  874. position: absolute;
  875. inset: 0;
  876. background: linear-gradient(45deg, rgba(6, 182, 212, 0.1), transparent);
  877. opacity: 0;
  878. transition: opacity 0.3s ease;
  879. pointer-events: none;
  880. }
  881. }
  882. // 优化过渡动画
  883. .slide-fade-enter-active,
  884. .slide-fade-leave-active {
  885. transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  886. position: absolute;
  887. width: 100%;
  888. height: 300px;
  889. }
  890. @media (min-width: 640px) {
  891. .slide-fade-enter-active,
  892. .slide-fade-leave-active {
  893. height: 350px;
  894. }
  895. }
  896. @media (min-width: 768px) {
  897. .slide-fade-enter-active,
  898. .slide-fade-leave-active {
  899. height: 400px;
  900. }
  901. }
  902. @media (min-width: 1024px) {
  903. .slide-fade-enter-active,
  904. .slide-fade-leave-active {
  905. height: 450px;
  906. }
  907. }
  908. .slide-fade-enter-from {
  909. opacity: 0;
  910. transform: translateX(30px);
  911. }
  912. .slide-fade-leave-to {
  913. opacity: 0;
  914. transform: translateX(-30px);
  915. }
  916. // 添加响应式优化
  917. @media (max-width: 640px) {
  918. .slide-fade-enter-from,
  919. .slide-fade-leave-to {
  920. transform: translateX(15px);
  921. }
  922. }
  923. // 优化图片加载动画
  924. @keyframes gradient {
  925. 0% {
  926. background-position: 0% 50%;
  927. }
  928. 50% {
  929. background-position: 100% 50%;
  930. }
  931. 100% {
  932. background-position: 0% 50%;
  933. }
  934. }
  935. .animate-gradient {
  936. background-size: 200% 200%;
  937. animation: gradient 3s ease infinite;
  938. }
  939. // 移除 pagination 相关样式
  940. :deep(.swiper-pagination-2) {
  941. display: none;
  942. }
  943. // 添加导航按钮禁用样式
  944. :deep(.swiper-button-disabled) {
  945. opacity: 0.35;
  946. cursor: not-allowed;
  947. pointer-events: none;
  948. }
  949. // 优化轮播图样式
  950. :deep(.swiper-container-1) {
  951. .swiper-slide {
  952. transition: all 0.5s ease;
  953. transform-origin: center center;
  954. backface-visibility: hidden;
  955. perspective: 1000px;
  956. }
  957. .swiper-pagination-bullet {
  958. width: 12px;
  959. height: 12px;
  960. background: transparent;
  961. border: 2px solid rgba(255, 255, 255, 0.5);
  962. opacity: 1;
  963. transition: all 0.3s ease;
  964. margin: 0 8px !important;
  965. &-active {
  966. background: #fff;
  967. border-color: #fff;
  968. transform: scale(1.2);
  969. }
  970. }
  971. .swiper-pagination {
  972. display: flex;
  973. justify-content: center;
  974. align-items: center;
  975. padding: 20px 0;
  976. }
  977. }
  978. // 优化图片加载动画
  979. @keyframes fadeIn {
  980. from {
  981. opacity: 0;
  982. transform: scale(1.1);
  983. }
  984. to {
  985. opacity: 1;
  986. transform: scale(1);
  987. }
  988. }
  989. .swiper-slide-active {
  990. img {
  991. animation: fadeIn 0.8s ease-out forwards;
  992. }
  993. }
  994. // 优化产品卡片动画和交互
  995. .swiper-slide {
  996. a {
  997. text-decoration: none;
  998. display: block;
  999. height: 100%;
  1000. &:hover {
  1001. text-decoration: none;
  1002. }
  1003. > div {
  1004. position: relative;
  1005. z-index: 1;
  1006. cursor: pointer;
  1007. transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  1008. &:hover {
  1009. transform: translateY(-2px);
  1010. box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
  1011. }
  1012. }
  1013. }
  1014. }
  1015. // 移除卡片抖动动画
  1016. @keyframes cardHover {
  1017. 0% {
  1018. transform: translateY(0);
  1019. }
  1020. 100% {
  1021. transform: translateY(-2px);
  1022. }
  1023. }
  1024. </style>