feat: improve home notice ticker
This commit is contained in:
@@ -524,19 +524,57 @@ body {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
.m-notice {
|
.m-notice {
|
||||||
|
width: 100%;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
|
border: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 9px;
|
gap: 9px;
|
||||||
color: #6c5a45;
|
color: #6c5a45;
|
||||||
|
font: inherit;
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.m-notice p {
|
.m-notice p {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
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 {
|
.m-section {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
padding: 17px 16px;
|
padding: 17px 16px;
|
||||||
|
|||||||
@@ -53,10 +53,22 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</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" />
|
<n-icon :component="MegaphoneOutline" />
|
||||||
<p>{{ home.notices[0].title || home.notices[0].content }}</p>
|
<span :class="['m-notice-tag', { 'is-activity': currentNotice.source_type === 'operational_simulation' }]">
|
||||||
</div>
|
{{ 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">
|
<section class="m-section m-card m-home-ranking">
|
||||||
<div class="m-section-title"
|
<div class="m-section-title"
|
||||||
@@ -101,7 +113,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
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 AsyncState from '../components/AsyncState.vue';
|
||||||
import AnnouncementPopup, {
|
import AnnouncementPopup, {
|
||||||
type AnnouncementNotice,
|
type AnnouncementNotice,
|
||||||
@@ -121,7 +133,7 @@
|
|||||||
wallet: { available_amount: number; frozen_amount: number; currency: string };
|
wallet: { available_amount: number; frozen_amount: number; currency: string };
|
||||||
features: Record<string, boolean>;
|
features: Record<string, boolean>;
|
||||||
banners: BannerData[];
|
banners: BannerData[];
|
||||||
notices: Array<{ title?: string; content?: string }>;
|
notices: NoticeData[];
|
||||||
task_summary: {
|
task_summary: {
|
||||||
today_count: number;
|
today_count: number;
|
||||||
today_commission_amount: number;
|
today_commission_amount: number;
|
||||||
@@ -135,6 +147,12 @@
|
|||||||
target_type: 'none' | 'internal_route' | 'external_url';
|
target_type: 'none' | 'internal_route' | 'external_url';
|
||||||
target_value?: string;
|
target_value?: string;
|
||||||
}
|
}
|
||||||
|
interface NoticeData {
|
||||||
|
id: number;
|
||||||
|
title?: string;
|
||||||
|
content?: string;
|
||||||
|
source_type?: string;
|
||||||
|
}
|
||||||
interface LeaderboardEntry {
|
interface LeaderboardEntry {
|
||||||
rank: number;
|
rank: number;
|
||||||
username: string;
|
username: string;
|
||||||
@@ -149,12 +167,15 @@
|
|||||||
const leaderboard = ref<LeaderboardEntry[]>([]);
|
const leaderboard = ref<LeaderboardEntry[]>([]);
|
||||||
const popupNotice = ref<AnnouncementNotice | null>(null);
|
const popupNotice = ref<AnnouncementNotice | null>(null);
|
||||||
const activeBannerIndex = ref(0);
|
const activeBannerIndex = ref(0);
|
||||||
|
const activeNoticeIndex = ref(0);
|
||||||
let bannerTimer: number | undefined;
|
let bannerTimer: number | undefined;
|
||||||
|
let noticeTimer: number | undefined;
|
||||||
const formatPoints = (value: number) =>
|
const formatPoints = (value: number) =>
|
||||||
new Intl.NumberFormat('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(
|
new Intl.NumberFormat('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(
|
||||||
(value || 0) / 100
|
(value || 0) / 100
|
||||||
);
|
);
|
||||||
const currentBanner = computed(() => home.value?.banners?.[activeBannerIndex.value]);
|
const currentBanner = computed(() => home.value?.banners?.[activeBannerIndex.value]);
|
||||||
|
const currentNotice = computed(() => home.value?.notices?.[activeNoticeIndex.value]);
|
||||||
const bannerStyle = computed(() => {
|
const bannerStyle = computed(() => {
|
||||||
const url = safeImage(currentBanner.value?.image_url);
|
const url = safeImage(currentBanner.value?.image_url);
|
||||||
return {
|
return {
|
||||||
@@ -234,6 +255,18 @@
|
|||||||
}, 4500);
|
}, 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() {
|
async function load() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
error.value = '';
|
error.value = '';
|
||||||
@@ -269,8 +302,13 @@
|
|||||||
activeBannerIndex.value = 0;
|
activeBannerIndex.value = 0;
|
||||||
restartBannerTimer();
|
restartBannerTimer();
|
||||||
});
|
});
|
||||||
|
watch(() => home.value?.notices?.length, () => {
|
||||||
|
activeNoticeIndex.value = 0;
|
||||||
|
restartNoticeTimer();
|
||||||
|
});
|
||||||
onMounted(load);
|
onMounted(load);
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
if (bannerTimer) window.clearInterval(bannerTimer);
|
if (bannerTimer) window.clearInterval(bannerTimer);
|
||||||
|
if (noticeTimer) window.clearInterval(noticeTimer);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user