From dce6ab2282cc8a397454ff27477c4cf5e93aa300 Mon Sep 17 00:00:00 2001
From: HouYunFei <1844025705@qq.com>
Date: Thu, 21 May 2026 11:04:59 +0800
Subject: [PATCH] =?UTF-8?q?refactor(theme):=20=E9=87=8D=E6=9E=84=E4=B8=BB?=
=?UTF-8?q?=E9=A2=98=E9=85=8D=E7=BD=AE=E5=92=8C=E7=AE=A1=E7=90=86=E5=90=8E?=
=?UTF-8?q?=E5=8F=B0=E6=A0=B7=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 将 Ant Design 主题配置提取到独立的 getAntThemeConfig 函数
- 移除 AGENTS.md 中关于状态管理和样式的冗余说明
- 删除 globals.css 中的 admin 相关样式和颜色变量
- 更新管理后台布局使用新的主题配置和样式常量
- 调整代码编辑器样式使用 Ant Design 的 CSS 变量
- 优化管理后台整体视觉风格为中性色调
---
AGENTS.md | 4 +
docs/pending-test.md | 1 +
web/src/app/(admin)/admin/layout.tsx | 78 ++++-----
web/src/app/(admin)/admin/settings/page.tsx | 36 ++--
web/src/app/globals.css | 68 --------
web/src/components/ant-theme-provider.tsx | 174 +-------------------
web/src/lib/app-theme.ts | 59 +++++++
7 files changed, 118 insertions(+), 302 deletions(-)
create mode 100644 web/src/lib/app-theme.ts
diff --git a/AGENTS.md b/AGENTS.md
index 39adff6..3e1a3c5 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -34,6 +34,7 @@
- 前端使用 Next.js App Router、React、TypeScript、Ant Design、Tailwind、Zustand。
- API 请求统一放在 `web/src/services/api/`。
- 全局或跨页面状态优先放在 `web/src/stores/`。
+- 已经放在全局 store 或全局 hook 中的状态/动作,组件需要时直接使用对应 store/hook,不要为了“纯组件”层层透传 props;避免一个组件传递过多参数。
- 画布相关状态和组件放在 `web/src/app/(user)/canvas/` 内部。
- 页面里只有一个主业务组件时直接写在 `page.tsx`,不要单独拆 `Manager` 组件再传一堆 props。
- 管理后台页面私有组件放到各自页面目录的 `components/` 下,例如 `admin/assets/components/`、`admin/prompts/components/`;不要为了单页面使用放到 `admin/components/` 共享目录。
@@ -42,6 +43,9 @@
- UI 图标优先使用 `lucide-react` 或项目已经使用的 Ant Design 图标。
- 页面文案保持中文。
- 不要在组件里堆太多无关逻辑;复杂逻辑优先抽成同目录工具函数或小组件。
+- 样式优先由组件自己管理;组件私有样式优先使用 Tailwind className 或少量内联 style,不要为单个组件新增大量全局 CSS。
+- 全局 CSS 只放基础变量、全局重置、跨页面通用样式和少量第三方组件必要覆盖;不要在 `globals.css` 堆页面私有样式。
+- 代码尽量短小直接,少拆不必要组件,少做多层 props 传递,避免为了抽象堆出更多代码。
- 前端业务数据需要浏览器本地持久化时,默认使用 `localforage`;`localStorage` 只用于极小的简单配置,不要用来保存业务列表、生成记录、图片、base64 或大 JSON。
## 画布 UI 规范
diff --git a/docs/pending-test.md b/docs/pending-test.md
index a0d40ee..37735b7 100644
--- a/docs/pending-test.md
+++ b/docs/pending-test.md
@@ -6,3 +6,4 @@
- 后端应自动维护 `settings` 表,并支持公开读取 public 配置、管理员读取和保存 public/private 两行 JSON 配置。
- 管理后台系统设置页面应按公开/私有两个 Tab 区分配置,并支持可视化编辑和手动编辑 JSON,且可保存 public/private 配置。
- 管理后台私有配置中的模型渠道应以列表展示、通过抽屉新增编辑,并支持填写权重、远程获取模型列表、模型清单测试和批量测试。
+- 管理后台亮色和暗色主题应使用接近 shadcn 的黑白中性色,侧栏、顶部栏、卡片、表格、弹窗和 JSON 编辑器不应再出现偏棕色或默认蓝色主色。
diff --git a/web/src/app/(admin)/admin/layout.tsx b/web/src/app/(admin)/admin/layout.tsx
index 23f751f..32121ac 100644
--- a/web/src/app/(admin)/admin/layout.tsx
+++ b/web/src/app/(admin)/admin/layout.tsx
@@ -1,7 +1,7 @@
"use client";
import { FileTextOutlined, HomeOutlined, LogoutOutlined, PictureOutlined, SettingOutlined } from "@ant-design/icons";
-import { Button, Flex, Layout, Menu, Typography } from "antd";
+import { Button, Flex, Layout, Menu, Typography, theme } from "antd";
import { LogOut } from "lucide-react";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
@@ -9,10 +9,12 @@ import type { ReactNode } from "react";
import { useEffect } from "react";
import { UserStatusActions } from "@/components/user-status-actions";
+import { adminLayoutStyle } from "@/lib/app-theme";
import { useThemeStore } from "@/stores/use-theme-store";
import { useUserStore } from "@/stores/use-user-store";
export default function AdminLayout({ children }: { children: ReactNode }) {
+ const { token: antToken } = theme.useToken();
const router = useRouter();
const pathname = usePathname();
const token = useUserStore((state) => state.token);
@@ -38,50 +40,50 @@ export default function AdminLayout({ children }: { children: ReactNode }) {
if (!isReady || !token || user?.role !== "admin") {
return (
-
+
);
}
return (
-
-
-
-
- 无限画布
+
+
+
+
+ 无限画布
+
+ , label: 提示词管理, style: adminLayoutStyle.menuItem },
+ { key: "/admin/assets", icon: , label: 素材库, style: adminLayoutStyle.menuItem },
+ { key: "/admin/settings", icon: , label: 系统设置, style: adminLayoutStyle.menuItem },
+ ]}
+ />
+
+ } href="/canvas" target="_blank" rel="noreferrer">前往画布
+ } onClick={logout}>退出登录
+
+
+
+
+ {pageTitle}
+
+ , label: "退出登录", onClick: logout }]}
+ />
- , label: 提示词管理 },
- { key: "/admin/assets", icon: , label: 素材库 },
- { key: "/admin/settings", icon: , label: 系统设置 },
- ]}
- />
-
- } href="/canvas" target="_blank" rel="noreferrer">前往画布
- } onClick={logout}>退出登录
-
-
-
-
- {pageTitle}
-
- , label: "退出登录", onClick: logout }]}
- />
-
-
- {children}
-
+
+ {children}
+
);
}
diff --git a/web/src/app/(admin)/admin/settings/page.tsx b/web/src/app/(admin)/admin/settings/page.tsx
index 025c40e..3c31382 100644
--- a/web/src/app/(admin)/admin/settings/page.tsx
+++ b/web/src/app/(admin)/admin/settings/page.tsx
@@ -1,9 +1,7 @@
"use client";
import { CheckCircleOutlined, DeleteOutlined, FormatPainterOutlined, PlusOutlined, ReloadOutlined, SaveOutlined } from "@ant-design/icons";
-import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
import { json } from "@codemirror/lang-json";
-import { tags } from "@lezer/highlight";
import { App, Button, Card, Col, Drawer, Flex, Form, Input, InputNumber, Modal, Row, Segmented, Select, Space, Switch, Table, Tabs, Tag, Typography } from "antd";
import dynamic from "next/dynamic";
import { useEffect, useState } from "react";
@@ -14,27 +12,17 @@ import { useUserStore } from "@/stores/use-user-store";
const CodeMirror = dynamic(() => import("@uiw/react-codemirror"), { ssr: false });
const jsonEditorTheme = EditorView.theme({
- "&": { backgroundColor: "var(--card)", color: "var(--foreground)" },
- ".cm-content": { caretColor: "var(--foreground)", padding: "12px 0" },
+ "&": { backgroundColor: "var(--ant-color-bg-container)", color: "var(--ant-color-text)" },
+ ".cm-content": { caretColor: "var(--ant-color-text)", padding: "12px 0" },
".cm-line": { padding: "0 18px" },
- ".cm-gutters": { backgroundColor: "color-mix(in srgb, var(--card) 94%, var(--foreground))", borderRight: "1px solid var(--border)", color: "var(--muted-foreground)" },
- ".cm-activeLine": { backgroundColor: "color-mix(in srgb, var(--primary) 9%, transparent)" },
- ".cm-activeLineGutter": { backgroundColor: "color-mix(in srgb, var(--primary) 10%, transparent)", color: "var(--foreground)" },
- ".cm-cursor": { borderLeftColor: "var(--foreground)" },
- ".cm-selectionBackground, &.cm-focused .cm-selectionBackground": { backgroundColor: "color-mix(in srgb, var(--primary) 25%, transparent)" },
- ".cm-foldPlaceholder": { backgroundColor: "var(--muted)", border: "1px solid var(--border)", color: "var(--muted-foreground)" },
+ ".cm-gutters": { backgroundColor: "var(--ant-color-fill-quaternary)", borderRight: "1px solid var(--ant-color-border)", color: "var(--ant-color-text-tertiary)" },
+ ".cm-activeLine": { backgroundColor: "var(--ant-color-fill-quaternary)" },
+ ".cm-activeLineGutter": { backgroundColor: "var(--ant-color-fill-quaternary)", color: "var(--ant-color-text)" },
+ ".cm-cursor": { borderLeftColor: "var(--ant-color-text)" },
+ ".cm-selectionBackground, &.cm-focused .cm-selectionBackground": { backgroundColor: "var(--ant-control-item-bg-active)" },
+ ".cm-foldPlaceholder": { backgroundColor: "var(--ant-color-fill-quaternary)", border: "1px solid var(--ant-color-border)", color: "var(--ant-color-text-tertiary)" },
"&.cm-focused": { outline: "none" },
-}, { dark: true });
-const jsonHighlightStyle = HighlightStyle.define([
- { tag: tags.propertyName, color: "#f2a7a7" },
- { tag: tags.string, color: "#d6c6a8" },
- { tag: tags.number, color: "#ddb27d" },
- { tag: tags.bool, color: "#c5b3ff" },
- { tag: tags.null, color: "#c5b3ff" },
- { tag: tags.punctuation, color: "rgba(250, 250, 249, 0.62)" },
- { tag: tags.squareBracket, color: "rgba(250, 250, 249, 0.62)" },
- { tag: tags.brace, color: "rgba(250, 250, 249, 0.62)" },
-]);
+});
const emptySettings: AdminSettings = {
public: {
@@ -291,11 +279,10 @@ export default function AdminSettingsPage() {
setJsonText((current) => ({ ...current, public: value }))}
- className="admin-json-editor"
style={{ fontSize: 13 }}
/>
@@ -341,11 +328,10 @@ export default function AdminSettingsPage() {