目标效果
实现一个类似慕课网课程详情页的 ImageSwiper 组件,具备以下功能:
- 顶部 Tab 切换:点击「通识强化」「进阶提升」「高阶跃迁」「拓展深耕」切换对应幻灯片
- 底部圆点分页:灰色圆点表示未选中,橙色圆点表示当前激活项
- 左右导航箭头:圆形白色按钮,带阴影,hover 时颜色加深
- 响应式高度:根据窗口宽度等比缩放幻灯片高度
- 循环播放:切换到最后一项后自动回到第一项
组件设计与文件结构
components/
ImageSwiper.vue # 图片轮播组件(自动注册)
text
组件通过 unplugin-vue-components 实现自动注册,可直接在 .md 文件中使用,无需手动 import。
Props 设计
const props = defineProps<{
items: string // JSON 字符串,包含图片链接数组
titles: string // JSON 字符串,包含标题数组
height?: number // 组件高度,默认 1200
}>()
ts
在 Markdown 中调用:
<ImageSwiper
:items="JSON.stringify([...])"
:titles="JSON.stringify(['通识强化', '进阶提升', '高阶跃迁', '拓展深耕'])"
:height="700"
/>
html
组件模板结构
<template>
<!-- 顶部 Tab 切换 -->
<div class="py-4">
<ul class="flex justify-evenly items-center w-full">
<li
v-for="(item, index) in parsedTitles"
:key="index"
class="flex flex-col items-center cursor-pointer transition-all"
:class="{ active: activeIndex === index }"
@click="handleTabClick(index)"
>
<div class="text-2xl border-b-2 px-2 pb-2">{{ index + 1 }}</div>
<div class="pt-4">{{ item }}</div>
</li>
</ul>
</div>
<!-- Swiper 轮播主体 -->
<Swiper
:modules="[Navigation, Pagination]"
:slides-per-view="1"
:space-between="0"
:loop="true"
:pagination="{
clickable: true,
bulletClass: '...',
bulletActiveClass: '...',
}"
@swiper="onSwiper"
>
<SwiperSlide v-for="(item, index) in parsedItems" :key="index">
<img :src="item.image" class="bg-contain w-full" />
</SwiperSlide>
</Swiper>
<!-- 左箭头 -->
<div class="group absolute left-2 top-1/2 -translate-y-1/2 z-30
rounded-1/2 bg-white shadow-lg w-15 h-15
flex items-center cursor-pointer">
<Icon icon="ep:arrow-left" class="text-gray-300 group-hover:text-gray-600" />
</div>
<!-- 右箭头 -->
<div class="group absolute right-2 top-1/2 -translate-y-1/2 z-30
rounded-1/2 bg-white shadow-lg w-15 h-15
flex items-center cursor-pointer">
<Icon icon="ep:arrow-right" class="text-gray-300 group-hover:text-gray-600" />
</div>
</template>
html
Script 逻辑实现
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useWindowSize } from '@vueuse/core'
import { Swiper, SwiperSlide } from 'swiper/vue'
import { Navigation, Pagination } from 'swiper/modules'
import type { SwiperType } from 'swiper'
const props = withDefaults(defineProps<{
items: string
titles: string
height?: number
}>(), {
height: 1200,
})
const swiperRef = ref<SwiperType>()
const activeIndex = ref(0)
const parsedItems = computed(() => JSON.parse(props.items))
const parsedTitles = computed(() => JSON.parse(props.titles))
const { width } = useWindowSize()
// 响应式高度:基于屏幕宽度与 1200 的比例
const slideHeight = computed(() => {
return (width.value / 1200) * props.height + 'px'
})
function onSwiper(swiper: SwiperType) {
swiperRef.value = swiper
}
function handleTabClick(index: number) {
activeIndex.value = index
swiperRef.value?.slideTo(index, 500)
}
</script>
ts
Pagination 样式自定义
Swiper 的 pagination 配置提供了 bulletClass 和 bulletActiveClass 两个属性,用于自定义分页圆点的样式:
pagination: {
clickable: true,
// 未激活的圆点:灰色小圆
bulletClass: 'inline-block w-3 h-3 rounded-1/2 bg-gray-300 z-20 mr-4 cursor-pointer',
// 激活的圆点:橙色大圆
bulletActiveClass: 'inline-block w-4 h-4 rounded-3 bg-orange-400 z-20 mr-4 cursor-pointer',
}
ts
| 属性 | 说明 |
|---|---|
bulletClass | 所有圆点的公共样式类 |
bulletActiveClass | 当前激活圆点的额外样式类 |
clickable | 是否允许点击圆点切换幻灯片 |
Tab 高亮与 Hover 效果
方案一:UnoCSS group 修饰符
<li class="group hover:text-orange-500">
<div class="group-hover:border-b-orange-500">...</div>
</li>
html
方案二:原生 CSS(更精确的控制)
.item:hover .line,
.item:hover .text {
color: orange;
border-color: orange;
}
css
Active 状态通过 activeIndex 与当前索引比较来控制:
const isActive = computed(() => activeIndex.value === index)
ts
<li :class="{ active: activeIndex === index }" @click="handleTabClick(index)">
html
响应式高度实现
使用 VueUse 的 useWindowSize 获取实时窗口宽度,结合预设基准值计算等比高度:
const { width } = useWindowSize()
const slideHeight = computed(() => {
// 基准宽度 1200px,按比例缩放
return (width.value / 1200) * props.height + 'px'
})
ts
模板中绑定 style:
<div :style="{ height: slideHeight }">
<!-- Swiper 内容 -->
</div>
html
这样无论窗口如何缩放,幻灯片高度始终按等比例变化,保证视觉效果一致。
循环播放
在 Swiper 组件上添加 loop 属性即可实现循环切换:
<Swiper :loop="true">
html
当幻灯片切换到最后一项时,继续点击下一张会自动回到第一项。
关键知识点总结
| 知识点 | 说明 |
|---|---|
Swiper slideTo(index, speed) | 通过 API 控制幻灯片跳转 |
Swiper @swiper 事件 | 获取 Swiper 实例引用 |
bulletClass / bulletActiveClass | 自定义分页圆点样式 |
useWindowSize() | VueUse 提供的响应式窗口尺寸 |
UnoCSS group / group-hover | 父元素 hover 控制子元素样式 |
loop: true | 启用循环播放模式 |
| 组件自动注册 | 通过 unplugin-vue-components 在 Markdown 中直接使用组件 |
↑