feat: 更新首页,添加公告和新闻列表,优化样式和交互效果

This commit is contained in:
2026-01-16 17:32:58 +07:00
parent f045810dd4
commit c0e7102d9b
4 changed files with 418 additions and 199 deletions

View File

@@ -1,5 +1,99 @@
<script lang='ts' setup>
import {
calendarOutline,
chatbubblesOutline,
chevronForwardOutline,
eyeOutline,
megaphoneOutline,
newspaperOutline,
peopleOutline,
rocketOutline,
timeOutline,
} from "ionicons/icons";
import { onMounted, onUnmounted, ref } from "vue";
// 公告数据
const announcements = ref([
{ id: 1, title: "关于深化改革的重要通知", time: "2026-01-16" },
{ id: 2, title: "平台升级维护公告", time: "2026-01-15" },
{ id: 3, title: "新年贺词:砥砺前行,共创辉煌", time: "2026-01-01" },
]);
// 新闻数据(模拟数据)
const newsList = ref([
{
id: 1,
title: "深化改革进入新阶段",
subtitle: "全面推进现代化建设,开创新局面",
time: "2026-01-16 10:30",
views: 1520,
image: "https://picsum.photos/seed/news1/400/250",
},
{
id: 2,
title: "团队协作再创佳绩",
subtitle: "凝心聚力,共筑梦想,携手共进新时代",
time: "2026-01-15 16:20",
views: 2340,
image: "https://picsum.photos/seed/news2/400/250",
},
{
id: 3,
title: "战略布局取得重大突破",
subtitle: "科学谋划,精准施策,推动高质量发展",
time: "2026-01-14 09:15",
views: 1890,
image: "https://picsum.photos/seed/news3/400/250",
},
]);
// 快捷入口
const quickActions = ref([
{ id: 1, name: "签到", icon: calendarOutline, color: "#c41e3a" },
{ id: 2, name: "团队中心", icon: peopleOutline, color: "#c41e3a" },
{ id: 3, name: "战略改革", icon: rocketOutline, color: "#c41e3a" },
{ id: 4, name: "在线客服", icon: chatbubblesOutline, color: "#c41e3a" },
]);
function handleQuickAction(action: any) {
console.log("点击快捷入口:", action.name);
// TODO: 实现各个快捷入口的功能
}
function handleAnnouncementClick(announcement: any) {
console.log("查看公告:", announcement.title);
// TODO: 跳转到公告详情
}
function handleNewsClick(news: any) {
console.log("查看新闻:", news.title);
// TODO: 跳转到新闻详情
}
// 走马灯相关
const currentAnnouncementIndex = ref(0);
let announcementTimer: number | null = null;
function startAnnouncementCarousel() {
announcementTimer = setInterval(() => {
currentAnnouncementIndex.value = (currentAnnouncementIndex.value + 1) % announcements.value.length;
}, 3000); // 每3秒切换
}
function stopAnnouncementCarousel() {
if (announcementTimer) {
clearInterval(announcementTimer);
announcementTimer = null;
}
}
onMounted(() => {
startAnnouncementCarousel();
});
onUnmounted(() => {
stopAnnouncementCarousel();
});
</script>
<template>
@@ -12,8 +106,282 @@
<ion-title>{{ $t('home.title') }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true" class="ion-padding" />
<ion-content :fullscreen="true" class="home-page">
<!-- 背景装饰 -->
<div class="bg-decoration">
<!-- 中国风图案 -->
<div class="chinese-pattern">
<svg class="pattern-svg pattern-1" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="40" fill="none" stroke="rgba(255,255,255,0.15)" stroke-width="2" />
<circle cx="50" cy="50" r="30" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="1.5" />
<path d="M50 10 L50 30 M50 70 L50 90 M10 50 L30 50 M70 50 L90 50" stroke="rgba(255,255,255,0.15)" stroke-width="2" />
</svg>
<svg class="pattern-svg pattern-2" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<path d="M30 30 Q50 45 70 30 Q55 50 70 70 Q50 55 30 70 Q45 50 30 30" fill="none" stroke="rgba(255,255,255,0.12)" stroke-width="2" />
</svg>
</div>
<!-- 五角星装饰 -->
<div class="stars-decoration">
<div class="star star-1">
</div>
<div class="star star-2">
</div>
<div class="star star-3">
</div>
</div>
<!-- 底部波浪 -->
<svg class="wave-bottom" viewBox="0 0 1200 120" xmlns="http://www.w3.org/2000/svg">
<path d="M0 80 Q300 40 600 80 T1200 80 L1200 120 L0 120 Z" fill="rgba(255,255,255,0.08)" />
<path d="M0 95 Q300 60 600 95 T1200 95 L1200 120 L0 120 Z" fill="rgba(255,255,255,0.05)" />
</svg>
</div>
<div class="relative z-1 p-4">
<!-- 快捷入口区域 -->
<section class="mb-5">
<div class="grid grid-cols-4 gap-4 bg-white/95 p-5 rounded-2xl shadow-lg">
<div
v-for="action in quickActions"
:key="action.id"
class="flex flex-col items-center gap-2 cursor-pointer transition-transform active:scale-95"
@click="handleQuickAction(action)"
>
<div
class="w-13 h-13 rounded-xl flex-center shadow-lg"
:style="{ background: `linear-gradient(135deg, ${action.color}, ${action.color}dd)` }"
>
<ion-icon :icon="action.icon" class="text-3xl text-white" />
</div>
<span class="text-xs text-[#333] font-medium text-center">{{ action.name }}</span>
</div>
</div>
</section>
<!-- 公告通知区域 - 走马灯 -->
<section class="mb-5">
<div
class="bg-white rounded-2xl shadow-sm overflow-hidden cursor-pointer relative"
@click="handleAnnouncementClick(announcements[currentAnnouncementIndex])"
>
<div class="announcement-carousel relative h-20">
<transition-group name="slide">
<div
v-for="(announcement, index) in announcements"
v-show="index === currentAnnouncementIndex"
:key="announcement.id"
class="flex items-center gap-3 p-4 absolute inset-0"
>
<div class="text-2xl shrink-0">
📢
</div>
<div class="flex-1 min-w-0">
<div class="text-sm text-[#333] font-medium mb-1 truncate">
{{ announcement.title }}
</div>
<div class="text-xs text-[#999]">
{{ announcement.time }}
</div>
</div>
<ion-icon :icon="chevronForwardOutline" class="text-lg text-[#ccc] shrink-0" />
</div>
</transition-group>
</div>
<!-- 指示器 -->
<div class="flex justify-center gap-1.5 pb-2">
<div
v-for="(announcement, index) in announcements"
:key="announcement.id"
class="w-1.5 h-1.5 rounded-full transition-all duration-300"
:class="index === currentAnnouncementIndex ? 'bg-[#c41e3a] w-4' : 'bg-[#ddd]'"
/>
</div>
</div>
</section>
<!-- 新闻列表区域 -->
<section class="mb-5">
<div class="flex justify-between items-center mb-4">
<div class="flex items-center gap-2">
<ion-icon :icon="newspaperOutline" class="text-2xl text-[#c41e3a]" />
<h3 class="text-lg font-bold text-[#1a1a1a] m-0">
新闻动态
</h3>
</div>
<ion-button fill="clear" size="small" class="text-sm text-white h-8">
更多
<ion-icon slot="end" :icon="chevronForwardOutline" />
</ion-button>
</div>
<div class="flex flex-col gap-4">
<div
v-for="news in newsList"
:key="news.id"
class="bg-white rounded-2xl overflow-hidden shadow-sm cursor-pointer transition-all active:translate-y-0.5 active:shadow-sm"
@click="handleNewsClick(news)"
>
<div class="relative w-full h-45 overflow-hidden">
<img :src="news.image" :alt="news.title" class="w-full h-full object-cover">
<div class="news-badge absolute top-3 left-3 bg-gradient-to-br from-[#c41e3a] to-[#8b1a2e] text-white px-3 py-1 rounded-xl text-xs font-semibold shadow-lg">
热点
</div>
</div>
<div class="p-4">
<h4 class="text-base font-bold text-[#1a1a1a] mb-2 leading-snug">
{{ news.title }}
</h4>
<p class="text-sm text-[#666] mb-3 leading-relaxed line-clamp-2">
{{ news.subtitle }}
</p>
<div class="flex items-center gap-4 text-xs text-[#999]">
<span class="flex items-center gap-1">
<ion-icon :icon="timeOutline" class="text-sm" />
{{ news.time }}
</span>
<span class="flex items-center gap-1">
<ion-icon :icon="eyeOutline" class="text-sm" />
{{ news.views }}
</span>
</div>
</div>
</div>
</div>
</section>
</div>
</ion-content>
</ion-page>
</template>
<style lang='css' scoped></style>
<style lang='css' scoped>
.home-page {
--background: linear-gradient(180deg, #c41e3a 0%, #e8756d 15%, #f5d5c8 35%, #fef5f1 50%, #fef5f1 65%, #f5d5c8 85%);
}
/* 中国风图案 */
.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 {
box-shadow: 0 2px 8px rgba(196, 30, 58, 0.3);
}
/* 走马灯动画 */
.announcement-carousel {
transition: height 0.3s ease;
}
.slide-enter-active,
.slide-leave-active {
transition: all 0.5s ease;
}
.slide-enter-from {
transform: translateX(100%);
opacity: 0;
}
.slide-leave-to {
transform: translateX(-100%);
opacity: 0;
}
.slide-enter-to,
.slide-leave-from {
transform: translateX(0);
opacity: 1;
}
</style>