分类之前只能新增,建错了只能干看着。补上三件事: - knowledge_category 加 color 列(迁移 0009,CHECK 卡 #RRGGBB), PATCH 支持改名与改色,空串表示取消上色;知识列表带出分类色,前端不用再查。 - 分类行悬停出「⋯」:改名、选色板/手填色值、建子分类、删除(有知识时确认后 force)。 - 选中分类改成主色浅底 + 描边 + 色点,列表头显示当前筛选;切分类时列表淡出并转圈, 不再一动不动等请求回来。 顺带给卡片画分类色上边线:扫一眼就能分出哪几条属于同一类。
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from typing import Annotated, Any
|
||||
@@ -73,6 +74,8 @@ class CategoryIn(BaseModel):
|
||||
name: str = Field(min_length=1, max_length=40)
|
||||
parent_id: uuid.UUID | None = None
|
||||
sort_order: int = 0
|
||||
# 分类色,卡片上边线用它。不传就不上色(前端按 id 稳定地挑一个默认色)。
|
||||
color: str | None = Field(default=None, max_length=7)
|
||||
|
||||
|
||||
class CategoryPatch(BaseModel):
|
||||
@@ -80,6 +83,8 @@ class CategoryPatch(BaseModel):
|
||||
parent_id: uuid.UUID | None = None
|
||||
move: bool = False
|
||||
sort_order: int | None = None
|
||||
# 不传 = 不改;传空串 = 清成默认色(分类色是“可选项”,得能取消)。
|
||||
color: str | None = Field(default=None, max_length=7)
|
||||
version: int
|
||||
|
||||
|
||||
@@ -147,6 +152,29 @@ def _doc_out(doc: KnowledgeDoc) -> dict[str, Any]:
|
||||
|
||||
|
||||
# ── 分类 ────────────────────────────────────────────────────────────────
|
||||
_COLOR_RE = re.compile(r"^#[0-9A-Fa-f]{6}$")
|
||||
|
||||
|
||||
def _norm_color(value: str | None) -> str | None:
|
||||
"""分类色统一成 ``#RRGGBB`` 大写;空串表示“不要颜色”。
|
||||
|
||||
不在这里做“看得见就好”的宽松处理:颜色是直接拼进 CSS 的,宁可当场报错,
|
||||
也不要让一个 ``red`` 或带 alpha 的值进了库、在前端静默变成透明边框。
|
||||
"""
|
||||
if value is None:
|
||||
return None
|
||||
raw = value.strip()
|
||||
if raw == "":
|
||||
return None
|
||||
if not _COLOR_RE.match(raw):
|
||||
raise AppError(
|
||||
ErrorCode.VALIDATION_FAILED,
|
||||
"分类颜色要用 #RRGGBB 格式,例如 #4F46E5",
|
||||
details={"value": value},
|
||||
)
|
||||
return raw.upper()
|
||||
|
||||
|
||||
async def _categories(
|
||||
session: AsyncSession, ws_id: uuid.UUID
|
||||
) -> dict[uuid.UUID, KnowledgeCategory]:
|
||||
@@ -175,6 +203,7 @@ def _category_payload(
|
||||
"name": cat.name,
|
||||
"depth": cat.depth,
|
||||
"sort_order": cat.sort_order,
|
||||
"color": cat.color,
|
||||
"version": cat.version,
|
||||
"path": names,
|
||||
"full_path": " / ".join(names),
|
||||
@@ -412,6 +441,7 @@ async def create_category(body: CategoryIn, ctx: MemberDep, session: SessionDep)
|
||||
name=name,
|
||||
depth=depth,
|
||||
sort_order=body.sort_order,
|
||||
color=_norm_color(body.color),
|
||||
)
|
||||
session.add(cat)
|
||||
await session.flush()
|
||||
@@ -472,6 +502,9 @@ async def patch_category(
|
||||
values.update(parent_id=new_parent_id, depth=new_depth)
|
||||
if body.sort_order is not None:
|
||||
values["sort_order"] = body.sort_order
|
||||
# 只传了 color 才算改色;没传保持原样,传空串等于取消上色。
|
||||
if body.color is not None:
|
||||
values["color"] = _norm_color(body.color)
|
||||
|
||||
result = await session.execute(
|
||||
update(KnowledgeCategory)
|
||||
|
||||
Reference in New Issue
Block a user