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:
@@ -0,0 +1,55 @@
|
||||
import { reactive } from 'vue'
|
||||
|
||||
// 12-color palettes — wide hue spacing for max distinction
|
||||
const COLORS_DARK = ['#C62828','#D84315','#EF6C00','#B8860B','#558B2F','#00897B','#0277BD','#C2185B','#4E342E','#00695C','#5D4037','#2E7D32']
|
||||
const COLORS_LIGHT = ['#FB7185','#FB923C','#FBBF24','#A3E635','#34D399','#2DD4BF','#38BDF8','#F472B6','#F97316','#84CC16','#22D3EE','#FDA4AF']
|
||||
|
||||
// Reactive name→color overrides (populated from backend user.color)
|
||||
export const managerColorOverrides: Record<string, string> = reactive({})
|
||||
let loaded = false
|
||||
|
||||
export async function loadManagerColors() {
|
||||
if (loaded) return
|
||||
try {
|
||||
const { default: api } = await import('@/api/index')
|
||||
const res = await api.get('/users/')
|
||||
for (const u of res.data) {
|
||||
if (u.color) managerColorOverrides[u.name] = u.color
|
||||
}
|
||||
loaded = true
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
export function setManagerColorOverride(name: string, color: string | null) {
|
||||
if (color) {
|
||||
managerColorOverrides[name] = color
|
||||
} else {
|
||||
delete managerColorOverrides[name]
|
||||
}
|
||||
}
|
||||
|
||||
export function getManagerColor(name: string, isLight: boolean): string {
|
||||
if (!name) return '#909399'
|
||||
// Stored override takes priority
|
||||
if (managerColorOverrides[name]) return managerColorOverrides[name]
|
||||
// Fallback: deterministic hash from name
|
||||
let h = 0
|
||||
for (let i = 0; i < name.length; i++) h = name.charCodeAt(i) + ((h << 5) - h)
|
||||
const colors = isLight ? COLORS_LIGHT : COLORS_DARK
|
||||
return colors[Math.abs(h) % colors.length]
|
||||
}
|
||||
|
||||
/** Return white or dark text color based on background luminance */
|
||||
export function getMgrTextColor(bgHex: string): string {
|
||||
const r = parseInt(bgHex.slice(1, 3), 16)
|
||||
const g = parseInt(bgHex.slice(3, 5), 16)
|
||||
const b = parseInt(bgHex.slice(5, 7), 16)
|
||||
const lum = 0.299 * r + 0.587 * g + 0.114 * b
|
||||
return lum > 150 ? '#1A1A1A' : '#FFFFFF'
|
||||
}
|
||||
|
||||
/** Return { background, color } style object for a manager tag */
|
||||
export function getMgrStyle(name: string, isLight: boolean) {
|
||||
const bg = getManagerColor(name, isLight)
|
||||
return { background: bg, color: getMgrTextColor(bg) }
|
||||
}
|
||||
Reference in New Issue
Block a user