品牌化设置 + Claude米色衬线体主题 + 全站中文

- 品牌设置API: GET/PUT /api/settings/brand (名称/标语/Logo)
- 管理页系统设置: 品牌编辑器表单(名称/标语/Logo URL)
- 恢复Anthropic风格: 米色(#FBF9F6) 底色, Source Serif 4 + Noto Serif SC 衬线体
- 暖棕(#C2693A) 主色调, 更充裕的间距与留白, 庄重大气
- 全站中文: antd zhCN locale, 所有页面文本汉化
- 数据存储在 Config singleton, 运行时更新
This commit is contained in:
admin committed 2026-08-10 16:24:45 +08:00
1 parent 409e5d314e
commit efde5597bd
16 files changed
+403 -672

No files matched your search

+27 -47
View File
@@ -1,9 +1,9 @@
import { useState, useEffect } from 'react'
import { Row, Col, Card, Statistic, Typography, Table, Tag, Space } from 'antd'
import { Row, Col, Card, Statistic, Typography, Table, Tag } from 'antd'
import { VideoCameraOutlined, SignalFilled, PlayCircleOutlined } from '@ant-design/icons'
import { fetchStreams, type StreamInfo } from '../../api/streams'
const { Title, Text } = Typography
const { Title } = Typography
export default function Dashboard() {
const [streams, setStreams] = useState<StreamInfo[]>([])
@@ -27,73 +27,53 @@ export default function Dashboard() {
const pendingCount = streams.filter((s) => s.status === 'pending').length
const statusMap: Record<string, { color: string; label: string }> = {
pending: { color: 'blue', label: 'Scheduled' },
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 },
const columns = [
{ title: '标题', dataIndex: 'title', key: 'title', ellipsis: true },
{
title: 'Status', dataIndex: 'status', key: 'status',
render: (status: string) => {
const s = statusMap[status] || { color: 'default', label: status }
return <Tag color={s.color}>{s.label}</Tag>
title: '状态', dataIndex: 'status', key: 'status', width: 100,
render: (s: string) => {
const m = statusMap[s] || { color: 'default', label: s }
return <Tag color={m.color}>{m.label}</Tag>
},
width: 110,
},
{
title: 'Created', dataIndex: 'created_at', key: 'created_at',
render: (v: string) => v?.slice(0, 16),
width: 170,
title: '创建时间', dataIndex: 'created_at', key: 'created_at',
render: (v: string) => v?.slice(0, 16), width: 170,
},
]
return (
<div>
<Title level={3} style={{ marginBottom: 24, fontWeight: 600 }}>Overview</Title>
<Title level={3} style={{ marginBottom: 24 }}>概览</Title>
<Row gutter={[24, 24]}>
<Col xs={24} sm={8}>
<Card style={{ borderRadius: 12 }}>
<Statistic
title="Live Now"
value={liveCount}
prefix={<SignalFilled style={{ color: '#2E7D32' }} />}
loading={loading}
/>
<Card style={{ borderRadius: 10 }}>
<Statistic title="直播中" value={liveCount}
prefix={<SignalFilled style={{ color: '#5B8C5A' }} />} loading={loading} />
</Card>
</Col>
<Col xs={24} sm={8}>
<Card style={{ borderRadius: 12 }}>
<Statistic
title="Scheduled"
value={pendingCount}
prefix={<VideoCameraOutlined style={{ color: '#1565C0' }} />}
loading={loading}
/>
<Card style={{ borderRadius: 10 }}>
<Statistic title="待开播" value={pendingCount}
prefix={<VideoCameraOutlined style={{ color: '#6B8CA8' }} />} loading={loading} />
</Card>
</Col>
<Col xs={24} sm={8}>
<Card style={{ borderRadius: 12 }}>
<Statistic
title="Total Streams"
value={streams.length}
prefix={<PlayCircleOutlined />}
loading={loading}
/>
<Card style={{ borderRadius: 10 }}>
<Statistic title="直播总数" value={streams.length}
prefix={<PlayCircleOutlined />} loading={loading} />
</Card>
</Col>
</Row>
<Card title="Recent Streams" style={{ marginTop: 24, borderRadius: 12 }}>
<Table
dataSource={streams.slice(0, 10)}
columns={recentColumns}
rowKey="id"
loading={loading}
pagination={false}
size="middle"
/>
<Card title="最近直播" style={{ marginTop: 24, borderRadius: 10 }}>
<Table dataSource={streams.slice(0, 10)} columns={columns}
rowKey="id" loading={loading} pagination={false} size="middle" />
</Card>
</div>
)