forked from dengzhifeng/ufutx-pc-website
271 lines
8.1 KiB
Vue
271 lines
8.1 KiB
Vue
<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; /* 根据需求调整数值,如8px、16px */
|
||
|
||
/* 可选:修改边框 */
|
||
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>
|