| | |
| | | <view class="footer-stack"> |
| | | <view class="notice-footer"> |
| | | <text class="notice-label">系统公告:</text> |
| | | <swiper class="notice-swiper" vertical="true" :autoplay="announcements.length > 1" :circular="true" :interval="4000" :duration="800" :display-multiple-items="1"> |
| | | <swiper-item v-for="(item, index) in announcements" :key="item.id || index"> |
| | | <view class="notice-item">{{ item.displayText }}</view> |
| | | </swiper-item> |
| | | <swiper-item v-if="announcements.length === 0"> |
| | | <view class="notice-item">暂无系统公告</view> |
| | | </swiper-item> |
| | | </swiper> |
| | | <view class="notice-scroll-wrapper"> |
| | | <view class="notice-scroller" :style="{ animationDuration: scrollDuration + 's' }" v-if="announcements.length > 0"> |
| | | <!-- Original list --> |
| | | <view class="notice-item-scroll" v-for="(item, index) in announcements" :key="'o-' + (item.id || index)">{{ item.displayText }}</view> |
| | | <!-- Duplicate list for seamless loop --> |
| | | <view class="notice-item-scroll" v-for="(item, index) in announcements" :key="'d-' + (item.id || index)">{{ item.displayText }}</view> |
| | | </view> |
| | | <view class="notice-empty" v-else>暂无系统公告</view> |
| | | </view> |
| | | <view class="notice-spacer"></view> |
| | | <view class="notice-version" v-if="versionText"> |
| | | <text class="version-text">V:{{ versionText }}</text> |
| | |
| | | } |
| | | }, |
| | | computed: { |
| | | |
| | | scrollDuration() { |
| | | // Base speed: 10 seconds + 2 seconds per item, adjust as needed |
| | | const base = 10; |
| | | return base + (this.announcements.length * 3); |
| | | } |
| | | }, |
| | | onLoad() { |
| | | this.startTimer(); |
| | |
| | | font-weight: bold; |
| | | color: #6ec6ff; |
| | | } |
| | | .notice-swiper { |
| | | flex: 1; |
| | | .notice-scroll-wrapper { |
| | | flex: 4; |
| | | min-width: 0; |
| | | height: 100%; |
| | | overflow: hidden; |
| | | position: relative; |
| | | display: flex; |
| | | align-items: center; |
| | | mask-image: linear-gradient(to right, transparent, black 20px, black calc(100% - 20px), transparent); |
| | | -webkit-mask-image: linear-gradient(to right, transparent, black 20px, black calc(100% - 20px), transparent); |
| | | } |
| | | .notice-item { |
| | | .notice-scroller { |
| | | display: flex; |
| | | align-items: center; |
| | | animation: notice-scroll linear infinite; |
| | | width: max-content; |
| | | } |
| | | .notice-scroller:hover { |
| | | animation-play-state: paused; |
| | | } |
| | | .notice-item-scroll { |
| | | font-size: 1.05rem; |
| | | color: #dbefff; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | display: flex; |
| | | align-items: center; |
| | | height: 100%; |
| | | margin-right: 4vw; |
| | | } |
| | | .notice-empty { |
| | | font-size: 1.05rem; |
| | | color: #dbefff; |
| | | white-space: nowrap; |
| | | } |
| | | @keyframes notice-scroll { |
| | | 0% { transform: translateX(0); } |
| | | 100% { transform: translateX(-50%); } |
| | | } |
| | | |
| | | .notice-spacer { |
| | | flex: 1; |
| | | } |