first1
This commit is contained in:
128
src/views/home/cpns/HomeBanner.vue
Normal file
128
src/views/home/cpns/HomeBanner.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import type { BannerVo } from '@/types/business/home'
|
||||
|
||||
interface Props {
|
||||
banners: BannerVo[]
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
/** 仅保留启用且带图的轮播项,按后端 sort 升序 */
|
||||
const slides = computed(() =>
|
||||
props.banners
|
||||
.filter((item) => item.enabled !== false && !!item.imageUrl)
|
||||
.slice()
|
||||
.sort((prev, next) => (prev.sort ?? 0) - (next.sort ?? 0)),
|
||||
)
|
||||
|
||||
const current = ref(0)
|
||||
let timer: number | undefined
|
||||
|
||||
function stop(): void {
|
||||
if (timer !== undefined) {
|
||||
window.clearInterval(timer)
|
||||
timer = undefined
|
||||
}
|
||||
}
|
||||
|
||||
function go(index: number): void {
|
||||
const total = slides.value.length
|
||||
if (total === 0) return
|
||||
current.value = (index + total) % total
|
||||
}
|
||||
|
||||
function start(): void {
|
||||
stop()
|
||||
if (slides.value.length <= 1) return
|
||||
timer = window.setInterval(() => go(current.value + 1), 5000)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => slides.value.length,
|
||||
() => {
|
||||
current.value = 0
|
||||
start()
|
||||
},
|
||||
)
|
||||
|
||||
onMounted(start)
|
||||
onBeforeUnmount(stop)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section v-if="slides.length" class="home-banner">
|
||||
<img
|
||||
v-for="(slide, index) in slides"
|
||||
v-show="index === current"
|
||||
:key="slide.id ?? index"
|
||||
class="home-banner__img"
|
||||
:src="slide.imageUrl ?? ''"
|
||||
:alt="`轮播图 ${index + 1}`"
|
||||
>
|
||||
|
||||
<template v-if="slides.length > 1">
|
||||
<button
|
||||
class="home-banner__arrow home-banner__arrow--prev"
|
||||
type="button"
|
||||
aria-label="上一张"
|
||||
@click="go(current - 1)"
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
<button
|
||||
class="home-banner__arrow home-banner__arrow--next"
|
||||
type="button"
|
||||
aria-label="下一张"
|
||||
@click="go(current + 1)"
|
||||
>
|
||||
›
|
||||
</button>
|
||||
</template>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.home-banner {
|
||||
position: relative;
|
||||
height: 240px;
|
||||
overflow: hidden;
|
||||
border-radius: @radius-lg;
|
||||
background: @bg-color;
|
||||
box-shadow: @shadow-base;
|
||||
|
||||
&__img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
&__arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
transform: translateY(-50%);
|
||||
border-radius: @radius-round;
|
||||
background: rgba(31, 39, 51, 0.35);
|
||||
color: @white;
|
||||
font-size: @font-size-xl;
|
||||
line-height: 1;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
|
||||
&--prev {
|
||||
left: @spacing-base;
|
||||
}
|
||||
|
||||
&--next {
|
||||
right: @spacing-base;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover &__arrow {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user