diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 224a870..f434378 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,6 +1,7 @@ import { 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 AdminLayout from './admin/AdminLayout' import LoginPage from './admin/LoginPage' @@ -10,7 +11,8 @@ import StreamForm from './admin/pages/StreamForm' import NodeManager from './admin/pages/NodeManager' import SettingsPage from './admin/pages/Settings' import WatchPage from './viewer/pages/WatchPage' -import { verifyToken, getToken } from './api/auth' +import { verifyToken } from './api/auth' +import { getToken } from './api/client' function AdminGuard({ children }: { children: React.ReactNode }) { const [authed, setAuthed] = useState(null) @@ -28,7 +30,7 @@ function AdminGuard({ children }: { children: React.ReactNode }) { export default function App() { return ( - + } /> diff --git a/frontend/src/admin/AdminLayout.tsx b/frontend/src/admin/AdminLayout.tsx index f861d14..9d8f7b8 100644 --- a/frontend/src/admin/AdminLayout.tsx +++ b/frontend/src/admin/AdminLayout.tsx @@ -15,11 +15,11 @@ const { Sider, Content, Header } = Layout const { Text } = Typography const menuItems = [ - { key: '/admin', icon: , label: 'Dashboard' }, - { key: '/admin/streams', icon: , label: 'Streams' }, - { key: '/admin/streams/new', icon: , label: 'New Stream' }, - { key: '/admin/nodes', icon: , label: 'Edge Nodes' }, - { key: '/admin/settings', icon: , label: 'Settings' }, + { key: '/admin', icon: , label: '概览' }, + { key: '/admin/streams', icon: , label: '直播列表' }, + { key: '/admin/streams/new', icon: , label: '创建直播' }, + { key: '/admin/nodes', icon: , label: '边缘节点' }, + { key: '/admin/settings', icon: , label: '系统设置' }, ] export default function AdminLayout() { @@ -81,7 +81,7 @@ export default function AdminLayout() { icon={} onClick={handleLogout} > - Logout + 退出登录 diff --git a/frontend/src/admin/LoginPage.tsx b/frontend/src/admin/LoginPage.tsx index f0a6191..6613f08 100644 --- a/frontend/src/admin/LoginPage.tsx +++ b/frontend/src/admin/LoginPage.tsx @@ -14,10 +14,10 @@ export default function LoginPage() { setLoading(true) try { await login(values.password) - message.success('Welcome back') + message.success('登录成功') navigate('/admin') } catch { - message.error('Invalid password') + message.error('密码错误') } finally { setLoading(false) } @@ -35,22 +35,22 @@ export default function LoginPage() {
LiveStream - Admin Panel + 管理后台
} - placeholder="Password" + placeholder="密码" size="large" />
diff --git a/frontend/src/admin/pages/Dashboard.tsx b/frontend/src/admin/pages/Dashboard.tsx index 7c8c99c..e33a4f7 100644 --- a/frontend/src/admin/pages/Dashboard.tsx +++ b/frontend/src/admin/pages/Dashboard.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react' import { Row, Col, Card, Statistic, Typography, Table, Tag, Space } from 'antd' -import { VideoCameraOutlined, EyeOutlined, CloudServerOutlined, SignalFilled } from '@ant-design/icons' +import { VideoCameraOutlined, EyeOutlined, SignalFilled } from '@ant-design/icons' import { fetchStreams, type StreamInfo } from '../../api/streams' const { Title } = Typography @@ -25,35 +25,34 @@ export default function Dashboard() { const liveCount = streams.filter((s) => s.status === 'live').length const pendingCount = streams.filter((s) => s.status === 'pending').length - const endedCount = streams.filter((s) => s.status === 'ended').length const statusMap: Record = { - pending: { color: 'blue', label: 'Pending' }, - live: { color: 'green', label: 'Live' }, - ended: { color: 'default', label: 'Ended' }, - cancelled: { color: 'red', label: 'Cancelled' }, + pending: { color: 'blue', label: '待开播' }, + live: { color: 'green', label: '直播中' }, + ended: { color: 'default', label: '已结束' }, + cancelled: { color: 'red', label: '已取消' }, } const recentColumns = [ - { title: 'Title', dataIndex: 'title', key: 'title', ellipsis: true }, + { title: '标题', dataIndex: 'title', key: 'title', ellipsis: true }, { - title: 'Status', dataIndex: 'status', key: 'status', + title: '状态', dataIndex: 'status', key: 'status', render: (status: string) => { const s = statusMap[status] || { color: 'default', label: status } return {s.label} }, }, - { title: 'Created', dataIndex: 'created_at', key: 'created_at', render: (v: string) => v?.slice(0, 16) }, + { title: '创建时间', dataIndex: 'created_at', key: 'created_at', render: (v: string) => v?.slice(0, 16) }, ] return (
- Dashboard + 概览 } loading={loading} @@ -63,7 +62,7 @@ export default function Dashboard() { } loading={loading} @@ -73,7 +72,7 @@ export default function Dashboard() { } loading={loading} @@ -81,7 +80,7 @@ export default function Dashboard() { - + = { + 'round-robin': '轮询', + 'weighted': '权重', + 'least-conn': '最少连接', +} + export default function NodeManager() { const [nodes, setNodes] = useState([]) const [strategy, setStrategy] = useState(null) @@ -39,15 +45,15 @@ export default function NodeManager() { ]) setNodes(nodesRes.data) setStrategy(strategyRes.data) - } catch { message.error('Failed to load') } + } catch { message.error('加载失败') } setLoading(false) } async function updateWeight(nodeId: string, weight: number) { try { await api.put(`/nodes/${nodeId}`, { weight }) - message.success('Updated') - } catch { message.error('Failed') } + message.success('已更新') + } catch { message.error('更新失败') } } async function toggleNode(nodeId: string, enabled: boolean) { @@ -56,17 +62,17 @@ export default function NodeManager() { try { await api.put(`/nodes/${nodeId}`, { weight: node.weight, enabled }) setNodes((prev) => prev.map((n) => (n.id === nodeId ? { ...n, enabled } : n))) - message.success('Updated') - } catch { message.error('Failed') } + message.success('已更新') + } catch { message.error('更新失败') } } const columns = [ - { title: 'Name', dataIndex: 'name', key: 'name' }, - { title: 'Host', dataIndex: 'host', key: 'host' }, - { title: 'HLS Port', dataIndex: 'hls_port', key: 'hls_port', width: 100 }, - { title: 'API Port', dataIndex: 'api_port', key: 'api_port', width: 100 }, + { title: '名称', dataIndex: 'name', key: 'name' }, + { title: '主机', dataIndex: 'host', key: 'host' }, + { title: 'HLS 端口', dataIndex: 'hls_port', key: 'hls_port', width: 100 }, + { title: 'API 端口', dataIndex: 'api_port', key: 'api_port', width: 100 }, { - title: 'Weight', dataIndex: 'weight', key: 'weight', width: 140, + title: '权重', dataIndex: 'weight', key: 'weight', width: 140, render: (_: number, record: NodeInfo) => ( ( } color="success">Online, + title: '状态', key: 'health', width: 100, + render: () => } color="success">在线, }, ] return (
- Edge Nodes + 边缘节点管理 {strategy && (
- + - + )} } onClick={loadData}>Refresh} + title="节点列表" + extra={} >
- Settings + 系统设置 - - - + + + + - -