feat(admin): 增加背景管理
- settings 新增 background.light / background.dark,留空使用内置图 - 后台首页管理新增「背景管理」,两栏 URL 输入与实时预览,可一键恢复内置图 - 前台按设置覆盖明暗背景图,仍保留 65vh 与交叉淡入 - 保存/撤销沿用现有设置乐观锁流程
This commit is contained in:
@@ -40,6 +40,7 @@ import {
|
||||
saveSettings,
|
||||
TONE_OPTIONS,
|
||||
type ProfileSettings,
|
||||
type BackgroundSettings,
|
||||
type SettingsDocument,
|
||||
type SettingsTone,
|
||||
type SiteSettings,
|
||||
@@ -63,6 +64,8 @@ import {
|
||||
import { createItem, deleteItem, fetchItem, saveItem } from '../../items'
|
||||
import { useProjects } from '../../useProjects'
|
||||
import type { Project } from '../../api'
|
||||
import siteBgLight from '../../assets/site-bg.jpg'
|
||||
import siteBgDark from '../../assets/site-bg-dark.jpg'
|
||||
|
||||
/**
|
||||
* 新版后台:原子模型。
|
||||
@@ -77,6 +80,7 @@ type TabKey =
|
||||
| 'home-sidebar'
|
||||
| 'home-cards'
|
||||
| 'home-header'
|
||||
| 'home-background'
|
||||
| 'profile'
|
||||
| 'system'
|
||||
|
||||
@@ -89,6 +93,7 @@ const ADMIN_NAV: MenuItem[] = [
|
||||
label: '首页管理',
|
||||
children: [
|
||||
{ key: 'home-header', label: '页头' },
|
||||
{ key: 'home-background', label: '背景管理' },
|
||||
{ key: 'home-sidebar', label: '侧边栏' },
|
||||
{ key: 'home-cards', label: '内容卡片' },
|
||||
],
|
||||
@@ -379,6 +384,13 @@ export function AdminNext() {
|
||||
/>
|
||||
)}
|
||||
|
||||
{tab === 'home-background' && (
|
||||
<BackgroundPanel
|
||||
value={draft.background}
|
||||
onChange={(background) => setDraft({ ...draft, background })}
|
||||
/>
|
||||
)}
|
||||
|
||||
{tab === 'profile' && (
|
||||
<ProfilePanel
|
||||
value={draft.profile}
|
||||
@@ -388,7 +400,9 @@ export function AdminNext() {
|
||||
|
||||
{tab === 'system' && <SystemPanel />}
|
||||
|
||||
{(tab === 'home-header' || tab === 'profile') && (
|
||||
{(tab === 'home-header' ||
|
||||
tab === 'home-background' ||
|
||||
tab === 'profile') && (
|
||||
<Space gap="sm" justify="end" className="admin-actions">
|
||||
<Button
|
||||
variant="secondary"
|
||||
@@ -2037,6 +2051,64 @@ function HeaderPanel({
|
||||
)
|
||||
}
|
||||
|
||||
function BackgroundPanel({
|
||||
value,
|
||||
onChange,
|
||||
}: {
|
||||
value: BackgroundSettings
|
||||
onChange: (background: BackgroundSettings) => void
|
||||
}) {
|
||||
const update = (patch: Partial<BackgroundSettings>) => onChange({ ...value, ...patch })
|
||||
const preview = (url: string, fallback: string) => ({
|
||||
backgroundImage: `url(${JSON.stringify(url.trim() || fallback)})`,
|
||||
})
|
||||
|
||||
return (
|
||||
<Space direction="column" gap="lg" align="stretch" className="admin-form">
|
||||
<Title level={4}>背景管理</Title>
|
||||
<Text size="sm" tone="muted">
|
||||
说明:分别设置亮色与暗色模式顶部的背景图,留空使用内置图;站内图片请放到托管目录 public/ 后填写路径,或填完整 URL。
|
||||
</Text>
|
||||
|
||||
<div className="background-grid">
|
||||
<div className="background-section">
|
||||
<FormField label="亮色背景图" htmlFor="background-light" hint="留空使用内置亮色背景">
|
||||
<Input
|
||||
id="background-light"
|
||||
value={value.light}
|
||||
placeholder="/bg-light.jpg 或 https://…"
|
||||
onChange={(event) => update({ light: event.target.value })}
|
||||
/>
|
||||
</FormField>
|
||||
<div className="background-preview" style={preview(value.light, siteBgLight)}>
|
||||
<span>亮色预览</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="background-section">
|
||||
<FormField label="暗色背景图" htmlFor="background-dark" hint="留空使用内置暗色背景">
|
||||
<Input
|
||||
id="background-dark"
|
||||
value={value.dark}
|
||||
placeholder="/bg-dark.jpg 或 https://…"
|
||||
onChange={(event) => update({ dark: event.target.value })}
|
||||
/>
|
||||
</FormField>
|
||||
<div className="background-preview" style={preview(value.dark, siteBgDark)}>
|
||||
<span>暗色预览</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button variant="secondary" onClick={() => onChange({ light: '', dark: '' })}>
|
||||
恢复内置背景
|
||||
</Button>
|
||||
</div>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
|
||||
function ProfilePanel({
|
||||
value,
|
||||
onChange,
|
||||
|
||||
Reference in New Issue
Block a user