样式修改

This commit is contained in:
fengxiangdebaozi committed 2026-07-03 16:35:52 +08:00
1 parent 96947d7636
commit f3511e5cd0
15 files changed
+933 -84

No files matched your search

@@ -5,6 +5,7 @@ from __future__ import annotations
import logging
from typing import Literal
from urllib.parse import urlparse
from uuid import uuid4
from fastapi import APIRouter, HTTPException, Request
from pydantic import BaseModel, Field
@@ -13,6 +14,8 @@ from app.gateway.auth.password import hash_password_async
from app.gateway.deps import get_optional_user_from_request
from deerflow.config import get_app_config
from deerflow.config.system_settings import (
AppearanceRouteParam,
CompactShortcutButton,
LeaderboardSettings,
ModelConcurrencySettings,
PluginDomainAgentMapping,
@@ -537,6 +540,7 @@ class AppearanceResponse(BaseModel):
default_skill_view: Literal["card", "list"] = Field(..., description="System default skill list display mode — card or list")
default_agent_view: Literal["card", "list"] = Field(..., description="System default agent list display mode — card or list")
default_layout_mode: Literal["full", "compact"] = Field(..., description="System default interface layout — full (sidebar + header) or compact (hidden)")
compact_shortcut_buttons: list[CompactShortcutButton] = Field(default_factory=list, description="Shortcut buttons rendered in top bars")
class AppearanceUpdateRequest(BaseModel):
@@ -544,6 +548,61 @@ class AppearanceUpdateRequest(BaseModel):
default_skill_view: Literal["card", "list"] | None = Field(default=None)
default_agent_view: Literal["card", "list"] | None = Field(default=None)
default_layout_mode: Literal["full", "compact"] | None = Field(default=None)
compact_shortcut_buttons: list[CompactShortcutButton] | None = Field(default=None)
def _normalize_compact_shortcut_buttons(
buttons: list[CompactShortcutButton] | None,
) -> list[CompactShortcutButton]:
normalized: list[CompactShortcutButton] = []
seen_ids: set[str] = set()
for index, item in enumerate(buttons or []):
label = (item.label or "").strip() or "快捷入口"
url = (item.url or "").strip()
row_id = (item.id or "").strip() or uuid4().hex
if row_id in seen_ids:
row_id = uuid4().hex
seen_ids.add(row_id)
route_params: list[AppearanceRouteParam] = []
for param in item.route_params or []:
key = (param.key or "").strip()
if not key:
continue
if param.paramType == "theme":
route_params.append(
AppearanceRouteParam(
key=key,
paramType="theme",
themeValue=(param.themeValue or "").strip()
or "red=red;blue=blue;dark-blue=dark-blue",
)
)
continue
source = (param.source or "access_token").strip() or "access_token"
route_params.append(
AppearanceRouteParam(
key=key,
paramType="login",
source=source,
customValue=(param.customValue or "").strip()
if source == "other"
else None,
)
)
normalized.append(
CompactShortcutButton(
id=row_id,
label=label,
url=url,
route_params=route_params,
enabled=bool(item.enabled),
sort_order=index,
)
)
return normalized
def _appearance_response(appearance) -> AppearanceResponse:
@@ -552,6 +611,10 @@ def _appearance_response(appearance) -> AppearanceResponse:
default_skill_view=appearance.default_skill_view,
default_agent_view=appearance.default_agent_view,
default_layout_mode=appearance.default_layout_mode,
compact_shortcut_buttons=sorted(
appearance.compact_shortcut_buttons,
key=lambda item: item.sort_order,
),
)
@@ -574,6 +637,10 @@ async def update_appearance_settings(
for key in ("default_theme", "default_skill_view", "default_agent_view", "default_layout_mode"):
if key in fields and fields[key] is not None:
update[key] = fields[key]
if "compact_shortcut_buttons" in fields and body.compact_shortcut_buttons is not None:
update["compact_shortcut_buttons"] = _normalize_compact_shortcut_buttons(
body.compact_shortcut_buttons
)
if update:
updated_appearance = current.appearance.model_copy(update=update)
saved = save_system_settings(
@@ -18,6 +18,7 @@ import logging
import threading
from pathlib import Path
from typing import Literal
from uuid import uuid4
from pydantic import BaseModel, ConfigDict, Field
@@ -38,6 +39,60 @@ class PromptPrefixSettings(BaseModel):
model_config = ConfigDict(extra="ignore")
class AppearanceRouteParam(BaseModel):
"""Runtime query-param descriptor for top-bar shortcut buttons.
The frontend resolves login params from ``userInfo`` and theme params from
the current UI theme, matching the light-app center's link builder.
"""
key: str = Field(default="", description="Query parameter name appended to the shortcut URL")
paramType: Literal["login", "theme"] = Field(default="login", description="Where the parameter value comes from")
source: str | None = Field(default=None, description="login param source field in userInfo, or 'other'")
customValue: str | None = Field(default=None, description="Literal value when source is 'other'")
themeValue: str | None = Field(default=None, description="Theme-value mapping string, e.g. red=red;dark-blue=dark")
model_config = ConfigDict(extra="ignore")
def _default_shortcut_route_params() -> list[AppearanceRouteParam]:
return [
AppearanceRouteParam(
key="access_token",
paramType="login",
source="access_token",
customValue="",
themeValue="red=red;blue=blue;dark-blue=dark-blue",
)
]
class CompactShortcutButton(BaseModel):
"""Configurable shortcut shown in top bars before the theme switcher."""
id: str = Field(default_factory=lambda: uuid4().hex, description="Stable row id for editing")
label: str = Field(default="快捷入口", description="Button text shown next to the icon")
url: str = Field(default="", description="Target URL opened in a new tab")
route_params: list[AppearanceRouteParam] = Field(
default_factory=_default_shortcut_route_params,
description="Query parameters resolved at launch time and appended to url",
)
enabled: bool = Field(default=True, description="Whether this shortcut is rendered")
sort_order: int = Field(default=0, description="Display order in top bars")
model_config = ConfigDict(extra="ignore")
def _default_compact_shortcut_buttons() -> list[CompactShortcutButton]:
return [
CompactShortcutButton(
id="default-compact-shortcut",
label="快捷入口",
url="",
enabled=True,
sort_order=0,
)
]
class AppearanceSettings(BaseModel):
"""System-wide appearance defaults that any *new* user inherits.
@@ -63,6 +118,10 @@ class AppearanceSettings(BaseModel):
default="full",
description="Default interface layout for users who haven't picked their own — full (sidebar + header) / compact (hidden)",
)
compact_shortcut_buttons: list[CompactShortcutButton] = Field(
default_factory=_default_compact_shortcut_buttons,
description="Shortcut buttons rendered before the theme switcher in top bars",
)
model_config = ConfigDict(extra="ignore")