feat: 双主题系统 — 编辑风 + 极简亮色

前端:
- 新增 Pinia theme store,localStorage 持久化,data-theme 切换
- App.vue: :root 新增 --font-*/--sidebar-* CSS 变量;新增 :root[data-theme=light] 完整配色(极简黑白金 + Noto Sans SC)
- DesktopLayout: 侧边栏 15 个变量化 + 顶栏主题切换按钮
- MobileLayout: 装饰色变量化
- Settings: 新增「界面主题」卡片
- 21 个组件 font-family 统一替换为 var(--font-*)
- 新增 utils/managerColor.ts — 12 色调色板 + 自适应文字色 + 后端自定义色优先
- WorkPlans/MiniBusiness/KeyVisits/CustomerManage: 客户经理标签色统一走共享工具
- UserManage: 编辑弹窗新增颜色选择器(仅 manager)
- LightBoard: 去标题星标图标
- ManagerWorkspace/WorkPlans/MiniBusiness/KeyVisits: 删除按钮改为实心红底白字

后端:
- User 模型新增 color 列 + lifespan 自动迁移
- users API 支持 color 字段读写
- UserOut schema 新增 color

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-06 16:03:13 +08:00
parent 336b60b3af
commit deeafe239f
37 changed files with 2275 additions and 218 deletions
+4
View File
@@ -16,6 +16,7 @@ class UpdateUserRoleRequest(BaseModel):
role: str # manager / director / leader
department: str = ""
require_report: bool = True
color: Optional[str] = None # hex color for manager tags
@router.get("/", response_model=list[UserOut])
@@ -73,6 +74,8 @@ async def update_user_role(
if data.department:
user.department = data.department
user.require_report = data.require_report
if data.color is not None:
user.color = data.color
await db.commit()
await db.refresh(user)
@@ -82,6 +85,7 @@ async def update_user_role(
"role": user.role,
"department": user.department,
"require_report": user.require_report,
"color": user.color,
}
+4
View File
@@ -45,6 +45,10 @@ async def lifespan(app: FastAPI):
await conn.run_sync(lambda c: c.exec_driver_sql(
"ALTER TABLE visits ADD COLUMN IF NOT EXISTS companion_names TEXT[] DEFAULT '{}'"
))
# v0.6 — manager color tag
await conn.run_sync(lambda c: c.exec_driver_sql(
"ALTER TABLE users ADD COLUMN IF NOT EXISTS color VARCHAR(7)"
))
# system_config table for v0.5
await conn.run_sync(lambda c: c.exec_driver_sql(
"CREATE TABLE IF NOT EXISTS system_config (key VARCHAR(64) PRIMARY KEY, value TEXT DEFAULT '')"
+1
View File
@@ -16,4 +16,5 @@ class User(Base):
department: Mapped[str] = mapped_column(String(100), default="")
wecom_userid: Mapped[str | None] = mapped_column(String(100), unique=True, nullable=True)
require_report: Mapped[bool] = mapped_column(default=True, server_default="true")
color: Mapped[str | None] = mapped_column(String(7), nullable=True) # e.g. "#C62828"
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
+1
View File
@@ -20,6 +20,7 @@ class UserOut(BaseModel):
department: str
wecom_userid: Optional[str] = None
require_report: bool = True
color: Optional[str] = None
model_config = {"from_attributes": True}