speakeloudest 784cabaddc
All checks were successful
site-dist-deploy / build-and-deploy (push) Successful in 2m9s
增加评论样式
2026-03-23 12:09:21 +02:00

181 lines
5.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>