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:
@@ -2,6 +2,9 @@
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import api from '@/api/index'
|
||||
import { useThemeStore, themeLabels, type Theme } from '@/stores/theme'
|
||||
|
||||
const themeStore = useThemeStore()
|
||||
|
||||
const managers = ref<any[]>([])
|
||||
const selectedUserIds = ref<string[]>([])
|
||||
@@ -107,6 +110,24 @@ async function handleImportPreview() {
|
||||
<div class="page-rule"></div>
|
||||
</div>
|
||||
|
||||
<!-- Theme -->
|
||||
<el-card class="setting-card">
|
||||
<template #header>
|
||||
<div class="card-header-title">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right:6px; color: var(--gold)">
|
||||
<circle cx="12" cy="12" r="5"></circle><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"></path>
|
||||
</svg>
|
||||
界面主题
|
||||
</div>
|
||||
</template>
|
||||
<div class="theme-options">
|
||||
<label v-for="t in (['editorial', 'light'] as Theme[])" :key="t" class="theme-opt" :class="{ 'theme-opt--active': themeStore.currentTheme === t }" @click="themeStore.setTheme(t)">
|
||||
<span class="theme-opt-radio">{{ themeStore.currentTheme === t ? '●' : '○' }}</span>
|
||||
<span class="theme-opt-label">{{ themeLabels[t] }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- Manual Remind -->
|
||||
<el-card class="setting-card">
|
||||
<template #header>
|
||||
@@ -206,7 +227,7 @@ async function handleImportPreview() {
|
||||
<el-button type="success" :loading="importLoading" @click="handleImportPreview" :disabled="!importFile">预览并导入</el-button>
|
||||
</el-form>
|
||||
<div v-if="importPreview" style="margin-top:16px">
|
||||
<h4 style="font-family: 'ZCOOL XiaoWei', STSong, serif; color: var(--ink)">文件预览</h4>
|
||||
<h4 style="font-family: var(--font-heading); color: var(--ink)">文件预览</h4>
|
||||
<el-table :data="Object.entries(importPreview)" stripe>
|
||||
<el-table-column prop="0" label="Sheet" />
|
||||
<el-table-column prop="1.row_count" label="数据行数" />
|
||||
@@ -249,7 +270,7 @@ async function handleImportPreview() {
|
||||
.page-head { margin-bottom: 24px; }
|
||||
.page-title {
|
||||
margin: 0;
|
||||
font-family: 'ZCOOL XiaoWei', STSong, serif;
|
||||
font-family: var(--font-heading);
|
||||
font-size: 22px; font-weight: 400;
|
||||
color: var(--ink); letter-spacing: 0.06em;
|
||||
}
|
||||
@@ -258,10 +279,18 @@ async function handleImportPreview() {
|
||||
.setting-card { margin-bottom: 16px; }
|
||||
.card-header-title {
|
||||
display: flex; align-items: center;
|
||||
font-family: 'ZCOOL XiaoWei', STSong, serif;
|
||||
font-family: var(--font-heading);
|
||||
font-size: 15px; color: var(--ink); letter-spacing: 0.05em;
|
||||
}
|
||||
.setting-row { display: flex; gap: 32px; align-items: flex-start; flex-wrap: wrap; }
|
||||
.setting-col { display: flex; flex-direction: column; gap: 8px; }
|
||||
.setting-label { font-family: 'Noto Serif SC', STSong, serif; font-size: 13px; color: var(--ink); }
|
||||
.setting-label { font-family: var(--font-body); font-size: 13px; color: var(--ink); }
|
||||
|
||||
/* ── Theme Selector ── */
|
||||
.theme-options { display: flex; gap: 16px; }
|
||||
.theme-opt { display: flex; align-items: center; gap: 8px; cursor: pointer; padding: 8px 16px; border: 1px solid var(--warm-border); transition: all var(--transition); }
|
||||
.theme-opt:hover { border-color: var(--ink); }
|
||||
.theme-opt--active { border-color: var(--ink); background: var(--c-primary-bg); }
|
||||
.theme-opt-radio { font-size: 14px; color: var(--ink); }
|
||||
.theme-opt-label { font-family: var(--font-body); font-size: 14px; color: var(--c-text); }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user