Hanye官网
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

index.vue 40KB

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