| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- import React, { useState, useEffect } from 'react';
- import { useNavigate, useLocation } from 'react-router';
- import {
- HomeIcon,
- UserIcon,
- NewspaperIcon,
- BellIcon
- } from '@heroicons/react/24/outline';
- import { useAuth } from '../hooks.tsx';
- import { formatRelativeTime } from '../utils.ts';
- interface BannerItem {
- id: number;
- title: string;
- image: string;
- link: string;
- }
- interface NewsItem {
- id: number;
- title: string;
- summary: string;
- publish_date: string;
- cover?: string;
- category: string;
- }
- interface NoticeItem {
- id: number;
- title: string;
- content: string;
- created_at: string;
- is_read: boolean;
- }
- // 首页组件
- const HomePage: React.FC = () => {
- const { user } = useAuth();
- const navigate = useNavigate();
- const location = useLocation();
- const [loading, setLoading] = useState(true);
- const [banners, setBanners] = useState<BannerItem[]>([]);
- const [news, setNews] = useState<NewsItem[]>([]);
- const [notices, setNotices] = useState<NoticeItem[]>([]);
- const [activeTab, setActiveTab] = useState('news');
-
- // 模拟加载数据
- useEffect(() => {
- // 模拟API请求
- setTimeout(() => {
- // 模拟轮播图数据
- setBanners([
- {
- id: 1,
- title: '欢迎使用移动端应用',
- image: 'https://images.unsplash.com/photo-1518655048521-f130df041f66?ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8cG9ydGZvbGlvJTIwYmFja2dyb3VuZHxlbnwwfHwwfHw%3D&ixlib=rb-1.2.1&w=1000&q=80',
- link: '/welcome'
- },
- {
- id: 2,
- title: '新功能上线了',
- image: 'https://images.unsplash.com/photo-1516321318423-f06f85e504b3?ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8cG9ydGZvbGlvJTIwYmFja2dyb3VuZHxlbnwwfHwwfHw%3D&ixlib=rb-1.2.1&w=1000&q=80',
- link: '/new-features'
- }
- ]);
-
- // 模拟新闻数据
- setNews([
- {
- id: 1,
- title: '用户体验升级,新版本发布',
- summary: '我们很高兴地宣布,新版本已经发布,带来了更好的用户体验和更多新功能。',
- publish_date: '2023-05-01T08:30:00',
- cover: 'https://images.unsplash.com/photo-1496171367470-9ed9a91ea931?ixid=MnwxMjA3fDB8MHxzZWFyY2h8MTB8fHRlY2h8ZW58MHx8MHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60',
- category: '产品更新'
- },
- {
- id: 2,
- title: '新的数据分析功能上线',
- summary: '新的数据分析功能让您更深入地了解您的业务数据,提供更好的决策支持。',
- publish_date: '2023-04-25T14:15:00',
- cover: 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixid=MnwxMjA3fDB8MHxzZWFyY2h8MTJ8fGNoYXJ0fGVufDB8fDB8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60',
- category: '功能介绍'
- },
- {
- id: 3,
- title: '如何提高工作效率的5个小技巧',
- summary: '这篇文章分享了5个可以立即实施的小技巧,帮助您提高日常工作效率。',
- publish_date: '2023-04-20T09:45:00',
- category: '使用技巧'
- }
- ]);
-
- // 模拟通知数据
- setNotices([
- {
- id: 1,
- title: '系统维护通知',
- content: '我们将于本周六凌晨2点至4点进行系统维护,期间系统可能会出现短暂不可用。',
- created_at: '2023-05-02T10:00:00',
- is_read: false
- },
- {
- id: 2,
- title: '您的账户信息已更新',
- content: '您的账户信息已成功更新,如非本人操作,请及时联系客服。',
- created_at: '2023-05-01T16:30:00',
- is_read: true
- }
- ]);
-
- setLoading(false);
- }, 800);
- }, []);
-
- // 处理轮播图点击
- const handleBannerClick = (link: string) => {
- navigate(link);
- };
-
- // 处理新闻点击
- const handleNewsClick = (id: number) => {
- navigate(`/news/${id}`);
- };
-
- // 处理通知点击
- const handleNoticeClick = (id: number) => {
- navigate(`/notices/${id}`);
- };
-
- return (
- <div className="pb-16">
- {/* 顶部用户信息 */}
- <div className="bg-blue-600 text-white p-4">
- <div className="flex items-center justify-between">
- <div className="flex items-center space-x-3">
- <div className="w-12 h-12 rounded-full bg-white/20 flex items-center justify-center">
- {user?.avatar ? (
- <img
- src={user.avatar}
- alt={user?.nickname || user?.username || '用户'}
- className="w-10 h-10 rounded-full object-cover"
- />
- ) : (
- <UserIcon className="w-6 h-6" />
- )}
- </div>
- <div>
- <h2 className="text-lg font-medium">
- {user ? `您好,${user.nickname || user.username}` : '您好,游客'}
- </h2>
- <p className="text-sm text-white/80">
- {user ? '欢迎回来' : '请登录体验更多功能'}
- </p>
- </div>
- </div>
-
- <div className="relative">
- <BellIcon className="w-6 h-6" />
- {notices.some(notice => !notice.is_read) && (
- <span className="absolute top-0 right-0 w-2 h-2 bg-red-500 rounded-full"></span>
- )}
- </div>
- </div>
- </div>
-
- {/* 轮播图 */}
- {!loading && banners.length > 0 && (
- <div className="relative w-full h-40 overflow-hidden mt-2">
- <div className="flex transition-transform duration-300"
- style={{ transform: `translateX(-${0 * 100}%)` }}>
- {banners.map((banner) => (
- <div
- key={banner.id}
- className="w-full h-40 flex-shrink-0 relative"
- onClick={() => handleBannerClick(banner.link)}
- >
- <img
- src={banner.image}
- alt={banner.title}
- className="w-full h-full object-cover"
- />
- <div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-3">
- <h3 className="text-white font-medium">{banner.title}</h3>
- </div>
- </div>
- ))}
- </div>
-
- {/* 指示器 */}
- <div className="absolute bottom-2 left-0 right-0 flex justify-center space-x-1">
- {banners.map((_, index) => (
- <span
- key={index}
- className={`w-2 h-2 rounded-full ${index === 0 ? 'bg-white' : 'bg-white/50'}`}
- ></span>
- ))}
- </div>
- </div>
- )}
-
- {/* 快捷入口 */}
- <div className="grid grid-cols-4 gap-2 p-4 bg-white rounded-lg shadow mt-4 mx-2">
- {[
- { icon: <HomeIcon className="w-6 h-6" />, name: '首页', path: '/' },
- { icon: <NewspaperIcon className="w-6 h-6" />, name: '资讯', path: '/news' },
- { icon: <BellIcon className="w-6 h-6" />, name: '通知', path: '/notices' },
- { icon: <UserIcon className="w-6 h-6" />, name: '我的', path: '/profile' }
- ].map((item, index) => (
- <div
- key={index}
- className="flex flex-col items-center justify-center p-2"
- onClick={() => navigate(item.path)}
- >
- <div className="w-12 h-12 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 mb-1">
- {item.icon}
- </div>
- <span className="text-sm">{item.name}</span>
- </div>
- ))}
- </div>
-
- {/* 内容标签页 */}
- <div className="mt-4 mx-2">
- <div className="flex border-b border-gray-200">
- <button
- className={`flex-1 py-2 text-center ${activeTab === 'news' ? 'text-blue-600 border-b-2 border-blue-600 font-medium' : 'text-gray-500'}`}
- onClick={() => setActiveTab('news')}
- >
- 最新资讯
- </button>
- <button
- className={`flex-1 py-2 text-center ${activeTab === 'notices' ? 'text-blue-600 border-b-2 border-blue-600 font-medium' : 'text-gray-500'}`}
- onClick={() => setActiveTab('notices')}
- >
- 通知公告
- </button>
- </div>
-
- <div className="mt-2">
- {activeTab === 'news' ? (
- loading ? (
- <div className="flex justify-center p-4">
- <div className="w-6 h-6 border-2 border-gray-200 border-t-blue-600 rounded-full animate-spin"></div>
- </div>
- ) : (
- <div className="space-y-4">
- {news.map((item) => (
- <div
- key={item.id}
- className="bg-white p-3 rounded-lg shadow flex items-start space-x-3"
- onClick={() => handleNewsClick(item.id)}
- >
- {item.cover && (
- <img
- src={item.cover}
- alt={item.title}
- className="w-20 h-20 object-cover rounded-md flex-shrink-0"
- />
- )}
- <div className={item.cover ? '' : 'w-full'}>
- <h3 className="font-medium text-gray-900 line-clamp-2">{item.title}</h3>
- <p className="text-sm text-gray-500 mt-1 line-clamp-2">{item.summary}</p>
- <div className="flex justify-between items-center mt-2">
- <span className="text-xs text-blue-600 bg-blue-50 px-2 py-1 rounded-full">
- {item.category}
- </span>
- <span className="text-xs text-gray-400">
- {formatRelativeTime(item.publish_date)}
- </span>
- </div>
- </div>
- </div>
- ))}
- </div>
- )
- ) : (
- loading ? (
- <div className="flex justify-center p-4">
- <div className="w-6 h-6 border-2 border-gray-200 border-t-blue-600 rounded-full animate-spin"></div>
- </div>
- ) : (
- <div className="space-y-3">
- {notices.map((item) => (
- <div
- key={item.id}
- className="bg-white p-3 rounded-lg shadow"
- onClick={() => handleNoticeClick(item.id)}
- >
- <div className="flex justify-between items-start">
- <h3 className={`font-medium ${item.is_read ? 'text-gray-700' : 'text-blue-600'}`}>
- {!item.is_read && (
- <span className="inline-block w-2 h-2 bg-blue-600 rounded-full mr-2"></span>
- )}
- {item.title}
- </h3>
- <span className="text-xs text-gray-400 mt-1">
- {formatRelativeTime(item.created_at)}
- </span>
- </div>
- <p className="text-sm text-gray-500 mt-2 line-clamp-2">{item.content}</p>
- </div>
- ))}
- </div>
- )
- )}
- </div>
- </div>
-
- {/* 底部导航 */}
- <div className="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 flex justify-around py-2">
- {[
- { icon: <HomeIcon className="w-6 h-6" />, name: '首页', path: '/' },
- { icon: <NewspaperIcon className="w-6 h-6" />, name: '资讯', path: '/news' },
- { icon: <BellIcon className="w-6 h-6" />, name: '通知', path: '/notices' },
- { icon: <UserIcon className="w-6 h-6" />, name: '我的', path: '/profile' }
- ].map((item, index) => (
- <div
- key={index}
- className="flex flex-col items-center"
- onClick={() => navigate(item.path)}
- >
- <div className={`${location.pathname === item.path ? 'text-blue-600' : 'text-gray-500'}`}>
- {item.icon}
- </div>
- <span className={`text-xs mt-1 ${location.pathname === item.path ? 'text-blue-600' : 'text-gray-500'}`}>
- {item.name}
- </span>
- </div>
- ))}
- </div>
- </div>
- );
- };
- export default HomePage;
|