feat: improve home notice ticker

This commit is contained in:
richjohnrambo
2026-10-19 09:34:26 +08:00
parent 2e47dd3935
commit 21371061ed
2 changed files with 81 additions and 5 deletions
+38
View File
@@ -524,19 +524,57 @@ body {
font-weight: 500;
}
.m-notice {
width: 100%;
margin-top: 12px;
padding: 12px 16px;
border: 0;
display: flex;
align-items: center;
gap: 9px;
color: #6c5a45;
font: inherit;
text-align: left;
cursor: pointer;
}
.m-notice p {
flex: 1;
min-width: 0;
margin: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.m-notice-tag {
flex: none;
padding: 2px 6px;
border-radius: 4px;
color: #8d5b17;
background: #fff2d8;
font-size: 12px;
line-height: 18px;
}
.m-notice-tag.is-activity {
color: #24704b;
background: #e9f7ef;
}
.m-notice-copy {
animation: m-notice-enter 0.32s ease-out;
}
.m-notice-arrow {
flex: none;
color: #b4a590;
font-size: 17px;
}
@keyframes m-notice-enter {
from {
opacity: 0;
transform: translateY(5px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.m-section {
margin-top: 12px;
padding: 17px 16px;
+43 -5
View File
@@ -53,10 +53,22 @@
</button>
</div>
<div v-if="home?.notices?.length" class="m-notice m-card">
<button
v-if="currentNotice"
class="m-notice m-card"
type="button"
aria-label="查看全部通知"
@click="router.push('/app/content/notices')"
>
<n-icon :component="MegaphoneOutline" />
<p>{{ home.notices[0].title || home.notices[0].content }}</p>
</div>
<span :class="['m-notice-tag', { 'is-activity': currentNotice.source_type === 'operational_simulation' }]">
{{ currentNotice.source_type === 'operational_simulation' ? '活动' : '公告' }}
</span>
<p :key="currentNotice.id" class="m-notice-copy">
{{ currentNotice.title || currentNotice.content || '查看最新通知' }}
</p>
<n-icon class="m-notice-arrow" :component="ChevronForwardOutline" />
</button>
<section class="m-section m-card m-home-ranking">
<div class="m-section-title"
@@ -101,7 +113,7 @@
<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { useRouter } from 'vue-router';
import { SearchOutline, MegaphoneOutline } from '@vicons/ionicons5';
import { ChevronForwardOutline, MegaphoneOutline, SearchOutline } from '@vicons/ionicons5';
import AsyncState from '../components/AsyncState.vue';
import AnnouncementPopup, {
type AnnouncementNotice,
@@ -121,7 +133,7 @@
wallet: { available_amount: number; frozen_amount: number; currency: string };
features: Record<string, boolean>;
banners: BannerData[];
notices: Array<{ title?: string; content?: string }>;
notices: NoticeData[];
task_summary: {
today_count: number;
today_commission_amount: number;
@@ -135,6 +147,12 @@
target_type: 'none' | 'internal_route' | 'external_url';
target_value?: string;
}
interface NoticeData {
id: number;
title?: string;
content?: string;
source_type?: string;
}
interface LeaderboardEntry {
rank: number;
username: string;
@@ -149,12 +167,15 @@
const leaderboard = ref<LeaderboardEntry[]>([]);
const popupNotice = ref<AnnouncementNotice | null>(null);
const activeBannerIndex = ref(0);
const activeNoticeIndex = ref(0);
let bannerTimer: number | undefined;
let noticeTimer: number | undefined;
const formatPoints = (value: number) =>
new Intl.NumberFormat('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(
(value || 0) / 100
);
const currentBanner = computed(() => home.value?.banners?.[activeBannerIndex.value]);
const currentNotice = computed(() => home.value?.notices?.[activeNoticeIndex.value]);
const bannerStyle = computed(() => {
const url = safeImage(currentBanner.value?.image_url);
return {
@@ -234,6 +255,18 @@
}, 4500);
}
}
function restartNoticeTimer() {
if (noticeTimer) window.clearInterval(noticeTimer);
noticeTimer = undefined;
const count = home.value?.notices?.length || 0;
if (count > 1) {
noticeTimer = window.setInterval(() => {
activeNoticeIndex.value = (activeNoticeIndex.value + 1) % count;
}, 4000);
}
}
async function load() {
loading.value = true;
error.value = '';
@@ -269,8 +302,13 @@
activeBannerIndex.value = 0;
restartBannerTimer();
});
watch(() => home.value?.notices?.length, () => {
activeNoticeIndex.value = 0;
restartNoticeTimer();
});
onMounted(load);
onBeforeUnmount(() => {
if (bannerTimer) window.clearInterval(bannerTimer);
if (noticeTimer) window.clearInterval(noticeTimer);
});
</script>