feat(首页): 使用 Swiper替换 Element UI 轮播组件

- 移除 Element UI 的 el-carousel 组件
-引入 Swiper 库并实现轮播功能
- 优化轮播项的样式和布局
- 添加自定义左右按钮和分页器
- 实现视频自动播放和轮播同步
- 优化轮播切换效果,支持 fade切换
This commit is contained in:
du.meimei
2025-05-24 17:53:08 +08:00
parent db4b68b7c3
commit 9c8d8ee218
27 changed files with 4325 additions and 18063 deletions

22094
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -43,6 +43,7 @@
"shrinkpng": "1.1.0", "shrinkpng": "1.1.0",
"signature_pad": "4.0.3", "signature_pad": "4.0.3",
"sortablejs": "1.15.0", "sortablejs": "1.15.0",
"swiper": "^6.8.4",
"three": "0.145.0", "three": "0.145.0",
"tinymce": "5.10.7", "tinymce": "5.10.7",
"uuid": "8.3.2", "uuid": "8.3.2",

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 381 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 664 B

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 B

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 B

After

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 B

After

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 755 B

After

Width:  |  Height:  |  Size: 464 B

View File

@@ -4,76 +4,115 @@
<p class="fw-bold fs-24 title">简单4步轻松完成你的调研</p> <p class="fw-bold fs-24 title">简单4步轻松完成你的调研</p>
<p class="desc">创建编辑投放分析让洞察更简单</p> <p class="desc">创建编辑投放分析让洞察更简单</p>
</div> </div>
<div style="height: 100%"> <div style="height: 80%">
<el-carousel <div class="swiper-container" ref="swiperContainer">
:autoplay="true" <div class="swiper-wrapper">
:interval="13000" <div
arrow="always" v-for="(item, index) in operatingList"
indicator-position="none" :key="index"
style="height: 100%"> class="swiper-slide"
<el-carousel-item v-for="(item,index) in operatingList" :key="item" > >
<div class="flex" style="justify-content: center"> <div class="flex" style="justify-content: center">
<div style="margin-right: 9%"> <div style="margin-right: 9%">
<div class="flex-start operating-item" > <div class="flex-start operating-item" >
<p class="progress" :style="{'color':item.btnColor}"> <p class="progress" :style="{'color':item.btnColor}">
<span class="fs-36"> <span class="fs-36">
{{index+1}} {{index+1}}
</span> </span>
<img src="@/assets/img/home/xiegang.png" alt=""> <img src="@/assets/img/home/xiegang.png" alt="">
<span> <span>
{{operatingList.length}} {{operatingList.length}}
</span> </span>
</p> </p>
<div class="info"> <div class="info">
<p>{{item.title}}</p> <p>{{item.title}}</p>
<p>{{item.desc}}</p> <p>{{item.desc}}</p>
</div>
</div> </div>
</div> <ul class="desc-item">
<ul class="desc-item"> <li class="flex" v-for="(tips,index) in item.descList" :key="index">
<li class="flex" v-for="(tips,index) in item.descList" :key="index"> <p class="block"></p>
<p class="block"></p> {{tips}}
{{tips}} <span class="fs-12 tag" v-if="index === 0 && item.isShowTag">
<span class="fs-12 tag" v-if="index === 0 && item.isShowTag">
新功能 新功能
</span> </span>
</li> </li>
</ul> </ul>
<a-button type="primary" @click="toSurveyInfo(item,index)"> <a-button type="primary" @click="toSurveyInfo(item,index)">
立即体验 立即体验
<img src="@/assets/img/home/tow.png" alt="" <img src="@/assets/img/home/tow.png" alt=""
style="margin-left: 5px;margin-bottom: 2px"> style="margin-left: 5px;margin-bottom: 2px">
</a-button> </a-button>
</div> </div>
<div> <div>
<video <video
ref="videoPlayer" ref="videoPlayer"
width="720" class="video-player"
height="380" width="820"
autoplay height="420"
muted autoplay
playsinline muted
style="width: 100%;"> playsinline
style="width: 100%;">
<source src="@/assets/img/home/viedeo.mp4" type="video/mp4"> <source :src="getVideo(index)" type="video/mp4">
Your browser does not support the video tag. Your browser does not support the video tag.
</video> </video>
</div>
</div> </div>
</div> </div>
</div>
</el-carousel-item> <!-- 分页器 -->
</el-carousel> <div class="swiper-pagination" ref="pagination"></div>
<!-- 自定义左右按钮 -->
<div class="custom-swiper-button-prev" ref="prevBtn" @click="goPre">
<img ref="prevImg" src="@/assets/img/home/kanoicon.png" alt="Previous" />
</div>
<div class="custom-swiper-button-next" ref="nextBtn" @click="goNext">
<img ref="nextImg" src="@/assets/img/home/pkicon.png" alt="Next" />
</div>
</div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import {ref, onMounted} from 'vue' import {ref, onMounted, onBeforeUnmount} from 'vue'
import Swiper from 'swiper';
import 'swiper/swiper-bundle.css';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { getQueryUserSurvey } from '@/api/home'; import { getQueryUserSurvey } from '@/api/home';
import { useStore } from 'vuex';
const router = useRouter(); const router = useRouter();
const emit = defineEmits(['create-survey']); const emit = defineEmits(['create-survey']);
const operatingList = ref([{
const swiperContainer = ref(null);
const pagination = ref(null);
const prevBtn = ref(null);
const nextBtn = ref(null);
const prevImg = ref(null);
const nextImg = ref(null);
// 🟩 存储 Swiper 实例
let mySwiper = null;
const goPre = () => {
if (mySwiper) {
mySwiper.slidePrev(); // 👈 使用实例方法
}
};
const goNext = () => {
if (mySwiper) {
mySwiper.slideNext(); // 👈 使用实例方法
}
};
const operatingList = ref(
[
{
title: '多种方式', title: '多种方式',
desc:'快速创建问卷', desc:'快速创建问卷',
descList:[ descList:[
@@ -136,6 +175,105 @@ const toSurveyInfo = async (item,index) => {
} }
} }
} }
const getVideo = (index) => {
return require(`@/assets/img/home/video${index+1}.mp4`)
}
const addVideoEndedListener =()=> {
const videos = document.querySelectorAll('.video-player');
videos.forEach((video, index) => {
video.addEventListener('ended', () => {
if (index < operatingList.value.length - 1) {
mySwiper.slideNext(); // 自动滑动到下一页
}
});
// // 使用 WeakMap 或 data-* 存储监听器,方便后续移除
// video._onVideoEnd = onVideoEnd;
// video.addEventListener('ended', onVideoEnd);
});
}
/**
* 更新左右图标
* @param currentIndex
*/
const updateButtonImages = (currentIndex) => {
// 获取 next 按钮的 img 元素
const nextImgElement = nextImg.value;
const prevImgElement = prevImg.value;
if (currentIndex === 0) {
prevImg.value.src = require('@/assets/img/home/arrow-dis.png');
nextImg.value.src = require('@/assets/img/home/arrow-act.png');
prevImgElement.style.transform = 'rotate(0deg)';
} else if (currentIndex === 3) {
nextImg.value.src = require('@/assets/img/home/arrow-dis.png');
nextImgElement.style.transform = 'rotate(180deg)';
}else{
// 其他情况恢复默认图片
prevImg.value.src = require('@/assets/img/home/arrow-act.png');
nextImg.value.src = require('@/assets/img/home/arrow-act.png');
prevImgElement.style.transform = 'rotate(180deg)';
nextImgElement.style.transform = 'rotate(0deg)';
}
};
/**
* 自动播放视频
*/
const autoPlayCurrentVideo = () => {
const activeSlide = mySwiper.slides[mySwiper.realIndex];
if (activeSlide) {
const video = activeSlide.querySelector('.video-player');
if (video) {
video.play().catch(err => {
console.warn('视频自动播放失败(可能被浏览器策略阻止)', err);
});
}
}
};
onMounted(() => {
// 🟩 初始化 Swiper 并赋值给 mySwiper
mySwiper = new Swiper(swiperContainer.value, {
loop: false,
autoplay: {
delay: 1300,
},
pagination: {
el: pagination.value,
clickable: true,
},
navigation: {
nextEl: nextBtn.value,
prevEl: prevBtn.value,
},
effect: 'fade',
fadeEffect: {
// crossFade: true, // 启用交叉淡出
},
simulateTouch: false, // 禁止滑动切换
on: {
init: () => {
updateButtonImages(0)
addVideoEndedListener(); // 初始化时添加监听器
},
slideChange: () => {
updateButtonImages(mySwiper.realIndex)
autoPlayCurrentVideo(); // 每次切换时播放当前页视频
},
},
});
});
// 在组件卸载时清理监听器
onBeforeUnmount(() => {
const videos = document.querySelectorAll('.video-player');
videos.forEach(video => {
if (video._onVideoEnd) {
video.removeEventListener('ended', video._onVideoEnd);
}
});
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@@ -154,9 +292,8 @@ li{
} }
.operating-container{ .operating-container{
margin-top: 20px; margin-top: 20px;
margin-bottom: 60px; min-height: 600px;
min-height: 500px; height: 600px;
height: 500px;
.top{ .top{
margin-bottom: 40px; margin-bottom: 40px;
} }
@@ -193,6 +330,7 @@ li{
font-size: 24px; font-size: 24px;
font-weight: bold; font-weight: bold;
margin-left: 20px; margin-left: 20px;
white-space: nowrap;
} }
} }
.desc-item{ .desc-item{
@@ -237,4 +375,55 @@ li{
:deep(.el-carousel__container) { :deep(.el-carousel__container) {
height: 100%; height: 100%;
} }
.swiper-container {
height: 100%;
width: 95%;
margin: 0 auto;
}
.swiper-slide {
//display: flex;
//align-items: center;
//justify-content: center;
font-size: 24px;
height: 100%;
//position: absolute; /* 必须设置为绝对定位才能实现 fade 切换 */
width: 100%;
//top: 0;
//left: 0;
//transition: opacity 1s ease;
}
/* 自定义按钮样式 */
.custom-swiper-button-prev,
.custom-swiper-button-next {
position: absolute;
top: 50%;
transform: translateY(-50%);
z-index: 10;
cursor: pointer;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
//background-color: rgba(0, 0, 0, 0.5);
border-radius: 50%;
}
.custom-swiper-button-prev img,
.custom-swiper-button-next img {
width: 30px;
height: 30px;
filter: brightness(100%);
}
.custom-swiper-button-prev {
left: 0;
}
.custom-swiper-button-next {
right: 0;
}
</style> </style>