Hanye官网
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

index.vue 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. <template>
  2. <div>
  3. <!-- 轮播图 -->
  4. <section class="max-w-full mb-12 md:mb-32">
  5. <Swiper
  6. :modules="[Pagination, Autoplay, EffectCreative]"
  7. :slides-per-view="1"
  8. :space-between="30"
  9. :loop="true"
  10. :pagination="{ el: '.swiper-pagination-1', clickable: true }"
  11. :autoplay="{
  12. delay: 5000,
  13. disableOnInteraction: false,
  14. pauseOnMouseEnter: true,
  15. waitForTransition: true,
  16. }"
  17. effect="creative"
  18. :creativeEffect="{
  19. prev: {
  20. shadow: true,
  21. translate: ['-120%', 0, -500],
  22. rotate: [0, 0, -90],
  23. opacity: 0,
  24. },
  25. next: {
  26. shadow: true,
  27. translate: ['120%', 0, -500],
  28. rotate: [0, 0, 90],
  29. opacity: 0,
  30. },
  31. }"
  32. :speed="1000"
  33. :grabCursor="true"
  34. :parallax="true"
  35. class="h-[320px] sm:h-[320px] md:h-[768px] lg:h-[900px] swiper-container-1"
  36. >
  37. <SwiperSlide>
  38. <div
  39. class="max-w-screen-2xl mx-auto h-full relative"
  40. :style="{
  41. backgroundImage: `url(${homeA1Webp})`,
  42. backgroundSize: 'cover',
  43. backgroundPosition: 'center',
  44. backgroundRepeat: 'no-repeat',
  45. }"
  46. >
  47. <div class="w-full h-full flex-col justify-center hidden md:flex relative z-10">
  48. <div
  49. class="rounded border border-white w-11 h-6 leading-none justify-center flex items-center text-white text-sm font-normal"
  50. >
  51. SSD
  52. </div>
  53. <div class="justify-center">
  54. <span class="text-white text-6xl font-normal leading-[78px]">{{
  55. $t("home.carousel.one.title")
  56. }}</span>
  57. <span class="text-white text-6xl font-normal leading-[78px]">{{
  58. $t("home.carousel.one.description")
  59. }}</span>
  60. <br />
  61. <span class="text-white text-6xl font-normal leading-[78px]">{{
  62. $t("home.carousel.one.description2")
  63. }}</span
  64. ><br />
  65. <span
  66. class="text-cyan-400 text-6xl font-normal leading-[78px]"
  67. >{{ $t("home.carousel.one.description3") }}</span
  68. >
  69. </div>
  70. <div class="flex flex-col gap-2 mt-4">
  71. <div
  72. class="flex items-center gap-2 text-stone-50 text-xl font-normal"
  73. >
  74. <div class="w-2 h-2 bg-white rounded-full"></div>
  75. {{ $t("home.carousel.one.description4") }}
  76. </div>
  77. <div
  78. class="flex items-center gap-2 text-stone-50 text-xl font-normal"
  79. >
  80. <div class="w-2 h-2 bg-white rounded-full"></div>
  81. {{ $t("home.carousel.one.description5") }}
  82. </div>
  83. </div>
  84. <div
  85. class="w-36 h-14 mt-12 flex items-center justify-center bg-[#35F1FF] rounded-lg hover:bg-[#35F1FF]/80 transition-colors duration-300"
  86. >
  87. <nuxt-link
  88. to="/products/1"
  89. class="w-full h-full !flex items-center justify-center text-zinc-900"
  90. >
  91. {{ $t("products.view_details") }}
  92. </nuxt-link>
  93. </div>
  94. </div>
  95. </div>
  96. </SwiperSlide>
  97. <div class="max-w-screen-2xl mx-auto relative">
  98. <div
  99. class="swiper-pagination swiper-pagination-1 text-left bottom-1 top-0"
  100. ></div>
  101. </div>
  102. </Swiper>
  103. </section>
  104. <!-- 按用途产品展示 -->
  105. <section class="max-w-full mb-12 md:mb-32 xl:px-2 lg:px-2 md:px-4 px-4">
  106. <div class="max-w-screen-2xl mx-auto relative">
  107. <div
  108. class="justify-center text-cyan-400 text-base font-normal leading-tight mb-4"
  109. >
  110. {{ $t("products.usage") }}
  111. </div>
  112. <div
  113. class="justify-center text-white font-normal mb-8 md:mb-16 text-xl sm:text-2xl md:text-4xl lg:text-6xl"
  114. >
  115. {{ $t("products.usage_title") }}
  116. </div>
  117. </div>
  118. <div class="max-w-screen-2xl mx-auto">
  119. <div class="w-full mb-8">
  120. <div class="flex flex-wrap items-center gap-2">
  121. <div
  122. v-for="(usage, index) in typedUsageList"
  123. :key="usage.id"
  124. 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"
  125. :class="{
  126. 'bg-cyan-400/10 border-cyan-400 text-cyan-400':
  127. activeIndex === index,
  128. 'hover:border-zinc-600': activeIndex !== index,
  129. }"
  130. @click="handleUsageClick(usage.id)"
  131. >
  132. <div
  133. class="text-center text-xs sm:text-sm font-normal leading-tight md:text-base transition-colors duration-300 relative z-10"
  134. >
  135. {{ usage.name }}
  136. </div>
  137. <div
  138. class="absolute inset-0 rounded-full bg-cyan-400/20 scale-0 transition-transform duration-300 group-hover:scale-100"
  139. ></div>
  140. </div>
  141. <div class="flex items-center justify-center gap-4 ml-auto">
  142. <div
  143. 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"
  144. >
  145. <i
  146. class="icon-arrow-left text-zinc-300 text-xs sm:text-sm font-normal"
  147. ></i>
  148. </div>
  149. <div
  150. 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"
  151. >
  152. <i
  153. class="icon-arrow-right text-zinc-300 text-xs sm:text-sm font-normal"
  154. ></i>
  155. </div>
  156. </div>
  157. </div>
  158. </div>
  159. </div>
  160. <div class="max-w-screen-2xl mx-auto">
  161. <div
  162. class="w-full h-[300px] sm:h-[350px] md:h-[400px] lg:h-[450px] relative"
  163. >
  164. <TransitionGroup name="slide-fade" tag="div" class="relative h-full">
  165. <div :key="activeUsageId" class="w-full h-full">
  166. <Swiper
  167. :modules="[Navigation]"
  168. :spaceBetween="30"
  169. :slidesPerView="4"
  170. :breakpoints="{
  171. 320: { slidesPerView: 2, spaceBetween: 10 },
  172. 640: { slidesPerView: 3, spaceBetween: 20 },
  173. 1024: { slidesPerView: 4, spaceBetween: 20 },
  174. 1280: { slidesPerView: 4, spaceBetween: 30 },
  175. 1536: { slidesPerView: 4, spaceBetween: 30 },
  176. }"
  177. :navigation="{
  178. prevEl: '.swiper-button-prev-2',
  179. nextEl: '.swiper-button-next-2',
  180. }"
  181. class="h-full"
  182. >
  183. <SwiperSlide
  184. v-for="product in activeProducts"
  185. :key="product.id"
  186. class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4"
  187. >
  188. <div class="w-full h-full p-2">
  189. <nuxt-link :to="product.link" class="block w-full h-full">
  190. <div
  191. 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"
  192. >
  193. <!-- 图片加载占位 -->
  194. <div
  195. v-if="!isImageLoaded[product.id]"
  196. class="absolute inset-0 bg-gradient-to-br from-zinc-800 via-zinc-700 to-zinc-800 animate-gradient"
  197. >
  198. <div
  199. class="absolute inset-0 flex items-center justify-center"
  200. >
  201. <div
  202. class="w-8 h-8 border-4 border-cyan-400/20 border-t-cyan-400 rounded-full animate-spin"
  203. ></div>
  204. </div>
  205. </div>
  206. <!-- 图片容器 -->
  207. <div
  208. class="w-full aspect-square relative transition-all duration-300 overflow-hidden rounded-lg"
  209. :class="{ 'opacity-0': !isImageLoaded[product.id] }"
  210. >
  211. <img
  212. :src="product.image"
  213. :alt="product.title"
  214. class="w-full h-full object-cover transition-all duration-500 group-hover:scale-110"
  215. @load="handleImageLoad(product.id)"
  216. @error="handleImageError(product.id)"
  217. loading="lazy"
  218. />
  219. <div
  220. 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"
  221. ></div>
  222. <!-- 添加查看详情按钮 -->
  223. <div
  224. 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"
  225. >
  226. <div
  227. 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"
  228. >
  229. {{ $t("products.view_details") }}
  230. </div>
  231. </div>
  232. </div>
  233. <!-- 文字内容 -->
  234. <div
  235. class="w-full mt-4 min-h-[80px] transition-all duration-300 transform"
  236. :class="{
  237. 'opacity-0 translate-y-4':
  238. !isImageLoaded[product.id],
  239. 'opacity-100 translate-y-0':
  240. isImageLoaded[product.id],
  241. }"
  242. >
  243. <div
  244. 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"
  245. >
  246. {{ product.title }}
  247. </div>
  248. <div
  249. class="text-center text-zinc-400 text-sm font-normal leading-tight line-clamp-2 group-hover:text-zinc-300 transition-colors duration-300"
  250. >
  251. {{ product.description }}
  252. </div>
  253. </div>
  254. </div>
  255. </nuxt-link>
  256. </div>
  257. </SwiperSlide>
  258. </Swiper>
  259. </div>
  260. </TransitionGroup>
  261. </div>
  262. </div>
  263. </section>
  264. <!-- 按分类栏目展示 -->
  265. <section class="max-w-full mb-12 md:mb-32 xl:px-2 lg:px-2 md:px-4 px-4">
  266. <div class="max-w-screen-2xl mx-auto relative">
  267. <div
  268. class="justify-center text-cyan-400 text-base font-normal leading-tight mb-4"
  269. >
  270. Hanye独自に開発・製造、販売
  271. </div>
  272. <div
  273. class="justify-center text-white font-normal mb-8 md:mb-16 text-xl sm:text-2xl md:text-4xl lg:text-6xl"
  274. >
  275. 製品カテゴリー
  276. </div>
  277. </div>
  278. <div class="max-w-screen-2xl mx-auto">
  279. <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
  280. <nuxt-link
  281. v-for="category in typedCategoryList"
  282. :key="category.id"
  283. :to="category.link"
  284. 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"
  285. >
  286. <div class="col-span-1 flex flex-col gap-4">
  287. <div
  288. class="flex flex-col gap-2 opacity-80 group-hover:opacity-100 transition-opacity duration-300"
  289. >
  290. <div
  291. v-for="feature in category.features"
  292. :key="feature"
  293. 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"
  294. >
  295. <i
  296. class="icon-star text-sm group-hover:scale-110 transition-transform duration-300"
  297. ></i>
  298. <span>{{ feature }}</span>
  299. </div>
  300. </div>
  301. <div
  302. 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"
  303. >
  304. <div
  305. 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"
  306. >
  307. {{ category.description }}
  308. </div>
  309. </div>
  310. </div>
  311. <div
  312. class="w-32 h-32 md:w-44 md:h-44 relative overflow-hidden rounded-lg"
  313. >
  314. <img
  315. :src="category.image"
  316. :alt="category.title"
  317. class="w-full h-full object-contain transition-transform duration-500 group-hover:scale-110"
  318. />
  319. <div
  320. 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"
  321. ></div>
  322. </div>
  323. </nuxt-link>
  324. </div>
  325. </div>
  326. </section>
  327. <!-- 产品核心展示 -->
  328. <section class="max-w-full mb-12 md:mb-32 xl:px-2 lg:px-2 md:px-4 px-4">
  329. <div
  330. 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"
  331. >
  332. <div class="inline-flex justify-start items-center gap-5">
  333. <div
  334. 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"
  335. >
  336. <div class="w-full h-full flex items-center justify-center">
  337. <i class="icon-h1 text-white text-5xl"></i>
  338. </div>
  339. </div>
  340. <div class="inline-flex flex-col justify-center items-start flex-1">
  341. <div
  342. class="justify-start text-white font-medium text-1xl md:text-lg"
  343. >
  344. {{ $t("products.support") }}
  345. </div>
  346. <div
  347. class="justify-start text-zinc-300 text-xs font-normal md:text-sm"
  348. >
  349. {{ $t("products.support_description") }}
  350. </div>
  351. </div>
  352. </div>
  353. <div class="inline-flex justify-start items-center gap-5">
  354. <div
  355. 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"
  356. >
  357. <div class="w-full h-full flex items-center justify-center">
  358. <i class="icon-h2 text-white text-5xl"></i>
  359. </div>
  360. </div>
  361. <div class="inline-flex flex-col justify-center items-start flex-1">
  362. <div
  363. class="justify-start text-white font-medium text-1xl md:text-lg"
  364. >
  365. {{ $t("products.development") }}
  366. </div>
  367. <div
  368. class="justify-start text-zinc-300 text-xs font-normal md:text-sm"
  369. >
  370. {{ $t("products.development_description") }}
  371. </div>
  372. </div>
  373. </div>
  374. <div class="inline-flex justify-start items-center gap-5">
  375. <div
  376. 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"
  377. >
  378. <div class="w-full h-full flex items-center justify-center">
  379. <i class="icon-h3 text-white text-5xl"></i>
  380. </div>
  381. </div>
  382. <div class="inline-flex flex-col justify-center items-start flex-1">
  383. <div
  384. class="justify-start text-white font-medium text-1xl md:text-lg"
  385. >
  386. {{ $t("products.develop") }}
  387. </div>
  388. <div
  389. class="justify-start text-zinc-300 text-xs font-normal md:text-sm"
  390. >
  391. {{ $t("products.develop_description") }}
  392. </div>
  393. </div>
  394. </div>
  395. </div>
  396. </section>
  397. <!-- 当社の強み -->
  398. <section class="max-w-full mb-12 md:mb-32 xl:px-2 lg:px-2 md:px-4 px-4">
  399. <div class="max-w-screen-2xl mx-auto relative">
  400. <div
  401. class="justify-center text-cyan-400 text-base font-normal leading-tight mb-4"
  402. >
  403. {{ $t("products.strong_point") }}
  404. </div>
  405. <div
  406. class="justify-center text-white font-normal mb-8 md:mb-16 text-xl sm:text-2xl md:text-4xl lg:text-6xl"
  407. >
  408. {{ $t("products.strong_point_title") }}
  409. </div>
  410. <div
  411. class="absolute right-0 top-1/2 -translate-y-1/2 z-10 lg:block hidden"
  412. >
  413. <div class="flex items-center justify-center gap-4">
  414. <div
  415. 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"
  416. >
  417. <i class="icon-arrow-left text-zinc-300 text-sm font-normal"></i>
  418. </div>
  419. <div
  420. 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"
  421. >
  422. <i class="icon-arrow-right text-zinc-300 text-sm font-normal"></i>
  423. </div>
  424. </div>
  425. </div>
  426. </div>
  427. <div class="max-w-screen-2xl mx-auto">
  428. <div class="w-full relative max-w-full">
  429. <Swiper
  430. :modules="[Navigation, Pagination]"
  431. slides-per-view="auto"
  432. :space-between="30"
  433. :pagination="{ el: '.swiper-pagination-3', clickable: true }"
  434. :navigation="{
  435. prevEl: '.swiper-button-prev-3',
  436. nextEl: '.swiper-button-next-3',
  437. }"
  438. class="h-[320px] sm:h-[320px] md:h-[480px] lg:h-[720px] max-w-full"
  439. >
  440. <SwiperSlide class="!max-w-screen-2xl !w-full">
  441. <div
  442. class="w-full h-full flex items-center px-0 md:px-20"
  443. :style="{
  444. backgroundImage: `url(${homeC1Webp})`,
  445. backgroundSize: 'cover',
  446. backgroundPosition: 'center',
  447. backgroundRepeat: 'no-repeat',
  448. }"
  449. >
  450. <div
  451. 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"
  452. >
  453. <div
  454. class="opacity-90 justify-start text-white text-2xl font-normal md:text-4xl"
  455. >
  456. 一貫体制による高品質と安定供給
  457. </div>
  458. <div
  459. class="opacity-70 justify-start text-white text-base md:text-lg font-normal leading-relaxed"
  460. >
  461. 「企画・開発から製造、品質管理、販売、オンラインショップ運営まで自社で完結。ISO認証取得の工場で生産された信頼性の高い製品を、安定してお届けします。」
  462. </div>
  463. </div>
  464. </div>
  465. </SwiperSlide>
  466. </Swiper>
  467. </div>
  468. <div class="max-w-screen-2xl mx-auto relative">
  469. <div class="swiper-pagination swiper-pagination-3"></div>
  470. </div>
  471. </div>
  472. </section>
  473. <!-- 产品咨询 -->
  474. <section class="max-w-full h-[240px] md:h-[480px] bg-black/80 md:block">
  475. <div class="h-full relative">
  476. <div
  477. class="absolute top-0 left-0 w-full h-full flex flex-col gap-6 items-center justify-center z-10"
  478. >
  479. <h1
  480. class="text-center justify-start text-white font-normal text-xl sm:text-2xl md:text-3xl px-2"
  481. >
  482. {{ $t("products.consultation") }}
  483. </h1>
  484. <nuxt-link
  485. :to="locale === defaultLocale ? '/contact' : `/${locale}/contact`"
  486. 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"
  487. >
  488. <span class="text-xs md:text-sm font-normal">{{
  489. $t("products.consultation_button")
  490. }}</span>
  491. <i class="icon-arrow-right text-sm font-normal"></i>
  492. </nuxt-link>
  493. </div>
  494. <img
  495. v-if="isMobile"
  496. :src="videoWebp"
  497. alt="video"
  498. class="w-full h-full object-cover opacity-20"
  499. />
  500. <video
  501. v-else
  502. :src="videoSrc"
  503. autoplay
  504. muted
  505. loop
  506. :poster="videoWebp"
  507. class="w-full h-full object-cover opacity-20"
  508. ></video>
  509. </div>
  510. </section>
  511. </div>
  512. </template>
  513. <script setup lang="ts">
  514. import { Swiper, SwiperSlide } from "swiper/vue";
  515. import {
  516. Navigation,
  517. Pagination,
  518. Autoplay,
  519. EffectCreative,
  520. } from "swiper/modules";
  521. import "swiper/css";
  522. import "swiper/css/navigation";
  523. import "swiper/css/pagination";
  524. import { useBreakpoints, breakpointsTailwind } from "@vueuse/core";
  525. import { useI18n } from "vue-i18n";
  526. import video from "@/assets/videos/video.mp4";
  527. import videoWebp from "@/assets/videos/video.webp";
  528. import homeA1Webp from "@/assets/images/home-a-1.webp";
  529. import homeC1Webp from "@/assets/images/home-c-1.webp";
  530. const { t, locale } = useI18n();
  531. const config = useRuntimeConfig();
  532. // 从运行时配置获取默认语言,如果未配置则默认为 'en'
  533. const defaultLocale = config.public.i18n?.defaultLocale || "en";
  534. const videoSrc = ref(video);
  535. // Define breakpoints
  536. const breakpoints = useBreakpoints(breakpointsTailwind);
  537. // Check if the device is mobile (smaller than md)
  538. const isMobile = breakpoints.smaller("md");
  539. // 获取轮播图数据
  540. const { data: carouselData, error: carouselError } = await useFetch(
  541. "/api/home"
  542. );
  543. const carouselList = ref(carouselData.value?.data || []);
  544. // 获取按用途产品数据
  545. const { data: usageData, error: usageError } = await useFetch(
  546. "/api/products/usage"
  547. );
  548. const usageList = ref(usageData.value?.data || []);
  549. const activeUsageId = ref(1);
  550. // 获取按分类栏目数据
  551. const { data: categoryData, error: categoryError } = await useFetch(
  552. "/api/products/category"
  553. );
  554. const categoryList = ref(categoryData.value?.data || []);
  555. interface Product {
  556. id: number;
  557. title: string;
  558. image: string;
  559. link: string;
  560. description?: string;
  561. }
  562. interface Usage {
  563. id: number;
  564. name: string;
  565. products: Product[];
  566. }
  567. interface Category {
  568. id: number;
  569. title: string;
  570. description: string;
  571. features: string[];
  572. image: string;
  573. link: string;
  574. }
  575. // 计算当前用途列表
  576. const typedUsageList = computed(() => {
  577. console.log("Typed Usage List:", usageList.value);
  578. return usageList.value as Usage[];
  579. });
  580. // 计算当前激活的索引
  581. const activeIndex = computed(() => {
  582. return typedUsageList.value.findIndex(
  583. (item) => item.id === activeUsageId.value
  584. );
  585. });
  586. const activeProducts = computed(() => {
  587. const currentUsage = typedUsageList.value.find(
  588. (item: Usage) => item.id === activeUsageId.value
  589. );
  590. console.log("Current Usage:", currentUsage);
  591. if (currentUsage?.products) {
  592. console.log(
  593. "Products:",
  594. currentUsage.products.map((p: Product) => ({
  595. id: p.id,
  596. title: p.title,
  597. image: p.image,
  598. link: p.link,
  599. }))
  600. );
  601. }
  602. return currentUsage?.products || [];
  603. });
  604. // 图片加载状态
  605. const isImageLoaded = ref<Record<number, boolean>>({});
  606. const imageErrors = ref<Record<number, boolean>>({});
  607. // 监听图片加载状态
  608. watch(
  609. activeProducts,
  610. (newProducts: Product[]) => {
  611. console.log("Active Products Changed:", newProducts);
  612. if (process.client) {
  613. newProducts.forEach((product: Product) => {
  614. if (product.image) {
  615. console.log(
  616. `Checking image for product ${product.id}:`,
  617. product.image
  618. );
  619. const img = new window.Image();
  620. img.onload = () => handleImageLoad(product.id);
  621. img.onerror = () => handleImageError(product.id);
  622. img.src = product.image;
  623. }
  624. });
  625. }
  626. },
  627. { immediate: true }
  628. );
  629. // 处理图片加载
  630. const handleImageLoad = (id: number) => {
  631. if (process.client) {
  632. console.log(`Image loaded for product ${id}`);
  633. isImageLoaded.value[id] = true;
  634. imageErrors.value[id] = false;
  635. }
  636. };
  637. // 处理图片加载错误
  638. const handleImageError = (id: number) => {
  639. if (process.client) {
  640. console.error(`Failed to load image for product ${id}`);
  641. isImageLoaded.value[id] = true;
  642. imageErrors.value[id] = true;
  643. }
  644. };
  645. // 处理用途点击
  646. const handleUsageClick = (id: number) => {
  647. if (process.client) {
  648. console.log("Usage clicked:", id);
  649. // 重置图片加载状态
  650. isImageLoaded.value = {};
  651. imageErrors.value = {};
  652. activeUsageId.value = id;
  653. }
  654. };
  655. // 计算当前分类列表
  656. const typedCategoryList = computed(() => {
  657. console.log("Typed Category List:", categoryList.value);
  658. return categoryList.value as Category[];
  659. });
  660. // SEO优化
  661. useHead({
  662. title: t("home.title") + " - Hanye",
  663. meta: [
  664. {
  665. name: "description",
  666. content: t("home.description"),
  667. },
  668. {
  669. name: "keywords",
  670. content: t("home.keywords"),
  671. },
  672. ],
  673. });
  674. </script>
  675. <style lang="scss" scoped>
  676. :deep(.swiper-pagination-3) {
  677. margin: auto;
  678. position: static;
  679. padding: 1rem 0;
  680. }
  681. :deep(.swiper-pagination-bullet) {
  682. background-color: var(--color-bg); /* Example color */
  683. border: 2px solid var(--color-text);
  684. }
  685. :deep(.swiper-pagination-bullet-active) {
  686. background-color: var(--color-text); /* Example color */
  687. }
  688. .category-item {
  689. background-image: url("@/assets/images/home-b-1.webp");
  690. background-size: cover;
  691. background-position: center;
  692. background-repeat: no-repeat;
  693. opacity: 0.8;
  694. transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  695. position: relative;
  696. overflow: hidden;
  697. &:hover {
  698. opacity: 1;
  699. transform: translateY(-2px);
  700. box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
  701. &::before {
  702. opacity: 1;
  703. }
  704. }
  705. &::before {
  706. content: "";
  707. position: absolute;
  708. inset: 0;
  709. background: linear-gradient(45deg, rgba(6, 182, 212, 0.1), transparent);
  710. opacity: 0;
  711. transition: opacity 0.3s ease;
  712. pointer-events: none;
  713. }
  714. }
  715. // 优化过渡动画
  716. .slide-fade-enter-active,
  717. .slide-fade-leave-active {
  718. transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  719. position: absolute;
  720. width: 100%;
  721. height: 300px;
  722. }
  723. @media (min-width: 640px) {
  724. .slide-fade-enter-active,
  725. .slide-fade-leave-active {
  726. height: 350px;
  727. }
  728. }
  729. @media (min-width: 768px) {
  730. .slide-fade-enter-active,
  731. .slide-fade-leave-active {
  732. height: 400px;
  733. }
  734. }
  735. @media (min-width: 1024px) {
  736. .slide-fade-enter-active,
  737. .slide-fade-leave-active {
  738. height: 450px;
  739. }
  740. }
  741. .slide-fade-enter-from {
  742. opacity: 0;
  743. transform: translateX(30px);
  744. }
  745. .slide-fade-leave-to {
  746. opacity: 0;
  747. transform: translateX(-30px);
  748. }
  749. // 添加响应式优化
  750. @media (max-width: 640px) {
  751. .slide-fade-enter-from,
  752. .slide-fade-leave-to {
  753. transform: translateX(15px);
  754. }
  755. }
  756. // 优化图片加载动画
  757. @keyframes gradient {
  758. 0% {
  759. background-position: 0% 50%;
  760. }
  761. 50% {
  762. background-position: 100% 50%;
  763. }
  764. 100% {
  765. background-position: 0% 50%;
  766. }
  767. }
  768. .animate-gradient {
  769. background-size: 200% 200%;
  770. animation: gradient 3s ease infinite;
  771. }
  772. // 移除 pagination 相关样式
  773. :deep(.swiper-pagination-2) {
  774. display: none;
  775. }
  776. // 添加导航按钮禁用样式
  777. :deep(.swiper-button-disabled) {
  778. opacity: 0.35;
  779. cursor: not-allowed;
  780. pointer-events: none;
  781. }
  782. // 优化轮播图样式
  783. :deep(.swiper-container-1) {
  784. .swiper-slide {
  785. transition: all 0.5s ease;
  786. transform-origin: center center;
  787. backface-visibility: hidden;
  788. perspective: 1000px;
  789. }
  790. .swiper-pagination-bullet {
  791. width: 12px;
  792. height: 12px;
  793. background: transparent;
  794. border: 2px solid rgba(255, 255, 255, 0.5);
  795. opacity: 1;
  796. transition: all 0.3s ease;
  797. margin: 0 8px !important;
  798. &-active {
  799. background: #fff;
  800. border-color: #fff;
  801. transform: scale(1.2);
  802. }
  803. }
  804. .swiper-pagination {
  805. display: flex;
  806. justify-content: center;
  807. align-items: center;
  808. padding: 20px 0;
  809. }
  810. }
  811. // 添加轮播图遮罩效果
  812. .swiper-slide {
  813. &::before {
  814. content: "";
  815. position: absolute;
  816. top: 0;
  817. left: 0;
  818. right: 0;
  819. bottom: 0;
  820. background: linear-gradient(
  821. to bottom,
  822. rgba(0, 0, 0, 0.2) 0%,
  823. rgba(0, 0, 0, 0.4) 100%
  824. );
  825. z-index: 1;
  826. }
  827. }
  828. // 优化图片加载动画
  829. @keyframes fadeIn {
  830. from {
  831. opacity: 0;
  832. transform: scale(1.1);
  833. }
  834. to {
  835. opacity: 1;
  836. transform: scale(1);
  837. }
  838. }
  839. .swiper-slide-active {
  840. img {
  841. animation: fadeIn 0.8s ease-out forwards;
  842. }
  843. }
  844. // 优化产品卡片动画和交互
  845. .swiper-slide {
  846. a {
  847. text-decoration: none;
  848. display: block;
  849. height: 100%;
  850. &:hover {
  851. text-decoration: none;
  852. }
  853. > div {
  854. position: relative;
  855. z-index: 1;
  856. cursor: pointer;
  857. transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  858. &:hover {
  859. transform: translateY(-2px);
  860. box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
  861. }
  862. }
  863. }
  864. }
  865. // 移除卡片抖动动画
  866. @keyframes cardHover {
  867. 0% {
  868. transform: translateY(0);
  869. }
  870. 100% {
  871. transform: translateY(-2px);
  872. }
  873. }
  874. </style>