feat(首页): 使用 Swiper替换 Element UI 轮播组件
- 移除 Element UI 的 el-carousel 组件 -引入 Swiper 库并实现轮播功能 - 优化轮播项的样式和布局 - 添加自定义左右按钮和分页器 - 实现视频自动播放和轮播同步 - 优化轮播切换效果,支持 fade切换
22094
package-lock.json
generated
@@ -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",
|
||||||
|
|||||||
BIN
src/assets/img/home/arrow-act.png
Normal file
|
After Width: | Height: | Size: 342 B |
BIN
src/assets/img/home/arrow-dis.png
Normal file
|
After Width: | Height: | Size: 315 B |
|
Before Width: | Height: | Size: 381 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 664 B After Width: | Height: | Size: 458 B |
|
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 585 B |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 273 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 178 B After Width: | Height: | Size: 148 B |
|
Before Width: | Height: | Size: 190 B After Width: | Height: | Size: 169 B |
|
Before Width: | Height: | Size: 160 B After Width: | Height: | Size: 152 B |
BIN
src/assets/img/home/video1.mp4
Normal file
BIN
src/assets/img/home/video2.mp4
Normal file
BIN
src/assets/img/home/video3.mp4
Normal file
BIN
src/assets/img/home/video4.mp4
Normal file
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 755 B After Width: | Height: | Size: 464 B |
@@ -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>
|
||||||
|
|||||||