ufutx-pc-website/src/views/Home/sections/CustomerFeedback.vue
2025-09-28 16:09:59 +08:00

271 lines
8.1 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>
<section class="feedback-section">
<div class="feedback-title">客户反馈</div>
<!-- 跑马灯容器去掉换行强制内容单行显示 -->
<div class="feedback-list">
<Marquee
:duration="360"
:reverse="false"
repeatCount="3"
pause-on-hover
container-class="h-56 bg-white rounded-lg shadow-sm p-4"
>
<!-- 内容卡片直接作为Marquee的子元素不嵌套额外div -->
<div v-for="(item, index) in feedbackList[0]" :key="`${item.username}-${index}`" class="feedback-card">
<el-popover placement="right-end" title="" :width="320" trigger="hover" popper-class="custom-popover">
<template #default>
<div class="_userinfo">
<div class="_userPic"><img :src="item.avatar" alt="avatar" class="avatar" /></div>
<div class="_username">{{ item.username }}</div>
</div>
<TextGenerateEffect class="_comment" :words="item.feedback" />
</template>
<template #reference>
<div class="popover-container">
<div class="avatar-container">
<img :src="item.avatar" alt="avatar" class="avatar" />
</div>
<div class="feedback-info">
<p class="username">{{ item.username }}</p>
<p class="comment">{{ item.feedback }}</p>
</div>
</div>
</template>
</el-popover>
</div>
</Marquee>
</div>
<div class="feedback-list" style="margin-top: 40px">
<Marquee
:duration="360"
:reverse="true"
repeatCount="3"
pause-on-hover
container-class="h-56 bg-white rounded-lg shadow-sm p-4"
>
<!-- 内容卡片直接作为Marquee的子元素不嵌套额外div -->
<div v-for="(item, index) in feedbackList[1]" :key="`${item.username}-${index}`" class="feedback-card">
<el-popover placement="right-end" title="" :width="320" trigger="hover" popper-class="custom-popover">
<template #default>
<div class="_userinfo">
<div class="_userPic"><el-image :src="item.avatar" alt="avatar" class="avatar" /></div>
<div class="_username">{{ item.username }}</div>
</div>
<TextGenerateEffect class="_comment" :words="item.feedback" />
</template>
<template #reference>
<div class="popover-container">
<div class="avatar-container">
<img :src="item.avatar" alt="avatar" class="avatar" />
</div>
<div class="feedback-info">
<p class="username">{{ item.username }}</p>
<p class="comment">{{ item.feedback }}</p>
</div>
</div>
</template>
</el-popover>
</div>
</Marquee>
</div>
</section>
</template>
<script setup lang="ts">
// 模拟假数据(头像统一使用你提供的链接)
import Marquee from '@/components/Marquee.vue'
import TextGenerateEffect from '@/components/TextGenerateEffect.vue'
import { userFeedbacks } from '@/data/userFeedbacks.ts'
// 添加泛型类型 T 表示数组元素的类型
function splitArrayRandomly<T>(arr: T[], ratio = 0.5): [T[], T[]] {
// 复制原数组避免修改
const shuffled = [...arr]
// Fisher-Yates 洗牌算法
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]
}
// 计算分割点(默认为 50%
const splitIndex = Math.round(shuffled.length * ratio)
// 分割数组
return [shuffled.slice(0, splitIndex), shuffled.slice(splitIndex)]
}
const feedbackList = reactive(splitArrayRandomly(userFeedbacks))
console.log('数组1:', feedbackList[0]) // 例如: [3, 7, 2, 5]
</script>
<style scoped lang="less">
// 基础变量
@bg-color: #f9fbff;
@card-bg: #ffffff;
@radius: 12px;
@padding: 16px;
@avatar-size: 60px;
@subtext-color: #999;
@transition: all 0.3s ease;
.feedback-section {
background-color: @bg-color;
padding: 0 0 100px 0; /* 改用标准padding写法避免自定义.px()可能的问题 */
text-align: center;
//background: bisque;
position: relative;
&&::before {
content: '';
position: absolute;
top: 0;
height: 100%;
width: 60px;
z-index: 1;
pointer-events: none;
}
&&::before {
left: 100px;
background: linear-gradient(to right, #f9fbff, transparent);
}
.feedback-title {
font-size: 28px;
font-weight: 600;
padding: 100px 0 60px;
}
// 跑马灯容器:关键修改——禁止换行,确保内容单行滚动
.feedback-list {
//width: 100vw;
max-width: 1820px;
margin: 0 auto;
margin-left: 100px;
box-sizing: border-box;
// 关键:强制水平布局,禁止换行
padding: 10px;
display: flex;
flex-direction: row; /* 明确水平方向 */
flex-wrap: nowrap; /* 禁止换行 */
overflow: hidden; /* 隐藏超出容器的部分 */
}
// 反馈卡片:固定宽度,确保排列整齐
.feedback-card {
.feedback-popper {
border-radius: 320px !important;
}
width: 320px; /* 固定宽度,确保能在一行放下多个 */
//max-width: 320px;
flex-shrink: 0; /* 禁止收缩,保证宽度不变 */
border-radius: 16px;
background: #fff;
//transition: @transition;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); /* 增加轻微阴影,提升视觉层次 */
margin-right: 50px;
cursor: pointer; /* 提示可交互 */
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); /* 卡片整体过渡 */
&:hover {
transform: translateY(-4px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
}
.popover-container {
padding: @padding;
display: flex; /* 卡片内部水平排列头像和文字 */
flex-direction: row; /* 明确水平方向 */
align-items: center;
}
.avatar-container {
flex-shrink: 0;
margin-right: 12px;
.avatar {
width: @avatar-size;
height: @avatar-size;
border-radius: 50%;
object-fit: cover;
border: 2px solid #f0f0f0;
}
}
.feedback-info {
text-align: left;
max-width: calc(100% - @avatar-size - 12px); /* 限制文本区域宽度 */
.username {
font-size: 16px;
font-weight: 500;
margin-bottom: 6px;
white-space: nowrap; /* 用户名不换行 */
overflow: hidden;
text-overflow: ellipsis;
}
.comment {
font-size: 14px;
color: @subtext-color;
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 2; /* 固定2行文本 */
-webkit-box-orient: vertical;
overflow: hidden;
/* 过渡动画重点控制max-height */
max-height: 42px; /* 14px*1.5*2=42px刚好2行高度 */
transition: max-height 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
}
}
}
// 响应式适配
//@media (max-width: 768px) {
// .feedback-section {
// padding: 0 20px 60px; /* 缩小移动端内边距 */
// }
// .feedback-card {
// width: 220px; /* 移动端缩小卡片宽度 */
// }
//}
</style>
<style lang="less">
.custom-popover {
/* 修改圆角核心 */
border-radius: 18px !important; /* 根据需求调整数值如8px16px */
/* 可选修改边框 */
border: 1px solid #e5e7eb !important;
/* 可选修改阴影 */
box-shadow: 0 4px 16px rgba(79, 79, 79, 0.08) !important;
/* 可选修改内部边距 */
padding: 22px !important;
._userinfo {
.flex();
align-items: center;
.mb(12px);
}
._userPic {
width: 62px;
height: 62px;
border-radius: 50%;
overflow: hidden;
border: 4px solid #f9fbff;
box-shadow: 0 0 16px rgba(49, 49, 49, 0.18);
.mr(10px);
}
._username {
font-size: 20px;
font-weight: 600;
color: @text-color;
}
._comment {
font-size: 16px;
letter-spacing: 1px;
color: @text-color-secondary;
}
}
//@media (max-width: @tablet-breakpoint) { }
</style>