All checks were successful
site-dist-deploy / build-and-deploy (push) Successful in 2m9s
181 lines
5.3 KiB
Vue
181 lines
5.3 KiB
Vue
<template>
|
||
<div class="review-carousel-container relative">
|
||
<transition name="fade" mode="out-in">
|
||
<div
|
||
v-if="currentReview"
|
||
:key="currentIndex"
|
||
class="review-card flex items-center lucid-glass-bar text-white rounded-2xl overflow-hidden p-4 md:p-5 relative"
|
||
:style="{ height: isMobile ? '114px' : '130px' }"
|
||
>
|
||
<!-- Avatar -->
|
||
<div class="flex-shrink-0 mr-4 md:mr-6">
|
||
<img
|
||
:src="currentReview.avatar"
|
||
alt="User Avatar"
|
||
class="w-[70px] h-[70px] md:w-[84px] md:h-[84px] rounded-full object-cover border-2 border-pink-300/30"
|
||
/>
|
||
</div>
|
||
|
||
<!-- Content -->
|
||
<div class="flex-grow min-w-0">
|
||
<div class="flex justify-between items-start mb-1">
|
||
<h3 class="text-base md:text-xl font-bold truncate pr-2">{{ currentReview.username }}</h3>
|
||
<router-link
|
||
to="/reviews"
|
||
class="more-reviews text-[10px] md:text-sm text-white hover:text-[#ADFF5B] transition-colors underline decoration-white underline-offset-4"
|
||
>
|
||
More Reviews
|
||
</router-link>
|
||
</div>
|
||
|
||
<!-- Stars and Rating -->
|
||
<div class="flex items-center gap-1 mb-1 md:mb-2">
|
||
<div class="flex gap-0.5">
|
||
<svg v-for="i in 5" :key="i" class="w-3 h-3 md:w-4 md:h-4 text-[#ADFF5B]" fill="currentColor" viewBox="0 0 20 20">
|
||
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
|
||
</svg>
|
||
</div>
|
||
<span class="text-xs md:text-sm text-white/50 font-medium ml-1">4.9</span>
|
||
<span class="text-xs md:text-sm text-white/30 ml-2">{{ currentReview.reviewCount }}k reviews</span>
|
||
</div>
|
||
|
||
<!-- Comment Text -->
|
||
<p class="text-xs md:text-sm text-white/90 leading-[1.4] line-clamp-2 md:pr-4">
|
||
“{{ currentReview.comment }}”
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</transition>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, onMounted, onUnmounted, computed } from 'vue';
|
||
|
||
// Import avatars
|
||
import avatar1 from './avatars/avatar1.png';
|
||
import avatar2 from './avatars/avatar2.png';
|
||
import avatar3 from './avatars/avatar3.png';
|
||
import avatar4 from './avatars/avatar4.png';
|
||
import avatar6 from './avatars/avatar6.png';
|
||
|
||
const isMobile = ref(false);
|
||
|
||
const reviews = [
|
||
{
|
||
username: '庄子不给',
|
||
avatar: avatar1,
|
||
comment: '真心不错,比我只前买的那个好用多了。网宿很给力。',
|
||
reviewCount: '5.7'
|
||
},
|
||
{
|
||
username: 'TechEnthusiast',
|
||
avatar: avatar6,
|
||
comment: 'Speed is incredible! The best VPN I have used in years for international streaming.',
|
||
reviewCount: '12.4'
|
||
},
|
||
{
|
||
username: '阿杰',
|
||
avatar: avatar2,
|
||
comment: '非常稳定的连接,在高峰时段也没掉过线。UI设计很现代,用着很舒服。',
|
||
reviewCount: '3.2'
|
||
},
|
||
{
|
||
username: 'Lina_Zhang',
|
||
avatar: avatar3,
|
||
comment: '客服响应速度很快,配置简单。推荐给需要长期稳定翻墙的朋友们。',
|
||
reviewCount: '8.9'
|
||
},
|
||
{
|
||
username: 'GlobalNomad',
|
||
avatar: avatar4,
|
||
comment: 'Perfect for my travels. Low latency and high security. A must-have for privacy.',
|
||
reviewCount: '6.1'
|
||
}
|
||
];
|
||
|
||
const currentIndex = ref(0);
|
||
const currentReview = computed(() => reviews[currentIndex.value]);
|
||
|
||
let timer: any = null;
|
||
|
||
const startCarousel = () => {
|
||
timer = setInterval(() => {
|
||
currentIndex.value = (currentIndex.value + 1) % reviews.length;
|
||
}, 5000);
|
||
};
|
||
|
||
const handleResize = () => {
|
||
isMobile.value = window.innerWidth < 768;
|
||
};
|
||
|
||
onMounted(() => {
|
||
handleResize();
|
||
window.addEventListener('resize', handleResize);
|
||
startCarousel();
|
||
});
|
||
|
||
onUnmounted(() => {
|
||
window.removeEventListener('resize', handleResize);
|
||
if (timer) clearInterval(timer);
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.review-carousel-container {
|
||
width: 100%;
|
||
}
|
||
|
||
@media (min-width: 768px) {
|
||
.review-carousel-container {
|
||
max-width: 400px;
|
||
}
|
||
}
|
||
|
||
.fade-enter-active,
|
||
.fade-leave-active {
|
||
transition: opacity 0.8s cubic-bezier(0.4, 0, 0.2, 1), transform 0.8s cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
|
||
.fade-enter-from {
|
||
opacity: 0;
|
||
transform: translateY(20px);
|
||
}
|
||
|
||
.fade-leave-to {
|
||
opacity: 0;
|
||
transform: translateY(-20px);
|
||
}
|
||
|
||
.review-card {
|
||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
||
transition: all 0.3s ease;
|
||
border-radius: 16px !important;
|
||
}
|
||
|
||
.review-card::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
border-radius: inherit;
|
||
padding: 1px;
|
||
background: linear-gradient(135deg, rgba(173, 255, 91, 0.1), transparent 50%);
|
||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||
-webkit-mask-composite: xor;
|
||
mask-composite: exclude;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.line-clamp-2 {
|
||
display: -webkit-box;
|
||
-webkit-box-orient: vertical;
|
||
-webkit-line-clamp: 2;
|
||
line-clamp: 2;
|
||
overflow: hidden;
|
||
}
|
||
</style>
|