Cases.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <template>
  2. <section
  3. id="cases"
  4. ref="casesRef"
  5. class="cases transition-all duration-1000 ease-out"
  6. :class="[isCasesVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-100px']"
  7. >
  8. <div class="cases-header">
  9. <div class="cases-title pf-sc-semibold">知识产权和案例</div>
  10. <div class="cases-desc pf-sc-regular">
  11. 绘家科技凭借强大的技术实力和丰富的实践经验,为客户提供卓越的智慧社区解决方案
  12. </div>
  13. </div>
  14. <div ref="casesListRef" class="cases-list" @scroll="handleCaseScroll">
  15. <div
  16. v-for="(caseItem, index) in cases"
  17. :key="caseItem.title"
  18. class="cases-card"
  19. :class="{ active: index === activeCaseIndex }"
  20. >
  21. <div class="flex flex-col h-full">
  22. <img :src="caseItem.img" class="cases-card-img" alt="" />
  23. <div class="cases-card-title pf-sc-semibold">
  24. {{ caseItem.title }}
  25. </div>
  26. <div class="cases-card-desc pf-sc-regular">
  27. {{ caseItem.desc }}
  28. </div>
  29. <div class="cases-card-points">
  30. <div v-for="point in caseItem.points" :key="point.title" class="cases-card-point">
  31. <i class="i-custom-check-one cases-card-point-icon"></i>
  32. <div class="cases-card-point-title pf-sc-semibold">
  33. {{ point.title }}
  34. </div>
  35. <div class="cases-card-point-desc pf-sc-regular">
  36. {{ point.itemDesc }}
  37. </div>
  38. </div>
  39. </div>
  40. <button class="cases-btn">
  41. {{ caseItem.btnText }}
  42. <i class="i-custom-arrow-right-c cases-card-btn-icon"></i>
  43. </button>
  44. </div>
  45. </div>
  46. </div>
  47. <div class="cases-dots lt-sm:flex hidden">
  48. <div
  49. v-for="(_, index) in cases"
  50. :key="index"
  51. class="dot"
  52. :class="{ active: index === activeCaseIndex }"
  53. @click="scrollToCase(index)"
  54. ></div>
  55. </div>
  56. </section>
  57. </template>
  58. <script setup lang="ts">
  59. import { cases } from '@/constants/common'
  60. const casesRef = ref<HTMLElement | null>(null)
  61. const { isVisible: isCasesVisible } = useScrollReveal(casesRef)
  62. const activeCaseIndex = ref(0)
  63. const casesListRef = ref<HTMLElement | null>(null)
  64. const handleCaseScroll = (e: Event) => {
  65. const el = e.target as HTMLElement
  66. const scrollLeft = el.scrollLeft
  67. const width = el.offsetWidth
  68. const cardWidth = width * 0.9
  69. const gap = 16
  70. const newIndex = Math.round(scrollLeft / (cardWidth + gap))
  71. if (newIndex !== activeCaseIndex.value) {
  72. activeCaseIndex.value = newIndex
  73. }
  74. }
  75. const scrollToCase = (index: number) => {
  76. if (!casesListRef.value) return
  77. const width = casesListRef.value.offsetWidth
  78. const cardWidth = width * 0.9
  79. const gap = 16
  80. casesListRef.value.scrollTo({
  81. left: index * (cardWidth + gap),
  82. behavior: 'smooth',
  83. })
  84. }
  85. </script>
  86. <style scoped lang="scss">
  87. .cases {
  88. @apply pt-120px pb-46px lt-sm:pt-120px lt-sm:pb-60px lt-sm:px-32px;
  89. @extend %landing-container;
  90. }
  91. .cases-header {
  92. @apply text-center mb-60px lt-sm:mb-60px lt-sm:px-32px;
  93. }
  94. .cases-title {
  95. @apply font-s-36px font-semibold text-#000000 mb-4px lh-60px;
  96. @apply lt-sm:font-s-48px lt-sm:mb-16px;
  97. }
  98. .cases-desc {
  99. @apply font-s-16px text-#091221/70 lh-30px;
  100. @apply lt-sm:font-s-24px lt-sm:lh-40px lt-sm:px-70px;
  101. }
  102. .cases-list {
  103. @apply flex gap-24px;
  104. @apply lt-sm:gap-36px lt-sm:overflow-x-auto lt-sm:overflow-y-hidden lt-sm:px-0 lt-sm:pb-10px;
  105. @apply lt-sm:[scroll-snap-type:x_mandatory] lt-sm:[scrollbar-width:none];
  106. &::-webkit-scrollbar {
  107. @apply lt-sm:hidden;
  108. }
  109. }
  110. .cases-card {
  111. @apply group flex-1 overflow-hidden rounded-16px bg-#F6F8FD p-32px transition-all duration-300;
  112. @apply lt-sm:flex-none lt-sm:w-[calc(100vw-84px)] lt-sm:min-w-[calc(100vw-84px)] lt-sm:p-32px lt-sm:rounded-16px;
  113. @apply lt-sm:[scroll-snap-align:center];
  114. &:first-child {
  115. @apply lt-sm:ml-42px;
  116. }
  117. &:last-child {
  118. @apply lt-sm:mr-42px;
  119. }
  120. }
  121. .cases-card-img {
  122. @apply w-full h-218px rounded-12px;
  123. @apply lt-sm:h-320px lt-sm:rounded-12px;
  124. }
  125. .cases-card-title {
  126. @apply font-s-18px font-semibold text-#091221 mt-24px;
  127. @apply lt-sm:font-s-32px lt-sm:mt-32px;
  128. }
  129. .cases-card-desc {
  130. @apply mt-8px font-s-14px lh-24px text-#091221/70;
  131. @apply lt-sm:font-s-24px lt-sm:lh-36px lt-sm:mt-16px;
  132. }
  133. .cases-card-points {
  134. @apply mt-24px flex flex-col gap-8px flex-1;
  135. @apply lt-sm:mt-32px lt-sm:gap-16px;
  136. }
  137. .cases-card-point {
  138. @apply flex items-center gap-8px;
  139. @apply lt-sm:gap-12px;
  140. }
  141. .cases-card-point-icon {
  142. @apply wh-20px mt-2px;
  143. @apply lt-sm:wh-32px;
  144. }
  145. .cases-card-point-title {
  146. @apply font-s-14px font-semibold text-#091221;
  147. @apply lt-sm:font-s-24px;
  148. }
  149. .cases-card-point-desc {
  150. @apply font-s-14px text-#091221/70;
  151. @apply lt-sm:font-s-24px;
  152. }
  153. .cases-btn {
  154. @apply mt-56px flex items-center justify-between gap-8px rounded-8px bg-white px-16px py-15px font-s-16px text-#0F67F8 transition-all hover:bg-#0F67F8 hover:text-white;
  155. @apply lt-sm:mt-48px lt-sm:h-96px lt-sm:rounded-8px lt-sm:px-32px lt-sm:font-s-28px lt-sm:bg-#0F67F8 lt-sm:text-white;
  156. }
  157. .cases-card-btn-icon {
  158. @apply wh-18px;
  159. @apply lt-sm:wh-32px lt-sm:text-white;
  160. }
  161. .cases-dots {
  162. @apply lt-sm:mt-40px lt-sm:flex-center lt-sm:gap-16px;
  163. .dot {
  164. @apply lt-sm:w-60px lt-sm:h-10px bg-#E5E9F5 rounded-full transition-all cursor-pointer;
  165. &.active {
  166. @apply bg-#0F67F8;
  167. }
  168. }
  169. }
  170. </style>