From 1dc1b6f6b50ce221576e4f411310cb89093d6c6c Mon Sep 17 00:00:00 2001 From: ddcat666 <26564566+ddcat666@users.noreply.github.com> Date: Thu, 24 Sep 2026 23:59:12 +0800 Subject: [PATCH] =?UTF-8?q?fix(platform):=20=E5=A4=96=E8=A7=82=E4=B8=BB?= =?UTF-8?q?=E9=A2=98=20-=20=E4=BF=9D=E8=AF=81=E7=BB=8F=E5=85=B8=E9=BB=91?= =?UTF-8?q?=E7=99=BD=E4=B8=BB=E9=A2=98=E4=B8=8D=E5=8F=AF=E8=A2=AB=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E6=88=96=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/appearance_button_fill_test.go | 8 ++++-- backend/internal/app/appearance_skins.go | 28 +++++++++++-------- backend/internal/app/appearance_test.go | 20 +++++++++---- web/src/lib/skin-themes.ts | 10 +++---- 4 files changed, 41 insertions(+), 25 deletions(-) diff --git a/backend/internal/app/appearance_button_fill_test.go b/backend/internal/app/appearance_button_fill_test.go index bb380d7ed..bfda36a81 100644 --- a/backend/internal/app/appearance_button_fill_test.go +++ b/backend/internal/app/appearance_button_fill_test.go @@ -47,8 +47,12 @@ func TestAppearanceButtonFillRoundTripAndValidation(t *testing.T) { }) } input.SkinThemes[0].Tokens.Buttons.Light.Start = "#000000" - if _, err := svc.UpdateAppearance(admin, input); err == nil { - t.Fatal("classic fill mutation accepted") + overwritten, err := svc.UpdateAppearance(admin, input) + if err != nil { + t.Fatal(err) + } + if overwritten.SkinThemes[0].Tokens.Buttons != defaultClassicAppearanceSkin().Tokens.Buttons { + t.Fatal("classic fill mutation was stored") } } diff --git a/backend/internal/app/appearance_skins.go b/backend/internal/app/appearance_skins.go index e1f05fdc7..ca798a5d1 100644 --- a/backend/internal/app/appearance_skins.go +++ b/backend/internal/app/appearance_skins.go @@ -262,11 +262,20 @@ func normalizeAppearanceSkinThemes(themes []AppearanceSkinTheme) []AppearanceSki result := make([]AppearanceSkinTheme, len(themes)) copy(result, themes) builtins := defaultAppearanceSkinThemes() + classic := defaultClassicAppearanceSkin() + replacedClassic := false for index := range result { result[index].ID = strings.ToLower(strings.TrimSpace(result[index].ID)) + if result[index].ID == defaultAppearanceSkinID { + // The system default is not editable. Whatever was submitted is + // discarded so a drifted client copy cannot reject the whole save. + result[index] = classic + replacedClassic = true + continue + } result[index].Name = strings.TrimSpace(result[index].Name) result[index].Description = strings.TrimSpace(result[index].Description) - result[index].Locked = result[index].ID == defaultAppearanceSkinID + result[index].Locked = false // Only a wholly absent legacy button block is upgraded. Partial or // malformed submitted parameters remain invalid on the write path. if result[index].Tokens.Buttons == (AppearanceSkinButtons{}) { @@ -285,6 +294,12 @@ func normalizeAppearanceSkinThemes(themes []AppearanceSkinTheme) []AppearanceSki normalizeAppearanceSkinModeColors(&result[index].Tokens.Dark) result[index].Tokens.Components.ShadowStyle = strings.ToLower(strings.TrimSpace(result[index].Tokens.Components.ShadowStyle)) } + if !replacedClassic { + if len(result) >= maxAppearanceSkinThemes { + result = result[:maxAppearanceSkinThemes-1] + } + result = append([]AppearanceSkinTheme{classic}, result...) + } return result } @@ -329,8 +344,6 @@ func validateAppearanceSkinThemes(themes []AppearanceSkinTheme, selectedID strin } seen := make(map[string]struct{}, len(themes)) foundSelected := false - foundClassic := false - classic := defaultClassicAppearanceSkin() for _, skin := range themes { if !appearanceSkinIDPattern.MatchString(skin.ID) { return BadAuthRequest("皮肤主题 ID 无效") @@ -342,12 +355,6 @@ func validateAppearanceSkinThemes(themes []AppearanceSkinTheme, selectedID strin if skin.ID == selectedID { foundSelected = true } - if skin.ID == defaultAppearanceSkinID { - foundClassic = true - if skin.Name != classic.Name || skin.Description != classic.Description || !reflect.DeepEqual(skin.Tokens, classic.Tokens) { - return BadAuthRequest("经典黑白为系统默认主题,不能修改或删除") - } - } if err := validateAppearanceSkinText(skin.Name, "皮肤主题名称", 40, true); err != nil { return err } @@ -369,9 +376,6 @@ func validateAppearanceSkinThemes(themes []AppearanceSkinTheme, selectedID strin } } } - if !foundClassic { - return BadAuthRequest("经典黑白为系统默认主题,不能修改或删除") - } if !foundSelected { return BadAuthRequest("当前启用的皮肤主题不存在") } diff --git a/backend/internal/app/appearance_test.go b/backend/internal/app/appearance_test.go index 7956a5333..d87e7f1c9 100644 --- a/backend/internal/app/appearance_test.go +++ b/backend/internal/app/appearance_test.go @@ -452,17 +452,25 @@ func TestAppearanceSkinLibrarySupportsEditableCopiesAndProtectsClassic(t *testin t.Fatalf("custom skin round trip = %#v", updated) } + classic := defaultClassicAppearanceSkin() withoutClassic := append([]AppearanceSkinTheme(nil), themes[1:]...) - _, err = svc.UpdateAppearance(admin, AppearanceSetting{BrandName: "HIMA Studio", BrandSlug: "hima-studio", AuthHeroTitle: defaultAppearanceHeroTitle, SkinID: custom.ID, SkinThemes: withoutClassic}) - if err == nil || !strings.Contains(err.Error(), "不能修改或删除") { - t.Fatalf("missing classic error = %v", err) + restored, err := svc.UpdateAppearance(admin, AppearanceSetting{BrandName: "HIMA Studio", BrandSlug: "hima-studio", AuthHeroTitle: defaultAppearanceHeroTitle, SkinID: custom.ID, SkinThemes: withoutClassic}) + if err != nil { + t.Fatal(err) + } + if restored.SkinThemes[0] != classic { + t.Fatalf("missing classic was not restored = %#v", restored.SkinThemes[0]) } mutatedClassic := append([]AppearanceSkinTheme(nil), themes...) mutatedClassic[0].Name = "改名" - _, err = svc.UpdateAppearance(admin, AppearanceSetting{BrandName: "HIMA Studio", BrandSlug: "hima-studio", AuthHeroTitle: defaultAppearanceHeroTitle, SkinID: custom.ID, SkinThemes: mutatedClassic}) - if err == nil || !strings.Contains(err.Error(), "不能修改或删除") { - t.Fatalf("mutated classic error = %v", err) + mutatedClassic[0].Tokens.Light.Primary = "#000000" + overwritten, err := svc.UpdateAppearance(admin, AppearanceSetting{BrandName: "HIMA Studio", BrandSlug: "hima-studio", AuthHeroTitle: defaultAppearanceHeroTitle, SkinID: custom.ID, SkinThemes: mutatedClassic}) + if err != nil { + t.Fatal(err) + } + if overwritten.SkinThemes[0] != classic || overwritten.Public.ActiveSkin.ID != custom.ID { + t.Fatalf("mutated classic was not overwritten = %#v", overwritten.SkinThemes[0]) } invalidColor := append([]AppearanceSkinTheme(nil), themes...) diff --git a/web/src/lib/skin-themes.ts b/web/src/lib/skin-themes.ts index d41a60238..58a47fe84 100644 --- a/web/src/lib/skin-themes.ts +++ b/web/src/lib/skin-themes.ts @@ -336,10 +336,10 @@ export const DEFAULT_CLASSIC_SKIN: SkinDefinition = { info: "#2563eb", workspace: "#ffffff", workspaceGrid: "#f3f3f3", - adminBackground: "#f5f5f5", + adminBackground: "#f3f4f6", adminSurface: "#ffffff", - adminSubtle: "#f6f6f6", - adminStrong: "#ececec", + adminSubtle: "#f7f8fa", + adminStrong: "#eceff3", authBackground: "#ffffff", authPanel: "#f7f7f7", authCard: "#ffffff", @@ -388,9 +388,9 @@ export const DEFAULT_CLASSIC_SKIN: SkinDefinition = { info: "#60a5fa", workspace: "#181818", workspaceGrid: "#222222", - adminBackground: "#0f0f0f", + adminBackground: "#101010", adminSurface: "#181818", - adminSubtle: "#222222", + adminSubtle: "#202020", adminStrong: "#2a2a2a", authBackground: "#08090c", authPanel: "#0b0c10",