feat(cards): 卡片外观可配置(不透明度 / 毛玻璃)

- 站点设置新增 card.opacity(40–100)与 card.blur,后台「首页管理 → 卡片外观」可调
- 前台所有卡片(首页 / 分组页 / 详情 / 侧栏)共用一套 CSS 变量
- 顺带带上上一轮:背景缓存、搜索记忆、明暗图标跟随实际状态、卡片描边/阴影、分组页卡片外壳
This commit is contained in:
2026-11-05 12:45:11 +08:00
parent a9a913942a
commit 4ce1b7e7c1
8 changed files with 229 additions and 19 deletions
+56 -1
View File
@@ -83,6 +83,7 @@ type TabKey =
| 'content'
| 'home-sidebar'
| 'home-cards'
| 'home-cards-look'
| 'home-header'
| 'home-background'
| 'home-search'
@@ -102,6 +103,7 @@ const ADMIN_NAV: MenuItem[] = [
{ key: 'home-search', label: '搜索框' },
{ key: 'home-sidebar', label: '侧边栏' },
{ key: 'home-cards', label: '内容卡片' },
{ key: 'home-cards-look', label: '卡片外观' },
],
},
{ key: 'profile', label: '资料管理' },
@@ -281,7 +283,7 @@ export function AdminNext() {
<div className="header-actions">
<IconButton
label={isDark ? '切换到浅色' : '切换到深色'}
icon={<Icon name={isDark ? 'sun_line' : 'moon_line'} />}
icon={<Icon name={isDark ? 'moon_line' : 'sun_line'} />}
onClick={() => void toggleTheme()}
disabled={!authenticated || saving}
/>
@@ -390,6 +392,16 @@ export function AdminNext() {
/>
)}
{tab === 'home-cards-look' && (
<section className="admin-home-section">
<Title level={4}>卡片外观</Title>
<CardLookPanel
value={draft.card}
onChange={(card) => setDraft({ ...draft, card })}
/>
</section>
)}
{tab === 'home-background' && (
<BackgroundPanel
value={draft.background}
@@ -416,6 +428,7 @@ export function AdminNext() {
{(tab === 'home-header' ||
tab === 'home-background' ||
tab === 'home-search' ||
tab === 'home-cards-look' ||
tab === 'profile') && (
<Space gap="sm" justify="end" className="admin-actions">
<Button
@@ -1394,6 +1407,48 @@ function SidebarPanel({
)
}
// ---------- 卡片外观(透明度 / 毛玻璃) ----------
function CardLookPanel({
value,
onChange,
}: {
value: SiteSettings['card']
onChange: (card: SiteSettings['card']) => void
}) {
return (
<Space direction="column" gap="md" align="stretch" className="admin-form">
<Text size="sm" tone="muted">
说明:前台所有卡片共用这一套外观(首页、分组页、详情页、侧栏)。不透明度越低,背景图透得越多;
毛玻璃要配合透明才看得出来。
</Text>
<FormField
label="卡片不透明度"
htmlFor="card-opacity"
hint="百分比 40–100;100 为实底,40 最透"
>
<Input
id="card-opacity"
type="number"
min={40}
max={100}
value={String(value.opacity)}
onChange={(event) => {
const opacity = Number(event.target.value)
if (Number.isFinite(opacity)) onChange({ ...value, opacity })
}}
/>
</FormField>
<ToggleRow
label="毛玻璃"
checked={value.blur === true}
onChange={(blur) => onChange({ ...value, blur })}
switchLabel="卡片使用毛玻璃"
/>
</Space>
)
}
// ---------- 首页卡片(勾选分组) ----------
function CardsPanel({