Files
BigStickNewOfficialWebsite/components/sections/TestimonialsSection.vue
T

133 lines
4.0 KiB
Vue
Raw Normal View History

2026-07-10 18:23:41 +08:00
<script setup>
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
import { testimonials as defaultTestimonials } from "~/data/home";
const props = defineProps({
testimonials: {
type: Array,
default: () => defaultTestimonials,
},
routes: {
type: Array,
default: () => [],
},
sectionId: {
type: String,
default: "testimonials",
},
});
const currentIndex = ref(0);
const isPaused = ref(false);
let timer;
const current = computed(() => props.testimonials[currentIndex.value]);
const goToSlide = (index) => {
currentIndex.value = index;
};
const goToPrev = () => {
currentIndex.value =
currentIndex.value === 0 ? props.testimonials.length - 1 : currentIndex.value - 1;
};
const goToNext = () => {
currentIndex.value =
currentIndex.value === props.testimonials.length - 1 ? 0 : currentIndex.value + 1;
};
const startAutoplay = () => {
timer = window.setInterval(() => {
if (!isPaused.value) {
goToNext();
}
}, 5000);
};
onMounted(() => {
startAutoplay();
});
onBeforeUnmount(() => {
window.clearInterval(timer);
});
</script>
<template>
<section
:id="sectionId"
class="bg-white py-20"
@mouseenter="isPaused = true"
@mouseleave="isPaused = false"
>
<div class="container-gm">
<div class="flex flex-col gap-5 md:flex-row md:items-end md:justify-between" data-reveal>
<div class="max-w-3xl">
<p class="text-sm uppercase tracking-[0.3em] text-[#a1a1a1]">Voice</p>
<h2 class="section-title mt-4">客户真实评价见证服务品质</h2>
<p class="mt-3 text-[#777]">
从真实客户视角补充信任背书进一步强化团队专业度和落地能力
</p>
</div>
<SectionRouteLinks :routes="routes" />
</div>
<div class="mx-auto mt-12 max-w-3xl text-center" data-reveal>
<div class="mx-auto flex h-16 w-16 items-center justify-center rounded-lg bg-gm-light text-xl font-semibold text-gm-green">
{{ current?.name.slice(0, 1) }}
</div>
<transition
mode="out-in"
enter-active-class="transition duration-500 ease-out"
enter-from-class="translate-y-3 opacity-0"
enter-to-class="translate-y-0 opacity-100"
leave-active-class="transition duration-300 ease-in"
leave-from-class="translate-y-0 opacity-100"
leave-to-class="-translate-y-2 opacity-0"
>
<div :key="current.id" class="mt-6">
<blockquote class="text-balance text-lg italic leading-8 text-[#555] md:text-xl">
{{ current.quote }}
</blockquote>
<p class="mt-6 text-base font-semibold text-[#262626]">{{ current.name }}</p>
<p class="text-sm text-[#9b9b9b]">{{ current.title }}</p>
</div>
</transition>
</div>
<div class="mt-8 flex items-center justify-center gap-2">
<button
v-for="(item, index) in props.testimonials"
:key="item.id"
type="button"
:aria-label="`切换到第 ${index + 1} 条评价`"
class="size-3 rounded-full transition-all duration-300"
:class="index === currentIndex ? 'bg-gm-green' : 'bg-gray-300'"
@click="goToSlide(index)"
/>
</div>
<div class="mt-6 flex items-center justify-center gap-4">
<button
type="button"
class="flex size-10 items-center justify-center rounded-full border border-gm-border text-[#666] transition hover:border-gm-green hover:text-gm-green"
aria-label="上一条评价"
@click="goToPrev"
>
</button>
<button
type="button"
class="flex size-10 items-center justify-center rounded-full border border-gm-border text-[#666] transition hover:border-gm-green hover:text-gm-green"
aria-label="下一条评价"
@click="goToNext"
>
</button>
</div>
</div>
</section>
</template>