| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- <template>
- <section
- id="cases"
- ref="casesRef"
- class="cases transition-all duration-1000 ease-out"
- :class="[isCasesVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-100px']"
- >
- <div class="cases-header">
- <div class="cases-title pf-sc-semibold">知识产权和案例</div>
- <div class="cases-desc pf-sc-regular">
- 绘家科技凭借强大的技术实力和丰富的实践经验,为客户提供卓越的智慧社区解决方案
- </div>
- </div>
- <div ref="casesListRef" class="cases-list" @scroll="handleCaseScroll">
- <div
- v-for="(caseItem, index) in cases"
- :key="caseItem.title"
- class="cases-card"
- :class="{ active: index === activeCaseIndex }"
- >
- <div class="flex flex-col h-full">
- <img :src="caseItem.img" class="cases-card-img" alt="" />
- <div class="cases-card-title pf-sc-semibold">
- {{ caseItem.title }}
- </div>
- <div class="cases-card-desc pf-sc-regular">
- {{ caseItem.desc }}
- </div>
- <div class="cases-card-points">
- <div v-for="point in caseItem.points" :key="point.title" class="cases-card-point">
- <i class="i-custom-check-one cases-card-point-icon"></i>
- <div class="cases-card-point-title pf-sc-semibold">
- {{ point.title }}
- </div>
- <div class="cases-card-point-desc pf-sc-regular">
- {{ point.itemDesc }}
- </div>
- </div>
- </div>
- <button class="cases-btn">
- {{ caseItem.btnText }}
- <i class="i-custom-arrow-right-c cases-card-btn-icon"></i>
- </button>
- </div>
- </div>
- </div>
- <div class="cases-dots lt-sm:flex hidden">
- <div
- v-for="(_, index) in cases"
- :key="index"
- class="dot"
- :class="{ active: index === activeCaseIndex }"
- @click="scrollToCase(index)"
- ></div>
- </div>
- </section>
- </template>
- <script setup lang="ts">
- import { cases } from '@/constants/common'
- const casesRef = ref<HTMLElement | null>(null)
- const isCasesVisible = ref(false)
- const activeCaseIndex = ref(0)
- const casesListRef = ref<HTMLElement | null>(null)
- const handleCaseScroll = (e: Event) => {
- const el = e.target as HTMLElement
- const scrollLeft = el.scrollLeft
- const width = el.offsetWidth
- const cardWidth = width * 0.9
- const gap = 16
- const newIndex = Math.round(scrollLeft / (cardWidth + gap))
- if (newIndex !== activeCaseIndex.value) {
- activeCaseIndex.value = newIndex
- }
- }
- const scrollToCase = (index: number) => {
- if (!casesListRef.value) return
- const width = casesListRef.value.offsetWidth
- const cardWidth = width * 0.9
- const gap = 16
- casesListRef.value.scrollTo({
- left: index * (cardWidth + gap),
- behavior: 'smooth',
- })
- }
- onMounted(() => {
- const observer = new IntersectionObserver(
- (entries) => {
- entries.forEach((entry) => {
- if (!entry.isIntersecting) return
- isCasesVisible.value = true
- observer.unobserve(entry.target)
- })
- },
- { threshold: 0.1 }
- )
- if (casesRef.value) {
- observer.observe(casesRef.value)
- }
- })
- </script>
- <style scoped lang="scss">
- .cases {
- @apply pt-120px pb-46px lt-sm:pt-120px lt-sm:pb-60px lt-sm:px-32px;
- @extend %landing-container;
- }
- .cases-header {
- @apply text-center mb-60px lt-sm:mb-60px lt-sm:px-32px;
- }
- .cases-title {
- @apply font-s-36px font-semibold text-#000000 mb-4px lh-60px;
- @apply lt-sm:font-s-48px lt-sm:mb-16px;
- }
- .cases-desc {
- @apply font-s-16px text-#091221/70 lh-30px;
- @apply lt-sm:font-s-24px lt-sm:lh-40px lt-sm:px-70px;
- }
- .cases-list {
- @apply flex gap-24px;
- @apply lt-sm:gap-36px lt-sm:overflow-x-auto lt-sm:overflow-y-hidden lt-sm:px-0 lt-sm:pb-10px;
- @apply lt-sm:[scroll-snap-type:x_mandatory] lt-sm:[scrollbar-width:none];
- &::-webkit-scrollbar {
- @apply lt-sm:hidden;
- }
- }
- .cases-card {
- @apply group flex-1 overflow-hidden rounded-16px bg-#F6F8FD p-32px transition-all duration-300;
- @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;
- @apply lt-sm:[scroll-snap-align:center];
- &:first-child {
- @apply lt-sm:ml-42px;
- }
- &:last-child {
- @apply lt-sm:mr-42px;
- }
- }
- .cases-card-img {
- @apply w-full h-218px rounded-12px;
- @apply lt-sm:h-320px lt-sm:rounded-12px;
- }
- .cases-card-title {
- @apply font-s-18px font-semibold text-#091221 mt-24px;
- @apply lt-sm:font-s-32px lt-sm:mt-32px;
- }
- .cases-card-desc {
- @apply mt-8px font-s-14px lh-24px text-#091221/70;
- @apply lt-sm:font-s-24px lt-sm:lh-36px lt-sm:mt-16px;
- }
- .cases-card-points {
- @apply mt-24px flex flex-col gap-8px flex-1;
- @apply lt-sm:mt-32px lt-sm:gap-16px;
- }
- .cases-card-point {
- @apply flex items-center gap-8px;
- @apply lt-sm:gap-12px;
- }
- .cases-card-point-icon {
- @apply wh-20px mt-2px;
- @apply lt-sm:wh-32px;
- }
- .cases-card-point-title {
- @apply font-s-14px font-semibold text-#091221;
- @apply lt-sm:font-s-24px;
- }
- .cases-card-point-desc {
- @apply font-s-14px text-#091221/70;
- @apply lt-sm:font-s-24px;
- }
- .cases-btn {
- @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;
- @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;
- }
- .cases-card-btn-icon {
- @apply wh-18px;
- @apply lt-sm:wh-32px lt-sm:text-white;
- }
- .cases-dots {
- @apply lt-sm:mt-40px lt-sm:flex-center lt-sm:gap-16px;
- .dot {
- @apply lt-sm:w-60px lt-sm:h-10px bg-#E5E9F5 rounded-full transition-all cursor-pointer;
- &.active {
- @apply bg-#0F67F8;
- }
- }
- }
- </style>
|