feat(dashboard): add background customization system

Add image/video background support across 5 layout layers (page, sidebar,
header, card, dialog) with per-layer effect controls and custom CSS injection.
- IndexedDB asset store for blob persistence (idb)
- AssetStoreProvider for blob URL lifecycle management
- BackgroundLayer component with CSS effects and prefers-reduced-motion support
- useBackground hook with inherit logic
- BackgroundUploader with local file and remote URL support
- BackgroundEffectsControls and ComponentCSSEditor UI components
- Background settings integrated into AppearanceTab in settings.tsx
- Layout, Card, and Dialog integration via non-breaking wrapper components
This commit is contained in:
DrSmoothl
2026-02-23 18:08:01 +08:00
parent 698b8355a4
commit 1fec4c3b9a
16 changed files with 1322 additions and 17 deletions

View File

@@ -0,0 +1,80 @@
import { AlertTriangle, Trash2 } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { CodeEditor } from '@/components/CodeEditor'
import { Label } from '@/components/ui/label'
import { sanitizeCSS } from '@/lib/theme/sanitizer'
export type ComponentCSSEditorProps = {
/** 组件唯一标识符 */
componentId: string
/** 当前 CSS 内容 */
value: string
/** CSS 内容变更回调 */
onChange: (css: string) => void
/** 编辑器标签文字 */
label?: string
/** 编辑器高度,默认 200px */
height?: string
}
/**
* 组件级 CSS 编辑器
* 提供 CSS 代码编辑、语法高亮和安全过滤警告功能
*/
export function ComponentCSSEditor({
componentId,
value,
onChange,
label,
height = '200px',
}: ComponentCSSEditorProps) {
// 实时计算 CSS 警告
const { warnings } = sanitizeCSS(value)
return (
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label className="text-sm font-medium">
{label || '自定义 CSS'}
</Label>
<Button
variant="ghost"
size="sm"
onClick={() => onChange('')}
disabled={!value}
className="h-7 px-2 text-xs text-muted-foreground hover:text-destructive"
title="清除所有 CSS"
>
<Trash2 className="mr-1.5 h-3.5 w-3.5" />
</Button>
</div>
<div className="rounded-md border bg-card overflow-hidden">
<CodeEditor
value={value}
onChange={onChange}
language="css"
height={height}
placeholder={`/* 为 ${componentId} 组件编写自定义 CSS */\n\n/* 示例: */\n/* .custom-class { background: red; } */`}
/>
{warnings.length > 0 && (
<div className="border-t border-yellow-200 dark:border-yellow-800 bg-yellow-50 dark:bg-yellow-950/30 p-3">
<div className="flex items-center gap-2 text-yellow-800 dark:text-yellow-200 text-xs font-medium mb-1">
<AlertTriangle className="h-3.5 w-3.5" />
CSS
</div>
<ul className="text-[10px] sm:text-xs text-yellow-700 dark:text-yellow-300 space-y-0.5 ml-5 list-disc">
{warnings.map((w, i) => (
<li key={i}>{w}</li>
))}
</ul>
</div>
)}
</div>
</div>
)
}