diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index eba56f6..09ca973 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,21 +1,22 @@ -import { useState, useEffect } from 'react' +import { lazy, Suspense, useState, useEffect } from 'react' import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom' import { ConfigProvider } from 'antd' import zhCN from 'antd/locale/zh_CN' import theme from './theme/anthropic' import { BrandProvider, useBrand } from './brand/BrandContext' -import AdminLayout from './admin/AdminLayout' -import LoginPage from './admin/LoginPage' -import Dashboard from './admin/pages/Dashboard' -import StreamList from './admin/pages/StreamList' -import StreamForm from './admin/pages/StreamForm' -import NodeManager from './admin/pages/NodeManager' -import SettingsPage from './admin/pages/Settings' -import HomePage from './viewer/pages/HomePage' -import WatchPage from './viewer/pages/WatchPage' import { verifyToken } from './api/auth' import { getToken } from './api/client' +const HomePage = lazy(() => import('./viewer/pages/HomePage')) +const WatchPage = lazy(() => import('./viewer/pages/WatchPage')) +const AdminLayout = lazy(() => import('./admin/AdminLayout')) +const LoginPage = lazy(() => import('./admin/LoginPage')) +const Dashboard = lazy(() => import('./admin/pages/Dashboard')) +const StreamList = lazy(() => import('./admin/pages/StreamList')) +const StreamForm = lazy(() => import('./admin/pages/StreamForm')) +const NodeManager = lazy(() => import('./admin/pages/NodeManager')) +const SettingsPage = lazy(() => import('./admin/pages/Settings')) + function AdminGuard({ children }: { children: React.ReactNode }) { const [authed, setAuthed] = useState(null) @@ -35,7 +36,8 @@ function AppRoutes() { useEffect(() => { document.title = brand.brand_name }, [brand.brand_name]) return ( - + }> + } /> } /> } /> } /> - + + ) } diff --git a/frontend/src/viewer/pages/HomePage.tsx b/frontend/src/viewer/pages/HomePage.tsx index d19f81b..db73549 100644 --- a/frontend/src/viewer/pages/HomePage.tsx +++ b/frontend/src/viewer/pages/HomePage.tsx @@ -1,11 +1,10 @@ import { useState, useEffect } from 'react' import { useNavigate } from 'react-router-dom' -import { Row, Col, Tag, Skeleton } from 'antd' +import { Skeleton } from 'antd' import { VideoCameraOutlined } from '@ant-design/icons' import { fetchStreams, type StreamInfo } from '../../api/streams' import { useBrand } from '../../brand/BrandContext' - -const liveDot = { width: 8, height: 8, borderRadius: '50%', background: '#E53935', display: 'inline-block', marginRight: 6, boxShadow: '0 0 0 2px rgba(229,57,53,0.2)' } +import './viewer.css' export default function HomePage() { const { brand } = useBrand() @@ -14,7 +13,12 @@ export default function HomePage() { const navigate = useNavigate() useEffect(() => { - fetchStreams().then(setStreams).catch(() => {}).finally(() => setLoading(false)) + let active = true + fetchStreams() + .then(data => { if (active) setStreams(data) }) + .catch(() => {}) + .finally(() => { if (active) setLoading(false) }) + return () => { active = false } }, []) const sorted = [...streams].sort((a, b) => { @@ -23,68 +27,44 @@ export default function HomePage() { }) return ( -
- {/* Hero */} -
+
+
+
LIVE PROGRAMME
{brand.brand_logo_url && ( {brand.brand_name} + className="viewer-logo" /> )} -

+

{brand.brand_name}

{brand.brand_tagline && ( -

+

{brand.brand_tagline}

)} -
+ - {/* Streams */} -
-
-

+
+
+

直播频道

- + {sorted.length} 个节目
{loading ? ( - +
{[1,2,3,4,5,6].map(i => ( - -
-
-
+
+
+
-
- +
))} - +
) : sorted.length === 0 ? (
当前没有正在播出或排期的直播节目

) : ( - +
{sorted.map(s => { const isLive = s.status === 'live' const isPending = s.status === 'pending' return ( - -
+ + ) })} - +
)} -

+ - {/* Footer */} -