feat: 添加横幅轮播和公告功能,支持自动切换和点击事件
This commit is contained in:
BIN
src/assets/images/home-banner2.jpg
Normal file
BIN
src/assets/images/home-banner2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 150 KiB |
@@ -3,8 +3,10 @@ import type { Treaty } from "@elysiajs/eden";
|
||||
import type { InfiniteScrollCustomEvent } from "@ionic/vue";
|
||||
import type { Action } from "./";
|
||||
import type { TreatyQuery } from "@/api/types";
|
||||
import { chevronForwardOutline, eyeOutline, timeOutline } from "ionicons/icons";
|
||||
import { chevronForwardOutline, eyeOutline, megaphoneOutline, timeOutline } from "ionicons/icons";
|
||||
import { client, safeClient } from "@/api";
|
||||
import banner2 from "@/assets/images/home-banner2.jpg?url";
|
||||
import banner1 from "@/assets/images/home-banner.jpg?url";
|
||||
import { actions } from "./";
|
||||
|
||||
type NewsItem = Treaty.Data<typeof client.api.news.get>["data"][number];
|
||||
@@ -18,11 +20,58 @@ const [query] = useResetRef<NewsQuery>({
|
||||
const data = ref<NewsItem[]>([]);
|
||||
const isFinished = ref(false);
|
||||
|
||||
const banners = ref([
|
||||
{ id: 1, image: banner1, title: "横幅1" },
|
||||
{ id: 2, image: banner2, title: "横幅2" },
|
||||
]);
|
||||
const currentBannerIndex = ref(0);
|
||||
|
||||
// 公告数据
|
||||
interface Announcement {
|
||||
id: number;
|
||||
title: string;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
const announcements = ref<Announcement[]>([
|
||||
{
|
||||
id: 1,
|
||||
title: "欢迎使用我们的服务平台,祝您投资顺利!",
|
||||
onClick: () => {
|
||||
console.log("点击了第一条公告");
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
// 横幅自动切换
|
||||
let bannerTimer: ReturnType<typeof setInterval> | null = null;
|
||||
function startBannerCarousel() {
|
||||
bannerTimer = setInterval(() => {
|
||||
currentBannerIndex.value = (currentBannerIndex.value + 1) % banners.value.length;
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
function stopBannerCarousel() {
|
||||
if (bannerTimer) {
|
||||
clearInterval(bannerTimer);
|
||||
bannerTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
startBannerCarousel();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
stopBannerCarousel();
|
||||
});
|
||||
|
||||
async function fetchNews() {
|
||||
const { data: responseData } = await safeClient(client.api.news.get({ query: { ...query.value } }));
|
||||
data.value.push(...(responseData.value?.data || []));
|
||||
isFinished.value = responseData.value?.pagination.hasNextPage === false;
|
||||
}
|
||||
|
||||
async function handleInfinite(event: InfiniteScrollCustomEvent) {
|
||||
if (isFinished.value) {
|
||||
event.target.complete();
|
||||
@@ -35,6 +84,7 @@ async function handleInfinite(event: InfiniteScrollCustomEvent) {
|
||||
event.target.complete();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function handleQuickAction(action: Action) {
|
||||
switch (action.id) {
|
||||
case "check_in":
|
||||
@@ -67,8 +117,50 @@ function handleNewsClick(news: NewsItem) {
|
||||
</ion-header>
|
||||
<ion-content :fullscreen="true" class="home-page">
|
||||
<div class="ion-padding-horizontal">
|
||||
<!-- 横幅轮播 -->
|
||||
<div class="rounded-2xl overflow-hidden relative mt-4 shadow-md">
|
||||
<img src="@/assets/images/home-banner.jpg" class="h-50 w-full object-cover" alt="首页横幅">
|
||||
<div class="relative h-50 w-full">
|
||||
<transition-group name="banner-fade">
|
||||
<img
|
||||
v-for="(banner, index) in banners"
|
||||
v-show="index === currentBannerIndex"
|
||||
:key="banner.id"
|
||||
:src="banner.image"
|
||||
class="absolute inset-0 h-full w-full object-cover"
|
||||
:alt="banner.title"
|
||||
>
|
||||
</transition-group>
|
||||
</div>
|
||||
<!-- 指示点 -->
|
||||
<div class="absolute bottom-3 left-1/2 -translate-x-1/2 flex gap-2 z-10">
|
||||
<div
|
||||
v-for="(banner, index) in banners"
|
||||
:key="banner.id"
|
||||
class="w-2 h-2 rounded-full transition-all duration-300 cursor-pointer"
|
||||
:class="index === currentBannerIndex ? 'bg-white w-4' : 'bg-white/50'"
|
||||
@click="currentBannerIndex = index"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 公告栏 -->
|
||||
<div
|
||||
v-for="announcement in announcements"
|
||||
:key="announcement.id"
|
||||
class="mt-3 bg-linear-to-r from-[#fff7e6] to-[#fffbe6] rounded-xl px-4 py-2.5 flex items-center gap-2 overflow-hidden"
|
||||
>
|
||||
<ion-icon :icon="megaphoneOutline" class="text-lg text-[#fa8c16] shrink-0" />
|
||||
<div class="flex-1 overflow-hidden">
|
||||
<div class="announcement-scroll">
|
||||
<div
|
||||
|
||||
class="announcement-item text-sm text-[#d48806] cursor-pointer hover:text-[#fa8c16] transition-colors"
|
||||
@click="announcement.onClick?.()"
|
||||
>
|
||||
{{ announcement.title }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="my-5 grid grid-cols-4 gap-4">
|
||||
@@ -149,128 +241,52 @@ function handleNewsClick(news: NewsItem) {
|
||||
</template>
|
||||
|
||||
<style lang='css' scoped>
|
||||
/* 中国风图案 */
|
||||
.chinese-pattern {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.pattern-svg {
|
||||
position: absolute;
|
||||
animation: rotate-pattern 30s linear infinite;
|
||||
}
|
||||
|
||||
.pattern-1 {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
top: 20px;
|
||||
right: 30px;
|
||||
}
|
||||
|
||||
.pattern-2 {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
top: 100px;
|
||||
left: 40px;
|
||||
animation-direction: reverse;
|
||||
animation-duration: 40s;
|
||||
}
|
||||
|
||||
@keyframes rotate-pattern {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 五角星装饰 */
|
||||
.stars-decoration {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.star {
|
||||
position: absolute;
|
||||
color: rgba(255, 215, 0, 0.3);
|
||||
font-size: 24px;
|
||||
animation: twinkle 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.star-1 {
|
||||
top: 40px;
|
||||
left: 25%;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.star-2 {
|
||||
top: 80px;
|
||||
right: 30%;
|
||||
font-size: 18px;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
.star-3 {
|
||||
top: 130px;
|
||||
left: 15%;
|
||||
font-size: 20px;
|
||||
animation-delay: 2s;
|
||||
}
|
||||
|
||||
@keyframes twinkle {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0.3;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.7;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
}
|
||||
|
||||
/* 底部波浪 */
|
||||
.wave-bottom {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* 新闻标签渐变 - 使用 @apply 不支持渐变,需要保留 */
|
||||
/* 新闻标签渐变 */
|
||||
.news-badge {
|
||||
box-shadow: 0 2px 8px rgba(196, 30, 58, 0.3);
|
||||
}
|
||||
|
||||
/* 走马灯动画 */
|
||||
.announcement-carousel {
|
||||
transition: height 0.3s ease;
|
||||
/* 横幅轮播动画 */
|
||||
.banner-fade-enter-active,
|
||||
.banner-fade-leave-active {
|
||||
transition: opacity 0.8s ease;
|
||||
}
|
||||
|
||||
.slide-enter-active,
|
||||
.slide-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.slide-enter-from {
|
||||
transform: translateX(100%);
|
||||
.banner-fade-enter-from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-leave-to {
|
||||
transform: translateX(-100%);
|
||||
.banner-fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-enter-to,
|
||||
.slide-leave-from {
|
||||
transform: translateX(0);
|
||||
.banner-fade-enter-to,
|
||||
.banner-fade-leave-from {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 公告滚动动画 */
|
||||
.announcement-scroll {
|
||||
display: flex;
|
||||
animation: scroll-announcement 5s linear infinite;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.announcement-item {
|
||||
padding-right: 100px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@keyframes scroll-announcement {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-33.333%);
|
||||
}
|
||||
}
|
||||
|
||||
.announcement-scroll:hover {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user