133 lines
4.0 KiB
Vue
133 lines
4.0 KiB
Vue
<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>
|