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 42KB

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