From 21371061ed5f545ac04c022912b9c9a420ca3bd3 Mon Sep 17 00:00:00 2001 From: richjohnrambo Date: Mon, 19 Oct 2026 09:34:26 +0800 Subject: [PATCH] feat: improve home notice ticker --- web/src/mobile/styles.css | 38 ++++++++++++++++++++++++ web/src/mobile/views/HomeView.vue | 48 +++++++++++++++++++++++++++---- 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/web/src/mobile/styles.css b/web/src/mobile/styles.css index 2a7b200..e02f496 100644 --- a/web/src/mobile/styles.css +++ b/web/src/mobile/styles.css @@ -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; diff --git a/web/src/mobile/views/HomeView.vue b/web/src/mobile/views/HomeView.vue index 7d2e192..760909a 100644 --- a/web/src/mobile/views/HomeView.vue +++ b/web/src/mobile/views/HomeView.vue @@ -53,10 +53,22 @@ -
+
+ + {{ currentNotice.source_type === 'operational_simulation' ? '活动' : '公告' }} + +

+ {{ currentNotice.title || currentNotice.content || '查看最新通知' }} +

+ +
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; 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([]); const popupNotice = ref(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); });