diff --git a/backend/service/bg.go b/backend/service/bg.go index 0909bcd..c7fc89e 100644 --- a/backend/service/bg.go +++ b/backend/service/bg.go @@ -17,14 +17,11 @@ const ( BgSurfaceSite = "site" BgSurfaceAdmin = "admin" - BgModeCover = "cover" - BgModeContain = "contain" - BgModeRepeat = "repeat" - BgModeRepeatX = "repeat-x" - BgModeRepeatY = "repeat-y" - BgModeStretch = "stretch" - BgModeCenter = "center" - DefaultBgMode = BgModeCover + BgModeCover = "cover" + BgModeContain = "contain" + BgModeRepeat = "repeat" + BgModeStretch = "stretch" + DefaultBgMode = BgModeCover ) // ErrBgFileMissing 设置了合法 URL 但磁盘上没有对应文件 @@ -37,10 +34,7 @@ var bgModes = map[string]struct{}{ BgModeCover: {}, BgModeContain: {}, BgModeRepeat: {}, - BgModeRepeatX: {}, - BgModeRepeatY: {}, BgModeStretch: {}, - BgModeCenter: {}, } // NormalizeBgMode 校验铺放模式;空串视为默认 cover diff --git a/backend/service/bg_test.go b/backend/service/bg_test.go index 6d8dd54..30cb7b6 100644 --- a/backend/service/bg_test.go +++ b/backend/service/bg_test.go @@ -36,8 +36,13 @@ func TestNormalizeBgMode(t *testing.T) { if _, ok := NormalizeBgMode("zoom"); ok { t.Fatal("unknown mode should fail") } + for _, legacy := range []string{"repeat-x", "repeat-y", "center"} { + if _, ok := NormalizeBgMode(legacy); ok { + t.Fatalf("legacy mode %s should fail", legacy) + } + } for _, want := range []string{ - BgModeCover, BgModeContain, BgModeRepeat, BgModeRepeatX, BgModeRepeatY, BgModeStretch, BgModeCenter, + BgModeCover, BgModeContain, BgModeRepeat, BgModeStretch, } { got, ok := NormalizeBgMode(" " + want + " ") if !ok || got != want { diff --git a/frontend/app/admin/appearance/AppearanceAdmin.tsx b/frontend/app/admin/appearance/AppearanceAdmin.tsx index 6724091..b2f0a01 100644 --- a/frontend/app/admin/appearance/AppearanceAdmin.tsx +++ b/frontend/app/admin/appearance/AppearanceAdmin.tsx @@ -24,8 +24,8 @@ import { AdminSettingRow, AdminStepper, AdminSwitch, - AdminSegmented, } from "@/components/admin"; +import BgModePicker from "./BgModePicker"; import { apiUpdateSiteSettings, apiUploadSiteBackground, @@ -41,10 +41,9 @@ import { normalizeHex, } from "@/lib/theme"; import { - BG_MODE_OPTIONS, + BG_PREVIEW_ASPECT, applySiteBackground, bgConfigFromSettings, - normalizeBgMode, previewBoxStyle, type BgMode, type BgSurface, @@ -239,7 +238,7 @@ export default function AppearanceAdmin({ initial }: { initial: PublicSettings } try { await saveBgPatch( { [key]: mode }, - surface === "site" ? "前台铺放模式已保存" : "后台铺放模式已保存" + surface === "site" ? "前台铺放方式已保存" : "后台铺放方式已保存" ); } catch (e) { toast(e instanceof Error ? e.message : "保存失败", "error"); @@ -285,11 +284,15 @@ export default function AppearanceAdmin({ initial }: { initial: PublicSettings }
{!url && ( )} + + 16:9 +
-

铺放模式

- 铺放方式

+ void onChangeBgMode(surface, normalizeBgMode(key))} - options={BG_MODE_OPTIONS.map((o) => ({ - key: o.key, - label: o.label, - disabled: busy, - }))} + disabled={busy} + ariaLabel={`${title}铺放方式`} + onChange={(next) => void onChangeBgMode(surface, next)} /> -

- {BG_MODE_OPTIONS.find((o) => o.key === mode)?.hint} -

); @@ -675,7 +680,7 @@ export default function AppearanceAdmin({ initial }: { initial: PublicSettings } 站点背景图 } - description="前台与后台可分别上传 JPEG / PNG / WebP(≤8MB)。未配置时使用默认纹饰画布。" + description="前台与后台可分别上传 JPEG / PNG / WebP(≤8MB)。预览按 16:9 常见显示器比例。未配置时使用默认纹饰画布。" >
{renderBgRail( diff --git a/frontend/app/admin/appearance/BgModePicker.tsx b/frontend/app/admin/appearance/BgModePicker.tsx new file mode 100644 index 0000000..177c756 --- /dev/null +++ b/frontend/app/admin/appearance/BgModePicker.tsx @@ -0,0 +1,107 @@ +"use client"; + +import { BG_MODE_OPTIONS, type BgMode } from "@/lib/bg"; + +/** 铺放示意:16:9 小窗里用色块表达裁切 / 完整 / 平铺 / 拉伸。 */ +function ModeGlyph({ mode }: { mode: BgMode }) { + const photo = "var(--accent-soft)"; + const ink = "var(--accent)"; + return ( + + {mode === "cover" && ( + + + + )} + {mode === "contain" && ( + + + + )} + {mode === "repeat" && ( + + {Array.from({ length: 6 }, (_, i) => ( + + ))} + + )} + {mode === "stretch" && ( + + + + )} + + ); +} + +export default function BgModePicker({ + value, + onChange, + disabled, + ariaLabel, +}: { + value: BgMode; + onChange: (mode: BgMode) => void; + disabled?: boolean; + ariaLabel: string; +}) { + return ( +
+ {BG_MODE_OPTIONS.map((o) => { + const active = value === o.key; + return ( + + ); + })} +
+ ); +} diff --git a/frontend/components/Footer.tsx b/frontend/components/Footer.tsx index 9bc18fa..bf7c303 100644 --- a/frontend/components/Footer.tsx +++ b/frontend/components/Footer.tsx @@ -16,7 +16,6 @@ export default function Footer({ siteName = "姜十三社区" }: { siteName?: st return (