全局整改: 布局重构、鉴权加固、API修复
- 首页/播放页: 去掉 antd Layout 改用 div 避免 flex 布局问题
- 播放页: 返回按钮改为 navigate('/') 替代 window.close()
- 后端登录/验证: 改用 POST JSON body 替代 query param
- 后端鉴权: require_auth 依赖注入保护所有管理接口
- nodes路由: 修复 /strategy 被 /{node_id} 拦截的顺序问题
- 前端 Settings: 策略更新改为 JSON body
This commit is contained in:
1 parent
f3b2a9cb46
commit
b81a7bf293
7 files changed
+110
-64
No files matched your search
@@ -1,12 +1,11 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import {
|
||||
Layout, Row, Col, Card, Typography, Tag, Button, Skeleton, Empty, Space,
|
||||
Row, Col, Card, Typography, Tag, Button, Skeleton, Empty, Space,
|
||||
} from 'antd'
|
||||
import { LoginOutlined, FireOutlined, ClockCircleOutlined } from '@ant-design/icons'
|
||||
import { fetchStreams, type StreamInfo } from '../../api/streams'
|
||||
|
||||
const { Header, Content } = Layout
|
||||
const { Title, Text } = Typography
|
||||
|
||||
const statusMeta: Record<string, { color: string; label: string; icon: React.ReactNode }> = {
|
||||
@@ -34,8 +33,8 @@ export default function HomePage() {
|
||||
})
|
||||
|
||||
return (
|
||||
<Layout style={{ minHeight: '100vh', background: '#FAF8F5' }}>
|
||||
<Header style={{
|
||||
<div style={{ minHeight: '100vh', background: '#FAF8F5' }}>
|
||||
<div style={{
|
||||
background: '#FFFFFF',
|
||||
borderBottom: '1px solid #E8E4DD',
|
||||
display: 'flex',
|
||||
@@ -57,13 +56,13 @@ export default function HomePage() {
|
||||
>
|
||||
管理后台
|
||||
</Button>
|
||||
</Header>
|
||||
<Content style={{ padding: '32px 48px', maxWidth: 1280, margin: '0 auto' }}>
|
||||
</div>
|
||||
<div style={{ maxWidth: 1280, margin: '0 auto', padding: '32px 48px' }}>
|
||||
<Title level={3} style={{ marginBottom: 24 }}>全部直播</Title>
|
||||
|
||||
{loading ? (
|
||||
<Row gutter={[24, 24]}>
|
||||
{[1,2,3,4,5,6].map((i) => (
|
||||
{[1, 2, 3, 4, 5, 6].map((i) => (
|
||||
<Col key={i} xs={24} sm={12} md={8} lg={8}>
|
||||
<Card><Skeleton active /></Card>
|
||||
</Col>
|
||||
@@ -120,7 +119,7 @@ export default function HomePage() {
|
||||
})}
|
||||
</Row>
|
||||
)}
|
||||
</Content>
|
||||
</Layout>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { useParams, useNavigate } from 'react-router-dom'
|
||||
import {
|
||||
Layout, Card, Select, Button, Typography, Space, Tag, Spin, Skeleton, Empty, message,
|
||||
Select, Button, Typography, Space, Tag, Spin, Skeleton, Empty, message,
|
||||
} from 'antd'
|
||||
import { ArrowLeftOutlined, WifiOutlined, SignalFilled } from '@ant-design/icons'
|
||||
import Hls from 'hls.js'
|
||||
import { getWatchUrls, type WatchInfo, type LineInfo } from '../../api/streams'
|
||||
|
||||
const { Header, Content } = Layout
|
||||
const { Title, Text } = Typography
|
||||
|
||||
export default function WatchPage() {
|
||||
const navigate = useNavigate()
|
||||
const { id } = useParams<{ id: string }>()
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
const [info, setInfo] = useState<WatchInfo | null>(null)
|
||||
@@ -41,7 +41,7 @@ export default function WatchPage() {
|
||||
setActiveQuality(data.qualities[0].label)
|
||||
}
|
||||
}
|
||||
} catch { /* 直播尚未就绪 */ }
|
||||
} catch { }
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
@@ -98,7 +98,13 @@ export default function WatchPage() {
|
||||
|
||||
if (!info) {
|
||||
return (
|
||||
<div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#FAF8F5' }}>
|
||||
<div style={{
|
||||
minHeight: '100vh',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
background: '#FAF8F5',
|
||||
}}>
|
||||
<Empty description="未找到该直播" />
|
||||
</div>
|
||||
)
|
||||
@@ -107,19 +113,25 @@ export default function WatchPage() {
|
||||
const statusLabel = info.status === 'live' ? '直播中' : info.status === 'pending' ? '待开播' : info.status === 'ended' ? '已结束' : info.status
|
||||
|
||||
return (
|
||||
<Layout style={{ minHeight: '100vh', background: '#1A1A16' }}>
|
||||
<Header style={{
|
||||
<div style={{
|
||||
minHeight: '100vh',
|
||||
background: '#1A1A16',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}>
|
||||
<div style={{
|
||||
height: 56,
|
||||
minHeight: 56,
|
||||
background: 'rgba(26,26,22,0.95)',
|
||||
borderBottom: '1px solid rgba(255,255,255,0.08)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
padding: '0 24px',
|
||||
height: 56,
|
||||
}}>
|
||||
<Space>
|
||||
<Button type="text" icon={<ArrowLeftOutlined />} style={{ color: '#fff' }}
|
||||
onClick={() => window.close()}>
|
||||
onClick={() => navigate('/')}>
|
||||
返回
|
||||
</Button>
|
||||
<div>
|
||||
@@ -157,21 +169,28 @@ export default function WatchPage() {
|
||||
placeholder="选择线路"
|
||||
/>
|
||||
</Space>
|
||||
</Header>
|
||||
<Content style={{
|
||||
</div>
|
||||
<div style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: 24,
|
||||
maxHeight: 'calc(100vh - 56px)',
|
||||
}}>
|
||||
<div style={{ width: '100%', maxWidth: 1280, aspectRatio: '16/9', background: '#000', position: 'relative' }}>
|
||||
<div style={{
|
||||
width: '100%',
|
||||
maxWidth: 1280,
|
||||
aspectRatio: '16 / 9',
|
||||
maxHeight: 'calc(100vh - 56px - 48px)',
|
||||
background: '#000',
|
||||
position: 'relative',
|
||||
}}>
|
||||
<video
|
||||
ref={videoRef}
|
||||
controls
|
||||
autoPlay
|
||||
playsInline
|
||||
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
||||
style={{ width: '100%', height: '100%', objectFit: 'contain', display: 'block' }}
|
||||
/>
|
||||
{info.status !== 'live' && (
|
||||
<div style={{
|
||||
@@ -189,7 +208,7 @@ export default function WatchPage() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Content>
|
||||
</Layout>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user