Files
live/frontend/src/admin/AdminLayout.tsx
T
admin 2685ebdd47 UI彻底重做: 全新设计语言, 纯手工精调
设计系统:
- CSS变量体系: 暖纸底色(#F9F7F4), 6级灰阶, 5级阴影
- 字体节奏: clamp响应式字号, -0.02em字间距
- 间距韵律: 4-128px 8级递进

首页:
- 96px大留白英雄区, clamp(36px,5vw,56px)巨型标题
- 200px暗色封面卡片(#2C2824)
- LIVE红点脉动标签, 待开播黑底白字徽章
- hover抬升2px + 阴影, 淡棕描边高亮

管理页:
- stat卡片: 1px边框+3px彩色顶线
- 表格卡片化: borderRadius 12, 统一白底+边框
- 表单/设置: 统一圆角8px, 大内边距36px

播放页:
- #1A1814暗底, 毛玻璃顶栏
- 视频圆角12px + boxShadow景深
- document.title动态标题

登录页: 56px内边距, 品牌居中, 返回链接
2026-08-10 17:44:24 +08:00

76 lines
2.8 KiB
TypeScript

import { useState } from 'react'
import { Outlet, useNavigate, useLocation } from 'react-router-dom'
import { Layout, Menu, Button, Typography } from 'antd'
import {
DashboardOutlined, VideoCameraOutlined, PlusOutlined,
CloudServerOutlined, SettingOutlined, LogoutOutlined,
} from '@ant-design/icons'
import { clearToken } from '../api/client'
import { useBrand } from '../brand/BrandContext'
const { Sider, Content, Header } = Layout
const { Text } = Typography
const menuItems = [
{ key: '/admin', icon: <DashboardOutlined />, label: '概览' },
{ key: '/admin/streams', icon: <VideoCameraOutlined />, label: '直播列表' },
{ key: '/admin/streams/new', icon: <PlusOutlined />, label: '创建直播' },
{ key: '/admin/nodes', icon: <CloudServerOutlined />, label: '边缘节点' },
{ key: '/admin/settings', icon: <SettingOutlined />, label: '系统设置' },
]
export default function AdminLayout() {
const { brand } = useBrand()
const [collapsed, setCollapsed] = useState(false)
const navigate = useNavigate()
const location = useLocation()
const selectedKey = menuItems.find(
item => location.pathname === item.key || (location.pathname === '/' && item.key === '/admin')
)?.key || '/admin'
const handleLogout = () => { clearToken(); navigate('/admin/login') }
return (
<Layout style={{ minHeight: '100vh' }}>
<Sider
collapsible collapsed={collapsed} onCollapse={setCollapsed}
width={220}
style={{
borderRight: '1px solid #EBE5DB',
background: '#fff',
}}
>
<div style={{
height: 60, display: 'flex', alignItems: 'center', justifyContent: 'center',
borderBottom: '1px solid #EBE5DB',
}}>
<Text strong style={{
fontSize: collapsed ? 14 : 17, letterSpacing: '-0.02em',
fontFamily: '"Source Serif 4", "Noto Serif SC", Georgia, serif',
color: '#C2693A',
}}>
{collapsed ? brand.brand_name.slice(0, 2) : brand.brand_name}
</Text>
</div>
<Menu mode="inline" selectedKeys={[selectedKey]} items={menuItems}
onClick={({ key }) => navigate(key)}
style={{ border: 'none', marginTop: 8 }} />
</Sider>
<Layout>
<Header style={{
background: '#fff', borderBottom: '1px solid #EBE5DB',
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
padding: '0 48px',
}}>
<Text style={{ color: '#949088', fontSize: 13 }}>{brand.brand_tagline}</Text>
<Button type="text" icon={<LogoutOutlined />} onClick={handleLogout}>退出登录</Button>
</Header>
<Content style={{ padding: 40, overflow: 'auto' }}>
<Outlet />
</Content>
</Layout>
</Layout>
)
}