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 { InfiniteScrollCustomEvent } from "@ionic/vue";
|
||||||
import type { Action } from "./";
|
import type { Action } from "./";
|
||||||
import type { TreatyQuery } from "@/api/types";
|
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 { 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 "./";
|
import { actions } from "./";
|
||||||
|
|
||||||
type NewsItem = Treaty.Data<typeof client.api.news.get>["data"][number];
|
type NewsItem = Treaty.Data<typeof client.api.news.get>["data"][number];
|
||||||
@@ -18,11 +20,58 @@ const [query] = useResetRef<NewsQuery>({
|
|||||||
const data = ref<NewsItem[]>([]);
|
const data = ref<NewsItem[]>([]);
|
||||||
const isFinished = ref(false);
|
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() {
|
async function fetchNews() {
|
||||||
const { data: responseData } = await safeClient(client.api.news.get({ query: { ...query.value } }));
|
const { data: responseData } = await safeClient(client.api.news.get({ query: { ...query.value } }));
|
||||||
data.value.push(...(responseData.value?.data || []));
|
data.value.push(...(responseData.value?.data || []));
|
||||||
isFinished.value = responseData.value?.pagination.hasNextPage === false;
|
isFinished.value = responseData.value?.pagination.hasNextPage === false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleInfinite(event: InfiniteScrollCustomEvent) {
|
async function handleInfinite(event: InfiniteScrollCustomEvent) {
|
||||||
if (isFinished.value) {
|
if (isFinished.value) {
|
||||||
event.target.complete();
|
event.target.complete();
|
||||||
@@ -35,6 +84,7 @@ async function handleInfinite(event: InfiniteScrollCustomEvent) {
|
|||||||
event.target.complete();
|
event.target.complete();
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleQuickAction(action: Action) {
|
function handleQuickAction(action: Action) {
|
||||||
switch (action.id) {
|
switch (action.id) {
|
||||||
case "check_in":
|
case "check_in":
|
||||||
@@ -67,8 +117,50 @@ function handleNewsClick(news: NewsItem) {
|
|||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content :fullscreen="true" class="home-page">
|
<ion-content :fullscreen="true" class="home-page">
|
||||||
<div class="ion-padding-horizontal">
|
<div class="ion-padding-horizontal">
|
||||||
|
<!-- 横幅轮播 -->
|
||||||
<div class="rounded-2xl overflow-hidden relative mt-4 shadow-md">
|
<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>
|
</div>
|
||||||
|
|
||||||
<section class="my-5 grid grid-cols-4 gap-4">
|
<section class="my-5 grid grid-cols-4 gap-4">
|
||||||
@@ -149,128 +241,52 @@ function handleNewsClick(news: NewsItem) {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang='css' scoped>
|
<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 {
|
.news-badge {
|
||||||
box-shadow: 0 2px 8px rgba(196, 30, 58, 0.3);
|
box-shadow: 0 2px 8px rgba(196, 30, 58, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 走马灯动画 */
|
/* 横幅轮播动画 */
|
||||||
.announcement-carousel {
|
.banner-fade-enter-active,
|
||||||
transition: height 0.3s ease;
|
.banner-fade-leave-active {
|
||||||
|
transition: opacity 0.8s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slide-enter-active,
|
.banner-fade-enter-from {
|
||||||
.slide-leave-active {
|
|
||||||
transition: all 0.5s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide-enter-from {
|
|
||||||
transform: translateX(100%);
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slide-leave-to {
|
.banner-fade-leave-to {
|
||||||
transform: translateX(-100%);
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slide-enter-to,
|
.banner-fade-enter-to,
|
||||||
.slide-leave-from {
|
.banner-fade-leave-from {
|
||||||
transform: translateX(0);
|
|
||||||
opacity: 1;
|
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>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user