From 7dc7888869ec8cc3b2956ab4f470b71aa938529f Mon Sep 17 00:00:00 2001 From: Jason Date: Mon, 4 May 2026 12:38:14 +0800 Subject: [PATCH] refactor(ui): ChatGPT-style visual overhaul for app shell and analytics pages --- src/App.scss | 98 +---- src/App.tsx | 26 +- src/components/ChatAnalysisHeader.scss | 51 +-- src/components/Sidebar.scss | 340 ++++++++-------- src/components/TitleBar.scss | 48 +-- src/pages/AnalyticsPage.scss | 162 ++++---- src/pages/AnalyticsWelcomePage.scss | 202 +++++----- src/pages/AnalyticsWelcomePage.tsx | 44 +-- src/pages/ChatAnalyticsHubPage.scss | 156 ++++---- src/pages/ChatAnalyticsHubPage.tsx | 53 ++- src/pages/HomePage.scss | 148 ++++--- src/pages/HomePage.tsx | 42 +- src/styles/main.scss | 515 ++++++------------------- 13 files changed, 756 insertions(+), 1129 deletions(-) diff --git a/src/App.scss b/src/App.scss index 5cffbbd..4c73248 100644 --- a/src/App.scss +++ b/src/App.scss @@ -3,56 +3,15 @@ display: flex; flex-direction: column; background: var(--bg-primary); - animation: appFadeIn 0.35s ease-out; position: relative; overflow: hidden; } -// 繁花如梦:底色层(::before)+ 光晕层(::after)分离,避免 blur 吃掉边缘 -[data-theme="blossom-dream"] .app-container { - background: transparent; -} - -// ::before 纯底色,不模糊 -[data-theme="blossom-dream"] .app-container::before { - content: ''; - position: absolute; - inset: 0; - pointer-events: none; - z-index: -2; - background: var(--bg-primary); -} - -// ::after 光晕层,模糊叠加在底色上 -[data-theme="blossom-dream"] .app-container::after { - content: ''; - position: absolute; - inset: 0; - pointer-events: none; - z-index: -1; - background: - radial-gradient(ellipse 55% 45% at 15% 20%, var(--blossom-pink) 0%, transparent 70%), - radial-gradient(ellipse 50% 40% at 85% 75%, var(--blossom-peach) 0%, transparent 65%), - radial-gradient(ellipse 45% 50% at 80% 10%, var(--blossom-blue) 0%, transparent 60%); - filter: blur(80px); - opacity: 0.75; -} - -// 深色模式光晕更克制 -[data-theme="blossom-dream"][data-mode="dark"] .app-container::after { - background: - radial-gradient(ellipse 55% 45% at 15% 20%, var(--blossom-pink) 0%, transparent 70%), - radial-gradient(ellipse 50% 40% at 85% 75%, var(--blossom-purple) 0%, transparent 65%), - radial-gradient(ellipse 45% 50% at 80% 10%, var(--blossom-blue) 0%, transparent 60%); - filter: blur(100px); - opacity: 0.2; -} - .window-drag-region { position: fixed; top: 0; left: 0; - right: 150px; // 预留系统最小化/最大化/关闭按钮区域 + right: 150px; height: 40px; -webkit-app-region: drag; pointer-events: auto; @@ -68,8 +27,9 @@ .content { flex: 1; overflow: auto; - padding: 24px; + padding: 24px 32px; position: relative; + background: var(--bg-primary); } .export-keepalive-page { @@ -84,18 +44,7 @@ display: none; } -@keyframes appFadeIn { - from { - opacity: 0; - transform: translateY(8px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -// 更新提示条 +// ---- Update banner ---- .update-banner { display: flex; align-items: center; @@ -107,7 +56,7 @@ .update-text { flex: 1; - + strong { font-weight: 600; } @@ -124,7 +73,7 @@ color: white; font-size: 13px; cursor: pointer; - transition: background 0.2s; + transition: background 0.15s; &:hover { background: rgba(255, 255, 255, 0.3); @@ -143,7 +92,7 @@ color: white; cursor: pointer; opacity: 0.7; - transition: opacity 0.2s; + transition: opacity 0.15s; &:hover { opacity: 1; @@ -178,29 +127,31 @@ } } -// 用户协议弹窗 +// ---- Agreement modal ---- .agreement-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; - background: rgba(0, 0, 0, 0.6); + background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; z-index: 1000; + backdrop-filter: blur(4px); } .agreement-modal { width: 520px; max-height: 80vh; background: var(--bg-primary); + border: 1px solid var(--border-color); border-radius: 16px; overflow: hidden; display: flex; flex-direction: column; - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); + box-shadow: 0 24px 48px rgba(0, 0, 0, 0.2); } .agreement-header { @@ -241,8 +192,8 @@ margin-bottom: 16px; padding: 12px 14px; border-radius: 10px; - border: 1px solid rgba(255, 160, 0, 0.35); - background: rgba(255, 160, 0, 0.12); + border: 1px solid rgba(245, 158, 11, 0.3); + background: rgba(245, 158, 11, 0.08); color: var(--text-primary); strong { @@ -291,19 +242,6 @@ color: var(--text-secondary); line-height: 1.6; } - - &::-webkit-scrollbar { - width: 6px; - } - - &::-webkit-scrollbar-track { - background: transparent; - } - - &::-webkit-scrollbar-thumb { - background: var(--border-color); - border-radius: 3px; - } } .agreement-footer { @@ -347,21 +285,21 @@ border-radius: 8px; font-size: 14px; cursor: pointer; - transition: background 0.2s; + transition: background 0.15s; &:hover { - background: var(--border-color); + background: var(--bg-hover); } } .btn-primary { background: var(--primary); - color: white; + color: var(--on-primary); border: none; border-radius: 8px; font-size: 14px; cursor: pointer; - transition: opacity 0.2s; + transition: opacity 0.15s; &:disabled { opacity: 0.5; diff --git a/src/App.tsx b/src/App.tsx index 5834978..f544cdf 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -30,7 +30,7 @@ import AccountManagementPage from './pages/AccountManagementPage' import BackupPage from './pages/BackupPage' import { useAppStore } from './stores/appStore' -import { themes, useThemeStore, type ThemeId, type ThemeMode } from './stores/themeStore' +import { useThemeStore, type ThemeMode } from './stores/themeStore' import * as configService from './services/config' import * as cloudControl from './services/cloudControl' import { Download, X, Shield } from 'lucide-react' @@ -74,7 +74,7 @@ function App() { setLocked } = useAppStore() - const { currentTheme, themeMode, setTheme, setThemeMode } = useThemeStore() + const { themeMode, setThemeMode } = useThemeStore() const isAgreementWindow = location.pathname === '/agreement-window' const isOnboardingWindow = location.pathname === '/onboarding-window' const isVideoPlayerWindow = location.pathname === '/video-player-window' @@ -149,12 +149,11 @@ function App() { } }, [isOnboardingWindow, isNotificationWindow, isAnnualReportWindow, isDualReportWindow]) - // 应用主题 + // 应用主题模式 (light / dark / system) useEffect(() => { const mq = window.matchMedia('(prefers-color-scheme: dark)') const applyMode = (mode: ThemeMode, systemDark?: boolean) => { const effectiveMode = mode === 'system' ? (systemDark ?? mq.matches ? 'dark' : 'light') : mode - document.documentElement.setAttribute('data-theme', currentTheme) document.documentElement.setAttribute('data-mode', effectiveMode) } @@ -168,19 +167,13 @@ function App() { } mq.addEventListener('change', handler) return () => mq.removeEventListener('change', handler) - }, [currentTheme, themeMode, isOnboardingWindow, isNotificationWindow, isAnnualReportWindow, isDualReportWindow]) + }, [themeMode, isOnboardingWindow, isNotificationWindow, isAnnualReportWindow, isDualReportWindow]) // 读取已保存的主题设置 useEffect(() => { const loadTheme = async () => { try { - const [savedThemeId, savedThemeMode] = await Promise.all([ - configService.getThemeId(), - configService.getTheme() - ]) - if (savedThemeId && themes.some((theme) => theme.id === savedThemeId)) { - setTheme(savedThemeId as ThemeId) - } + const savedThemeMode = await configService.getTheme() if (savedThemeMode === 'light' || savedThemeMode === 'dark' || savedThemeMode === 'system') { setThemeMode(savedThemeMode) } @@ -191,23 +184,20 @@ function App() { } } loadTheme() - }, [setTheme, setThemeMode]) + }, [setThemeMode]) // 保存主题设置 useEffect(() => { if (!themeHydrated) return const saveTheme = async () => { try { - await Promise.all([ - configService.setThemeId(currentTheme), - configService.setTheme(themeMode) - ]) + await configService.setTheme(themeMode) } catch (e) { console.error('保存主题配置失败:', e) } } saveTheme() - }, [currentTheme, themeMode, themeHydrated]) + }, [themeMode, themeHydrated]) // 检查是否已同意协议 useEffect(() => { diff --git a/src/components/ChatAnalysisHeader.scss b/src/components/ChatAnalysisHeader.scss index 4e5e99f..f920d6d 100644 --- a/src/components/ChatAnalysisHeader.scss +++ b/src/components/ChatAnalysisHeader.scss @@ -4,28 +4,29 @@ align-items: center; justify-content: space-between; gap: 16px; - min-height: 28px; + min-height: 32px; padding: 4px 0; background: transparent; border: none; - border-radius: 0; flex-shrink: 0; } .chat-analysis-back { display: inline-flex; align-items: center; - gap: 6px; - padding: 0; + gap: 4px; + padding: 4px 8px 4px 4px; border: none; + border-radius: 6px; background: transparent; - color: var(--text-secondary); + color: var(--text-tertiary); cursor: pointer; - transition: color 0.2s ease; + transition: background 0.15s ease, color 0.15s ease; font-size: 13px; - font-weight: 600; + font-weight: 500; &:hover { + background: var(--bg-hover); color: var(--text-primary); } } @@ -33,12 +34,13 @@ .chat-analysis-breadcrumb { display: flex; align-items: center; - gap: 8px; + gap: 4px; font-size: 13px; - color: var(--text-secondary); + color: var(--text-tertiary); .chat-analysis-breadcrumb-separator { - opacity: 0.6; + opacity: 0.5; + font-size: 12px; } } @@ -49,25 +51,27 @@ .chat-analysis-current-trigger { display: inline-flex; align-items: center; - gap: 6px; - padding: 0; + gap: 4px; + padding: 4px 8px; border: none; + border-radius: 6px; background: transparent; - color: var(--text-secondary); + color: var(--text-tertiary); cursor: pointer; font-size: 13px; font-weight: 600; - transition: color 0.2s ease; + transition: background 0.15s ease, color 0.15s ease; .current { color: var(--text-primary); } svg { - transition: transform 0.2s ease; + transition: transform 0.15s ease; } &:hover { + background: var(--bg-hover); color: var(--text-primary); } @@ -78,34 +82,33 @@ .chat-analysis-menu { position: absolute; - top: calc(100% + 10px); + top: calc(100% + 6px); right: 0; min-width: 120px; - padding: 6px; - background: var(--card-bg); + padding: 4px; + background: var(--bg-secondary-solid, var(--bg-secondary)); border: 1px solid var(--border-color); - border-radius: 12px; - box-shadow: 0 12px 28px rgba(0, 0, 0, 0.12); + border-radius: 10px; + box-shadow: var(--shadow-md); z-index: 20; } .chat-analysis-menu-item { width: 100%; display: block; - padding: 9px 12px; + padding: 8px 12px; border: none; - border-radius: 8px; + border-radius: 6px; background: transparent; color: var(--text-primary); text-align: left; cursor: pointer; font-size: 13px; font-weight: 500; - transition: background 0.2s ease, color 0.2s ease; + transition: background 0.15s ease; &:hover { background: var(--bg-hover); - color: var(--primary); } } diff --git a/src/components/Sidebar.scss b/src/components/Sidebar.scss index 5f153ee..d082bd4 100644 --- a/src/components/Sidebar.scss +++ b/src/components/Sidebar.scss @@ -1,14 +1,16 @@ +// ChatGPT-style sidebar .sidebar { - width: 220px; - background: var(--bg-secondary); - border-right: 1px solid var(--border-color); + width: var(--sidebar-width, 260px); + background: var(--bg-sidebar, var(--bg-secondary)); display: flex; flex-direction: column; - padding: 16px 0; - transition: width 0.25s ease; + padding: 8px 0; + transition: width 0.2s ease; + flex-shrink: 0; + overflow: hidden; &.collapsed { - width: 64px; + width: 68px; .sidebar-user-card-wrap { margin: 0 8px 8px; @@ -40,156 +42,15 @@ } } -.sidebar-user-card-wrap { - position: relative; - margin: 0 12px 10px; - --sidebar-user-menu-width: 172px; -} - -.sidebar-user-menu { - position: absolute; - left: 0; - right: auto; - bottom: calc(100% + 8px); - width: max(100%, var(--sidebar-user-menu-width)); - z-index: 12; - border: 1px solid var(--border-color); - border-radius: 12px; - background: var(--bg-secondary-solid, var(--bg-primary)); - display: flex; - flex-direction: column; - gap: 4px; - padding: 6px; - box-shadow: 0 8px 20px rgba(15, 23, 42, 0.12); - opacity: 0; - transform: translateY(8px) scale(0.95); - pointer-events: none; - transition: opacity 0.2s ease, transform 0.2s ease; - - &.open { - opacity: 1; - transform: translateY(0) scale(1); - pointer-events: auto; - } -} - -.sidebar-user-menu-item { - width: 100%; - border: none; - border-radius: 10px; - background: transparent; - color: var(--text-primary); - padding: 9px 10px; - display: flex; - align-items: center; - gap: 8px; - font-size: 13px; - font-weight: 500; - cursor: pointer; - text-align: left; - transition: background 0.2s ease, color 0.2s ease; - - &:hover { - background: var(--bg-tertiary); - } - - &.danger { - color: #d93025; - - &:hover { - background: rgba(255, 59, 48, 0.08); - } - } -} - -.sidebar-user-card { - width: 100%; - padding: 10px; - border: 1px solid var(--border-color); - border-radius: 12px; - background: var(--bg-secondary); - display: flex; - align-items: center; - gap: 10px; - min-height: 56px; - cursor: pointer; - transition: border-color 0.2s ease, background 0.2s ease, box-shadow 0.2s ease; - - &:hover { - border-color: rgba(99, 102, 241, 0.32); - background: var(--bg-tertiary); - } - - &.menu-open { - border-color: rgba(99, 102, 241, 0.44); - box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.12); - } - - .user-avatar { - width: 36px; - height: 36px; - border-radius: 10px; - overflow: hidden; - background: linear-gradient(135deg, var(--primary), var(--primary-hover)); - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - - img { - width: 100%; - height: 100%; - object-fit: cover; - } - - span { - color: var(--on-primary); - font-size: 14px; - font-weight: 600; - } - } - - .user-meta { - min-width: 0; - flex: 1; - } - - .user-name { - font-size: 13px; - color: var(--text-primary); - font-weight: 600; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .user-wxid { - margin-top: 2px; - font-size: 11px; - color: var(--text-tertiary); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .user-menu-caret { - color: var(--text-tertiary); - display: inline-flex; - transition: transform 0.2s ease, color 0.2s ease; - - &.open { - transform: rotate(180deg); - color: var(--text-secondary); - } - } -} - +// ---- Navigation ---- .nav-menu { flex: 1; display: flex; flex-direction: column; - gap: 4px; + gap: 2px; padding: 0 12px; + overflow-y: auto; + overflow-x: hidden; } .nav-item { @@ -197,24 +58,26 @@ align-items: center; gap: 12px; padding: 10px 12px; - border-radius: 9999px; + border-radius: 8px; color: var(--text-secondary); text-decoration: none; - transition: all 0.2s ease; + transition: background 0.15s ease, color 0.15s ease; white-space: nowrap; border: none; background: transparent; cursor: pointer; font-family: inherit; + font-size: 14px; &:hover { - background: var(--bg-tertiary); + background: var(--bg-hover); color: var(--text-primary); } &.active { - background: var(--primary); - color: var(--on-primary); + background: var(--bg-hover); + color: var(--text-primary); + font-weight: 600; } } @@ -242,7 +105,7 @@ height: 20px; border-radius: 999px; padding: 0 6px; - background: #ff3b30; + background: #ef4444; color: #ffffff; font-size: 11px; font-weight: 700; @@ -250,7 +113,6 @@ align-items: center; justify-content: center; line-height: 1; - box-shadow: 0 0 0 2px rgba(255, 59, 48, 0.18); } .nav-badge.icon-badge { @@ -262,42 +124,158 @@ height: 16px; padding: 0 4px; font-size: 10px; - box-shadow: 0 0 0 2px var(--bg-secondary); + box-shadow: 0 0 0 2px var(--bg-sidebar, var(--bg-secondary)); } +// ---- Footer ---- .sidebar-footer { padding: 0 12px; border-top: 1px solid var(--border-color); - padding-top: 12px; - margin-top: 8px; + padding-top: 8px; + margin-top: 4px; display: flex; flex-direction: column; - gap: 4px; + gap: 2px; } -// 繁花如梦主题:侧边栏毛玻璃 + 激活项用主品牌色 -[data-theme="blossom-dream"] .sidebar { - background: rgba(255, 255, 255, 0.6); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - border-right: 1px solid rgba(255, 255, 255, 0.4); +// ---- User card ---- +.sidebar-user-card-wrap { + position: relative; + margin: 0 12px 8px; + --sidebar-user-menu-width: 172px; } -[data-theme="blossom-dream"][data-mode="dark"] .sidebar { - background: rgba(34, 30, 36, 0.75); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - border-right: 1px solid rgba(255, 255, 255, 0.06); +.sidebar-user-menu { + position: absolute; + left: 0; + right: auto; + bottom: calc(100% + 8px); + width: max(100%, var(--sidebar-user-menu-width)); + z-index: 12; + border: 1px solid var(--border-color); + border-radius: 10px; + background: var(--bg-secondary-solid, var(--bg-secondary)); + display: flex; + flex-direction: column; + gap: 2px; + padding: 4px; + box-shadow: var(--shadow-md); + opacity: 0; + transform: translateY(6px) scale(0.97); + pointer-events: none; + transition: opacity 0.15s ease, transform 0.15s ease; + + &.open { + opacity: 1; + transform: translateY(0) scale(1); + pointer-events: auto; + } } -// 激活项:主品牌色纵向微渐变 -[data-theme="blossom-dream"] .nav-item.active { - background: linear-gradient(180deg, #D4849A 0%, #C4748A 100%); +.sidebar-user-menu-item { + width: 100%; + border: none; + border-radius: 8px; + background: transparent; + color: var(--text-primary); + padding: 8px 10px; + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + font-weight: 500; + cursor: pointer; + text-align: left; + transition: background 0.15s ease; + + &:hover { + background: var(--bg-hover); + } + + &.danger { + color: #ef4444; + + &:hover { + background: rgba(239, 68, 68, 0.08); + } + } } -// 深色激活项:用藕粉色,背景深灰底 + 粉色文字/图标(高阶玩法) -[data-theme="blossom-dream"][data-mode="dark"] .nav-item.active { - background: rgba(209, 158, 187, 0.15); - color: #D19EBB; - border: 1px solid rgba(209, 158, 187, 0.2); +.sidebar-user-card { + width: 100%; + padding: 10px; + border-radius: 10px; + background: transparent; + display: flex; + align-items: center; + gap: 10px; + min-height: 52px; + cursor: pointer; + border: none; + transition: background 0.15s ease; + + &:hover { + background: var(--bg-hover); + } + + &.menu-open { + background: var(--bg-hover); + } + + .user-avatar { + width: 32px; + height: 32px; + border-radius: 50%; + overflow: hidden; + background: var(--primary); + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + + img { + width: 100%; + height: 100%; + object-fit: cover; + } + + span { + color: var(--on-primary); + font-size: 13px; + font-weight: 600; + } + } + + .user-meta { + min-width: 0; + flex: 1; + } + + .user-name { + font-size: 13px; + color: var(--text-primary); + font-weight: 600; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .user-wxid { + margin-top: 1px; + font-size: 11px; + color: var(--text-tertiary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .user-menu-caret { + color: var(--text-tertiary); + display: inline-flex; + transition: transform 0.15s ease; + + &.open { + transform: rotate(180deg); + } + } } diff --git a/src/components/TitleBar.scss b/src/components/TitleBar.scss index 8c3c9b8..50ebb61 100644 --- a/src/components/TitleBar.scss +++ b/src/components/TitleBar.scss @@ -1,12 +1,11 @@ .title-bar { - height: 41px; - background: var(--bg-secondary); + height: 48px; + background: transparent; display: flex; align-items: center; justify-content: space-between; padding-left: 16px; - padding-right: 16px; - border-bottom: 1px solid var(--border-color); + padding-right: 8px; -webkit-app-region: drag; flex-shrink: 0; gap: 8px; @@ -14,12 +13,6 @@ z-index: 2101; } -// 繁花如梦:标题栏毛玻璃 -[data-theme="blossom-dream"] .title-bar { - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); -} - .title-brand { display: inline-flex; align-items: center; @@ -33,16 +26,15 @@ } .titles { - font-size: 15px; - font-weight: 500; + font-size: 14px; + font-weight: 600; color: var(--text-secondary); - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + letter-spacing: -0.01em; } .title-sidebar-toggle { - width: 28px; - height: 28px; + width: 32px; + height: 32px; padding: 0; border: none; border-radius: 8px; @@ -52,11 +44,11 @@ align-items: center; justify-content: center; cursor: pointer; - transition: background 0.2s ease, color 0.2s ease; + transition: background 0.15s ease, color 0.15s ease; -webkit-app-region: no-drag; &:hover { - background: var(--bg-tertiary); + background: var(--bg-hover); color: var(--text-primary); } } @@ -64,26 +56,26 @@ .title-window-controls { display: inline-flex; align-items: center; - gap: 6px; + gap: 2px; -webkit-app-region: no-drag; } .title-window-control-btn { - width: 28px; + width: 36px; height: 28px; padding: 0; border: none; - border-radius: 8px; + border-radius: 6px; background: transparent; color: var(--text-tertiary); display: inline-flex; align-items: center; justify-content: center; cursor: pointer; - transition: background 0.2s ease, color 0.2s ease; + transition: background 0.15s ease, color 0.15s ease; &:hover { - background: var(--bg-tertiary); + background: var(--bg-hover); color: var(--text-primary); } @@ -107,14 +99,14 @@ color: var(--text-secondary); cursor: pointer; padding: 6px; - border-radius: 4px; + border-radius: 6px; display: flex; align-items: center; justify-content: center; - transition: all 0.2s; + transition: all 0.15s; &:hover { - background: var(--bg-tertiary); + background: var(--bg-hover); color: var(--text-primary); } @@ -124,8 +116,8 @@ } &.live-play-btn.active { - background: rgba(var(--primary-rgb, 76, 132, 255), 0.16); - color: var(--primary, #4c84ff); + background: var(--primary-light); + color: var(--primary); } } diff --git a/src/pages/AnalyticsPage.scss b/src/pages/AnalyticsPage.scss index f905f4d..ae19cf4 100644 --- a/src/pages/AnalyticsPage.scss +++ b/src/pages/AnalyticsPage.scss @@ -10,7 +10,7 @@ } } -// 加载和错误状态 +// Loading and error states .loading-container, .error-container { display: flex; @@ -23,7 +23,7 @@ color: var(--text-secondary); .spin { - animation: spin 1s linear infinite; + animation: analyticsSpin 1s linear infinite; } p.loading-status { @@ -33,13 +33,12 @@ } .progress-bar-wrapper { - width: 300px; - height: 8px; + width: 280px; + height: 4px; background: var(--bg-tertiary); border-radius: 999px; overflow: hidden; position: relative; - border: 1px solid var(--border-color); } .progress-bar-fill { @@ -47,9 +46,9 @@ left: 0; top: 0; height: 100%; - background: var(--primary-gradient); - transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1); - box-shadow: 0 0 10px rgba(139, 115, 85, 0.3); + background: var(--primary); + transition: width 0.3s ease; + border-radius: 999px; } .progress-percent { @@ -65,57 +64,82 @@ } } -@keyframes spin { - from { - transform: rotate(0deg); - } +@keyframes analyticsSpin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} - to { - transform: rotate(360deg); +// Page scroll content +.page-scroll { + display: flex; + flex-direction: column; + gap: 24px; +} + +.page-section { + display: flex; + flex-direction: column; + gap: 16px; +} + +.section-header { + display: flex; + align-items: center; + justify-content: space-between; + + h2 { + display: flex; + align-items: center; + gap: 8px; + font-size: 16px; + font-weight: 600; + color: var(--text-primary); + margin: 0; } } -// 统计卡片 +// Stats overview cards .stats-overview { display: grid; grid-template-columns: repeat(4, 1fr); - gap: 16px; - margin-bottom: 24px; + gap: 12px; } .stat-card { display: flex; align-items: center; - gap: 16px; - padding: 20px; + gap: 14px; + padding: 18px 16px; background: var(--card-bg); border-radius: 12px; border: 1px solid var(--border-color); .stat-icon { - width: 48px; - height: 48px; + width: 40px; + height: 40px; display: flex; align-items: center; justify-content: center; background: var(--primary-light); - border-radius: 12px; + border-radius: 10px; color: var(--primary); + flex-shrink: 0; } .stat-info { display: flex; flex-direction: column; - gap: 4px; + gap: 2px; .stat-value { - font-size: 24px; - font-weight: 600; + font-size: 22px; + font-weight: 700; color: var(--text-primary); + letter-spacing: -0.5px; } .stat-label { - font-size: 13px; + font-size: 12px; color: var(--text-tertiary); } } @@ -125,23 +149,23 @@ display: flex; align-items: center; gap: 8px; - padding: 12px 16px; + padding: 10px 14px; background: var(--bg-tertiary); border-radius: 8px; - margin-bottom: 24px; font-size: 13px; color: var(--text-secondary); svg { color: var(--text-tertiary); + flex-shrink: 0; } } +// Charts .charts-grid { display: grid; grid-template-columns: repeat(2, 1fr); - gap: 16px; - margin-bottom: 24px; + gap: 12px; } .chart-card { @@ -155,30 +179,30 @@ } h3 { - font-size: 15px; - font-weight: 500; + font-size: 14px; + font-weight: 600; color: var(--text-primary); - margin: 0 0 16px; + margin: 0 0 12px; } } +// Rankings .rankings-list { display: flex; flex-direction: column; - gap: 8px; + gap: 4px; } .ranking-item { display: flex; align-items: center; gap: 12px; - padding: 12px 16px; - background: var(--bg-primary); + padding: 10px 14px; border-radius: 8px; - transition: background 0.2s; + transition: background 0.15s ease; &:hover { - background: var(--bg-tertiary); + background: var(--bg-hover); } .rank { @@ -196,13 +220,13 @@ &.top { background: var(--primary); - color: white; + color: var(--on-primary); } } .contact-avatar { - width: 40px; - height: 40px; + width: 36px; + height: 36px; flex-shrink: 0; position: relative; @@ -228,8 +252,8 @@ position: absolute; right: -4px; bottom: -4px; - width: 18px; - height: 18px; + width: 16px; + height: 16px; display: flex; align-items: center; justify-content: center; @@ -239,24 +263,21 @@ &.medal-1 { background: linear-gradient(135deg, #ffd700, #ffb800); color: #fff; - box-shadow: 0 2px 4px rgba(255, 184, 0, 0.4); } &.medal-2 { background: linear-gradient(135deg, #c0c0c0, #a8a8a8); color: #fff; - box-shadow: 0 2px 4px rgba(168, 168, 168, 0.4); } &.medal-3 { background: linear-gradient(135deg, #cd7f32, #b87333); color: #fff; - box-shadow: 0 2px 4px rgba(184, 115, 51, 0.4); } svg { - width: 10px; - height: 10px; + width: 8px; + height: 8px; } } } @@ -265,7 +286,7 @@ flex: 1; display: flex; flex-direction: column; - gap: 2px; + gap: 1px; min-width: 0; .contact-name { @@ -284,14 +305,14 @@ } .message-count { - font-size: 14px; - font-weight: 500; + font-size: 13px; + font-weight: 600; color: var(--primary); flex-shrink: 0; } } -// 响应式 +// Responsive @media (max-width: 1200px) { .stats-overview { grid-template-columns: repeat(2, 1fr); @@ -312,11 +333,11 @@ } } -// 排除好友弹窗 +// Exclude friends modal .exclude-modal-overlay { position: fixed; inset: 0; - background: rgba(0, 0, 0, 0.45); + background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; @@ -325,13 +346,13 @@ } .exclude-modal { - width: 560px; + width: 520px; max-width: calc(100vw - 48px); - background: var(--card-bg); + background: var(--bg-secondary-solid, var(--bg-secondary)); border-radius: 16px; border: 1px solid var(--border-color); padding: 20px; - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2); + box-shadow: 0 24px 48px rgba(0, 0, 0, 0.2); .exclude-modal-header { display: flex; @@ -342,6 +363,7 @@ h3 { margin: 0; font-size: 16px; + font-weight: 600; color: var(--text-primary); } } @@ -349,14 +371,14 @@ .modal-close { width: 32px; height: 32px; - border-radius: 50%; + border-radius: 8px; border: none; - background: var(--bg-tertiary); + background: transparent; display: flex; align-items: center; justify-content: center; cursor: pointer; - color: var(--text-secondary); + color: var(--text-tertiary); transition: all 0.15s; &:hover { @@ -370,7 +392,7 @@ align-items: center; gap: 8px; padding: 8px 12px; - border-radius: 10px; + border-radius: 8px; border: 1px solid var(--border-color); background: var(--bg-primary); margin-bottom: 12px; @@ -399,7 +421,7 @@ } .exclude-modal-body { - max-height: 420px; + max-height: 380px; overflow: auto; padding-right: 4px; } @@ -419,7 +441,7 @@ .exclude-list { display: flex; flex-direction: column; - gap: 6px; + gap: 4px; } .exclude-item { @@ -427,23 +449,23 @@ align-items: center; gap: 12px; padding: 8px 10px; - border-radius: 10px; + border-radius: 8px; cursor: pointer; border: 1px solid transparent; transition: all 0.15s; - background: var(--bg-primary); &:hover { - background: var(--bg-tertiary); + background: var(--bg-hover); } &.active { - border-color: rgba(7, 193, 96, 0.4); - background: rgba(7, 193, 96, 0.08); + border-color: rgba(16, 163, 127, 0.3); + background: rgba(16, 163, 127, 0.06); } input { margin: 0; + accent-color: var(--primary); } } @@ -455,7 +477,7 @@ display: flex; flex-direction: column; min-width: 0; - gap: 2px; + gap: 1px; } .exclude-name { @@ -479,7 +501,7 @@ display: flex; align-items: center; justify-content: space-between; - margin-top: 16px; + margin-top: 14px; } .exclude-footer-left { diff --git a/src/pages/AnalyticsWelcomePage.scss b/src/pages/AnalyticsWelcomePage.scss index 0e698bc..4e14705 100644 --- a/src/pages/AnalyticsWelcomePage.scss +++ b/src/pages/AnalyticsWelcomePage.scss @@ -1,146 +1,116 @@ -.analytics-entry-page { +.analytics-welcome-shell { display: flex; flex-direction: column; gap: 16px; min-height: 100%; } -.analytics-welcome-container { +.analytics-welcome-body { display: flex; flex-direction: column; flex: 1; align-items: center; justify-content: center; min-height: 0; - padding: 40px; - background: var(--bg-primary); + padding: 40px 24px; + animation: welcomeFadeIn 0.4s ease-out; +} + +.analytics-welcome-content { + text-align: center; + max-width: 480px; + width: 100%; +} + +.analytics-welcome-icon { + width: 56px; + height: 56px; + margin: 0 auto 20px; + background: var(--primary-light); + border-radius: 14px; + display: flex; + align-items: center; + justify-content: center; + color: var(--primary); +} + +.analytics-welcome-content h1 { + font-size: 24px; + font-weight: 700; + margin: 0 0 10px; color: var(--text-primary); - animation: fadeIn 0.4s ease-out; - overflow-y: auto; + letter-spacing: -0.3px; +} - &.analytics-welcome-container--mode { - border-radius: 20px; - border: 1px solid var(--border-color); - background: - radial-gradient(circle at top, rgba(7, 193, 96, 0.06), transparent 48%), - var(--bg-primary); +.analytics-welcome-content p { + color: var(--text-secondary); + margin: 0 0 32px; + font-size: 14px; + line-height: 1.7; +} + +.analytics-welcome-actions { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; +} + +.analytics-welcome-card { + display: flex; + align-items: center; + gap: 14px; + padding: 16px 18px; + background: var(--card-bg); + border: 1px solid var(--border-color); + border-radius: 12px; + cursor: pointer; + text-align: left; + color: var(--text-secondary); + transition: background 0.15s ease, border-color 0.15s ease; + + &:hover { + background: var(--bg-hover); + border-color: var(--text-tertiary); + color: var(--primary); } - .welcome-content { - text-align: center; - max-width: 600px; - - .icon-wrapper { - width: 80px; - height: 80px; - margin: 0 auto 24px; - background: rgba(7, 193, 96, 0.1); - border-radius: 20px; - display: flex; - align-items: center; - justify-content: center; - color: #07c160; - - svg { - width: 40px; - height: 40px; - } - } - - h1 { - font-size: 28px; - margin-bottom: 12px; - font-weight: 600; - } - - p { - color: var(--text-secondary); - margin-bottom: 40px; - font-size: 16px; - line-height: 1.6; - } - - .action-cards { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 20px; - margin-top: 20px; - - button { - display: flex; - flex-direction: column; - align-items: center; - padding: 30px 20px; - background: var(--bg-secondary); - border: 1px solid var(--border-color); - border-radius: 12px; - cursor: pointer; - transition: all 0.2s ease; - text-align: center; - - &:hover:not(:disabled) { - transform: translateY(-2px); - border-color: #07c160; - box-shadow: 0 4px 12px rgba(7, 193, 96, 0.1); - - .card-icon { - color: #07c160; - background: rgba(7, 193, 96, 0.1); - } - } - - &:disabled { - opacity: 0.6; - cursor: not-allowed; - filter: grayscale(100%); - } - - .card-icon { - width: 50px; - height: 50px; - border-radius: 12px; - background: var(--bg-tertiary); - display: flex; - align-items: center; - justify-content: center; - margin-bottom: 16px; - color: var(--text-secondary); - transition: all 0.2s ease; - } - - h3 { - font-size: 18px; - margin-bottom: 8px; - color: var(--text-primary); - } - - span { - font-size: 13px; - color: var(--text-tertiary); - } - } - } + svg { + flex-shrink: 0; } } -@media (max-width: 768px) { - .analytics-welcome-container { - padding: 28px 18px; +.analytics-welcome-card-text { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} - .welcome-content { - .action-cards { - grid-template-columns: 1fr; - } - } +.analytics-welcome-card-title { + font-size: 14px; + font-weight: 600; + color: var(--text-primary); +} + +.analytics-welcome-card-meta { + font-size: 12px; + color: var(--text-tertiary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +@media (max-width: 540px) { + .analytics-welcome-actions { + grid-template-columns: 1fr; } } -@keyframes fadeIn { +@keyframes welcomeFadeIn { from { opacity: 0; transform: translateY(10px); } - to { opacity: 1; transform: translateY(0); diff --git a/src/pages/AnalyticsWelcomePage.tsx b/src/pages/AnalyticsWelcomePage.tsx index e5344ae..44da825 100644 --- a/src/pages/AnalyticsWelcomePage.tsx +++ b/src/pages/AnalyticsWelcomePage.tsx @@ -6,12 +6,6 @@ import './AnalyticsWelcomePage.scss' function AnalyticsWelcomePage() { const navigate = useNavigate() - // 检查是否有任何缓存数据加载或基本的存储状态表明它已准备好。 - // 实际上,如果 store 没有持久化,`isLoaded` 可能会在应用刷新时重置。 - // 如果用户点击“加载缓存”但缓存为空,AnalyticsPage 的逻辑(loadData 不带 force)将尝试从后端缓存加载。 - // 如果后端缓存也为空,则会重新计算。 - - // 我们也可以检查 `lastLoadTime` 来显示“上次更新:xxx”(如果已持久化)。 const { lastLoadTime } = useAnalyticsStore() const handleLoadCache = () => { @@ -28,35 +22,37 @@ function AnalyticsWelcomePage() { } return ( -
+
-
-
-
- +
+
+
+

私聊数据分析

- WeFlow 可以分析你的好友聊天记录,生成详细的统计报表。
- 你可以选择加载上次的分析结果,或者重新开始一次新的私聊分析。 + 分析你的好友聊天记录,生成详细统计报表。
+ 选择加载上次结果或开始新分析。

-
- -
diff --git a/src/pages/ChatAnalyticsHubPage.scss b/src/pages/ChatAnalyticsHubPage.scss index 4d970cd..f6621a7 100644 --- a/src/pages/ChatAnalyticsHubPage.scss +++ b/src/pages/ChatAnalyticsHubPage.scss @@ -1,4 +1,4 @@ -.chat-analytics-hub-page { +.analytics-hub { min-height: 100%; display: flex; align-items: center; @@ -6,118 +6,128 @@ padding: 40px 24px; } -.chat-analytics-hub-content { - width: min(860px, 100%); +.analytics-hub-inner { + width: min(720px, 100%); display: flex; flex-direction: column; align-items: center; text-align: center; + animation: analyticsHubFadeIn 0.4s ease-out; } -.chat-analytics-hub-badge { - display: inline-flex; - align-items: center; - gap: 8px; - padding: 8px 14px; - border-radius: 999px; - background: var(--primary-light); - color: var(--primary); - font-size: 13px; - font-weight: 600; -} - -.chat-analytics-hub-content h1 { - margin: 20px 0 12px; - font-size: 32px; - line-height: 1.2; +.analytics-hub-title { + font-size: 28px; + font-weight: 700; color: var(--text-primary); + margin: 0 0 12px; + letter-spacing: -0.5px; } -.chat-analytics-hub-desc { - max-width: 620px; - margin: 0 0 32px; +.analytics-hub-desc { + max-width: 520px; + margin: 0 0 36px; color: var(--text-secondary); font-size: 15px; line-height: 1.7; } -.chat-analytics-hub-grid { +.analytics-hub-grid { width: 100%; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 20px; + gap: 16px; } -.chat-analytics-entry-card { +.analytics-hub-card { display: flex; flex-direction: column; align-items: flex-start; text-align: left; - gap: 14px; - min-height: 260px; - padding: 28px; + gap: 16px; + padding: 24px; border: 1px solid var(--border-color); - border-radius: 20px; - background: - linear-gradient(180deg, rgba(7, 193, 96, 0.08) 0%, rgba(7, 193, 96, 0.02) 100%), - var(--card-bg); + border-radius: 12px; + background: var(--card-bg); color: var(--text-primary); cursor: pointer; - transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease; + transition: background 0.15s ease, border-color 0.15s ease; &:hover { - transform: translateY(-4px); - border-color: rgba(7, 193, 96, 0.35); - box-shadow: 0 20px 36px rgba(7, 193, 96, 0.12); - } + background: var(--bg-hover); + border-color: var(--text-tertiary); - .entry-card-icon { - width: 52px; - height: 52px; - border-radius: 16px; - display: flex; - align-items: center; - justify-content: center; - background: rgba(7, 193, 96, 0.12); - color: #07c160; - - &.group { - background: rgba(24, 119, 242, 0.12); - color: #1877f2; + .analytics-hub-card-arrow { + transform: translateX(3px); } } +} - .entry-card-header { - width: 100%; - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; +.analytics-hub-card-icon { + width: 44px; + height: 44px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + background: var(--primary-light); + color: var(--primary); + + &--group { + background: rgba(59, 130, 246, 0.1); + color: #3b82f6; } +} + +[data-mode="dark"] .analytics-hub-card-icon--group { + background: rgba(96, 165, 250, 0.12); + color: #60a5fa; +} + +.analytics-hub-card-body { + display: flex; + flex-direction: column; + gap: 8px; + width: 100%; +} + +.analytics-hub-card-header { + display: flex; + align-items: center; + justify-content: space-between; h2 { margin: 0; - font-size: 24px; - line-height: 1.2; - } - - p { - margin: 0; - color: var(--text-secondary); - font-size: 14px; - line-height: 1.7; - } - - .entry-card-cta { - margin-top: auto; - color: var(--primary); - font-size: 13px; + font-size: 18px; font-weight: 600; + line-height: 1.2; } } -@media (max-width: 900px) { - .chat-analytics-hub-grid { +.analytics-hub-card-arrow { + color: var(--text-tertiary); + transition: transform 0.15s ease; +} + +.analytics-hub-card-body p { + margin: 0; + color: var(--text-secondary); + font-size: 13px; + line-height: 1.6; +} + +@media (max-width: 640px) { + .analytics-hub-grid { grid-template-columns: 1fr; } } + +@keyframes analyticsHubFadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} diff --git a/src/pages/ChatAnalyticsHubPage.tsx b/src/pages/ChatAnalyticsHubPage.tsx index 6c4456e..3e21c73 100644 --- a/src/pages/ChatAnalyticsHubPage.tsx +++ b/src/pages/ChatAnalyticsHubPage.tsx @@ -1,4 +1,4 @@ -import { ArrowRight, BarChart3, MessageSquare, Users } from 'lucide-react' +import { ArrowRight, MessageSquare, Users } from 'lucide-react' import { useNavigate } from 'react-router-dom' import './ChatAnalyticsHubPage.scss' @@ -6,49 +6,46 @@ function ChatAnalyticsHubPage() { const navigate = useNavigate() return ( -
-
-
- - 聊天分析 -
- -

选择你要进入的分析视角

-

- 私聊分析更适合看好友聊天统计和趋势,群聊分析则用于查看群成员、发言排行和活跃时段。 +

+
+

聊天分析

+

+ 选择你要进入的分析视角。私聊分析适合查看好友聊天统计,群聊分析则用于查看群成员活跃度。

-
+
diff --git a/src/pages/HomePage.scss b/src/pages/HomePage.scss index cd4cb78..40a4ee8 100644 --- a/src/pages/HomePage.scss +++ b/src/pages/HomePage.scss @@ -1,6 +1,5 @@ .home-page { height: 100%; - background: var(--bg-primary); display: flex; align-items: center; justify-content: center; @@ -8,105 +7,94 @@ position: relative; } -.home-bg-blobs { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - filter: blur(80px); - z-index: 0; - opacity: 0.6; - pointer-events: none; -} - -.blob { - position: absolute; - border-radius: 50%; - animation: moveBlob 20s infinite alternate ease-in-out; -} - -.blob-1 { - width: 400px; - height: 400px; - background: rgba(var(--primary-rgb), 0.25); - top: -100px; - left: -50px; - animation-duration: 25s; -} - -.blob-2 { - width: 350px; - height: 350px; - background: rgba(var(--primary-rgb), 0.15); - bottom: -50px; - right: -50px; - animation-duration: 30s; - animation-delay: -5s; -} - -.blob-3 { - width: 300px; - height: 300px; - background: rgba(255, 255, 255, 0.1); - top: 40%; - left: 30%; - animation-duration: 22s; - animation-delay: -10s; -} - -[data-mode="dark"] .blob-3 { - background: rgba(255, 255, 255, 0.03); -} - .home-content { - z-index: 1; - animation: fadeScaleUp 1s cubic-bezier(0.2, 0.8, 0.2, 1); -} - -.hero { text-align: center; + max-width: 640px; + width: 100%; + padding: 0 24px; + animation: homeFadeIn 0.5s ease-out; } -.hero-title { - font-size: 64px; - font-weight: 800; - margin: 0 0 16px; +.home-title { + font-size: 48px; + font-weight: 700; + margin: 0 0 12px; color: var(--text-primary); - letter-spacing: -2px; - background: linear-gradient(135deg, var(--primary) 0%, rgba(var(--primary-rgb), 0.6) 100%); - background-clip: text; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; + letter-spacing: -1.5px; } -.hero-subtitle { - font-size: 18px; +.home-subtitle { + font-size: 16px; color: var(--text-secondary); - max-width: 520px; - margin: 0 auto; + margin: 0 0 48px; line-height: 1.6; - opacity: 0.8; } -@keyframes moveBlob { - from { - transform: translate(0, 0) scale(1); +.home-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 12px; +} + +.home-feature-card { + display: flex; + align-items: center; + gap: 14px; + padding: 16px 18px; + border: 1px solid var(--border-color); + border-radius: 12px; + background: var(--card-bg); + cursor: pointer; + text-align: left; + transition: background 0.15s ease, border-color 0.15s ease; + color: var(--text-secondary); + + &:hover { + background: var(--bg-hover); + border-color: var(--text-tertiary); + color: var(--text-primary); } - to { - transform: translate(100px, 50px) scale(1.1); + svg { + flex-shrink: 0; } } -@keyframes fadeScaleUp { +.home-feature-text { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} + +.home-feature-label { + font-size: 14px; + font-weight: 600; + color: var(--text-primary); +} + +.home-feature-desc { + font-size: 12px; + color: var(--text-tertiary); +} + +@keyframes homeFadeIn { from { opacity: 0; - transform: scale(0.95) translateY(20px); + transform: translateY(12px); } - to { opacity: 1; - transform: scale(1) translateY(0); + transform: translateY(0); + } +} + +@media (max-width: 480px) { + .home-grid { + grid-template-columns: 1fr; + } + + .home-title { + font-size: 36px; } } \ No newline at end of file diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index edca946..0423bad 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -1,20 +1,40 @@ -import { FolderOpen, ShieldCheck, Sparkles, Waves } from 'lucide-react' -import { useAppStore } from '../stores/appStore' +import { MessageSquare, BarChart3, Download, Aperture, Footprints, FolderClosed } from 'lucide-react' +import { useNavigate } from 'react-router-dom' import './HomePage.scss' function HomePage() { + const navigate = useNavigate() + + const features = [ + { icon: MessageSquare, label: '聊天', desc: '浏览聊天记录', path: '/chat' }, + { icon: Aperture, label: '朋友圈', desc: '查看朋友圈动态', path: '/sns' }, + { icon: BarChart3, label: '聊天分析', desc: '分析聊天统计数据', path: '/analytics' }, + { icon: FolderClosed, label: '资源浏览', desc: '管理媒体文件', path: '/resources' }, + { icon: Footprints, label: '我的足迹', desc: '回顾你的轨迹', path: '/footprint' }, + { icon: Download, label: '导出', desc: '导出聊天记录', path: '/export' }, + ] + return (
-
-
-
-
-
-
-
-

WeFlow

-

每一条消息的背后,都藏着一段温暖的时光

+

WeFlow

+

每一条消息的背后,都藏着一段温暖的时光

+ +
+ {features.map((f) => ( + + ))}
diff --git a/src/styles/main.scss b/src/styles/main.scss index 0de9c35..8b19126 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -1,400 +1,106 @@ -// CSS 变量 - 主题 +// WeFlow — ChatGPT-inspired design token system +// Only light/dark/system modes. No per-theme variants. +// All legacy variable names preserved for backward compatibility. @use './chat-patterns.scss'; -:root { - // 颜色 - --primary: #8B7355; - --primary-hover: #7A6548; - --primary-light: rgba(139, 115, 85, 0.1); - --danger: #dc3545; - --warning: #ffc107; +// ============================================= +// Light mode (default) +// ============================================= +:root, +[data-mode="light"] { + // ---- Accent / Brand ---- + --primary: #10a37f; + --primary-rgb: 16, 163, 127; + --primary-hover: #0d8a6c; + --primary-light: rgba(16, 163, 127, 0.1); + --primary-gradient: linear-gradient(135deg, #10a37f 0%, #1ab893 100%); - // 背景 - --bg-primary: #F0EEE9; - --bg-secondary: rgba(255, 255, 255, 0.7); - --bg-tertiary: rgba(0, 0, 0, 0.03); - --bg-hover: rgba(0, 0, 0, 0.05); + --danger: #ef4444; + --warning: #f59e0b; - // 文字 - --text-primary: #3d3d3d; - --text-secondary: #666666; - --text-tertiary: #999999; - - // 边框 - --border-color: rgba(0, 0, 0, 0.08); - --border-radius: 9999px; - - // 阴影 - --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); - --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08); - - // 侧边栏 - --sidebar-width: 220px; - - // 主题渐变 - --bg-gradient: linear-gradient(135deg, #F0EEE9 0%, #E8E6E1 100%); - --primary-gradient: linear-gradient(135deg, #8B7355 0%, #A68B5B 100%); - - // 卡片背景 - --card-bg: rgba(255, 255, 255, 0.7); - --card-inner-bg: #FAFAF7; - --sent-card-bg: var(--primary); - - // primary 色上方的前景文字色(大多数主题为白色) - --on-primary: white; -} - -// ==================== 浅色主题 ==================== - -// 云上舞白主题 (默认) -[data-theme="cloud-dancer"][data-mode="light"], -[data-theme="cloud-dancer"]:not([data-mode]) { - --primary: #8B7355; - --primary-rgb: 139, 115, 85; - --primary-hover: #7A6548; - --primary-light: rgba(139, 115, 85, 0.1); - --bg-primary: #F0EEE9; - --bg-secondary: rgba(255, 255, 255, 0.7); - --bg-tertiary: rgba(0, 0, 0, 0.03); - --bg-hover: rgba(0, 0, 0, 0.05); - --text-primary: #3d3d3d; - --text-secondary: #666666; - --text-tertiary: #999999; - --border-color: rgba(0, 0, 0, 0.08); - --bg-gradient: linear-gradient(135deg, #F0EEE9 0%, #E8E6E1 100%); - --primary-gradient: linear-gradient(135deg, #8B7355 0%, #A68B5B 100%); - --card-bg: rgba(255, 255, 255, 0.7); - --card-inner-bg: #FAFAF7; - --sent-card-bg: var(--primary); -} - -// 刚玉蓝主题 -[data-theme="corundum-blue"][data-mode="light"], -[data-theme="corundum-blue"]:not([data-mode]) { - --primary: #4A6670; - --primary-rgb: 74, 102, 112; - --primary-hover: #3D565E; - --primary-light: rgba(74, 102, 112, 0.1); - --bg-primary: #E8EEF0; - --bg-secondary: rgba(255, 255, 255, 0.7); - --bg-tertiary: rgba(0, 0, 0, 0.03); - --bg-hover: rgba(0, 0, 0, 0.05); - --text-primary: #3d3d3d; - --text-secondary: #666666; - --text-tertiary: #999999; - --border-color: rgba(0, 0, 0, 0.08); - --bg-gradient: linear-gradient(135deg, #E8EEF0 0%, #D8E4E8 100%); - --primary-gradient: linear-gradient(135deg, #4A6670 0%, #5A7A86 100%); - --card-bg: rgba(255, 255, 255, 0.7); - --card-inner-bg: #F8FAFB; - --sent-card-bg: var(--primary); -} - -// 冰猕猴桃汁绿主题 -[data-theme="kiwi-green"][data-mode="light"], -[data-theme="kiwi-green"]:not([data-mode]) { - --primary: #7A9A5C; - --primary-rgb: 122, 154, 92; - --primary-hover: #6A8A4C; - --primary-light: rgba(122, 154, 92, 0.1); - --bg-primary: #E8F0E4; - --bg-secondary: rgba(255, 255, 255, 0.7); - --bg-tertiary: rgba(0, 0, 0, 0.03); - --bg-hover: rgba(0, 0, 0, 0.05); - --text-primary: #3d3d3d; - --text-secondary: #666666; - --text-tertiary: #999999; - --border-color: rgba(0, 0, 0, 0.08); - --bg-gradient: linear-gradient(135deg, #E8F0E4 0%, #D8E8D0 100%); - --primary-gradient: linear-gradient(135deg, #7A9A5C 0%, #8AAA6C 100%); - --card-bg: rgba(255, 255, 255, 0.7); - --card-inner-bg: #F8FBF6; - --sent-card-bg: var(--primary); -} - -// 辛辣红主题 -[data-theme="spicy-red"][data-mode="light"], -[data-theme="spicy-red"]:not([data-mode]) { - --primary: #8B4049; - --primary-rgb: 139, 64, 73; - --primary-hover: #7A3540; - --primary-light: rgba(139, 64, 73, 0.1); - --bg-primary: #F0E8E8; - --bg-secondary: rgba(255, 255, 255, 0.7); - --bg-tertiary: rgba(0, 0, 0, 0.03); - --bg-hover: rgba(0, 0, 0, 0.05); - --text-primary: #3d3d3d; - --text-secondary: #666666; - --text-tertiary: #999999; - --border-color: rgba(0, 0, 0, 0.08); - --bg-gradient: linear-gradient(135deg, #F0E8E8 0%, #E8D8D8 100%); - --primary-gradient: linear-gradient(135deg, #8B4049 0%, #A05058 100%); - --card-bg: rgba(255, 255, 255, 0.7); - --card-inner-bg: #FAF8F8; - --sent-card-bg: var(--primary); -} - -// 明水鸭色主题 -[data-theme="teal-water"][data-mode="light"], -[data-theme="teal-water"]:not([data-mode]) { - --primary: #5A8A8A; - --primary-rgb: 90, 138, 138; - --primary-hover: #4A7A7A; - --primary-light: rgba(90, 138, 138, 0.1); - --bg-primary: #E4F0F0; - --bg-secondary: rgba(255, 255, 255, 0.7); - --bg-tertiary: rgba(0, 0, 0, 0.03); - --bg-hover: rgba(0, 0, 0, 0.05); - --text-primary: #3d3d3d; - --text-secondary: #666666; - --text-tertiary: #999999; - --border-color: rgba(0, 0, 0, 0.08); - --bg-gradient: linear-gradient(135deg, #E4F0F0 0%, #D4E8E8 100%); - --primary-gradient: linear-gradient(135deg, #5A8A8A 0%, #6A9A9A 100%); - --card-bg: rgba(255, 255, 255, 0.7); - --card-inner-bg: #F6FBFB; - --sent-card-bg: var(--primary); -} - -// 繁花如梦 - 浅色(晨曦花境) -[data-theme="blossom-dream"][data-mode="light"], -[data-theme="blossom-dream"]:not([data-mode]) { - // 三色定义(供伪元素光晕使用,饱和度提高以便在底色上可见) - --blossom-pink: #F0A0B8; - --blossom-peach: #FFB07A; - --blossom-blue: #90B8E0; - - // 主品牌色:Pantone 粉晶 Rose Quartz - --primary: #D4849A; - --primary-rgb: 212, 132, 154; - --primary-hover: #C4748A; - --primary-light: rgba(212, 132, 154, 0.12); - - // 背景三层:主背景最深(相对),面板次之,卡片最白 - --bg-primary: #F5EDF2; - --bg-secondary: rgba(255, 255, 255, 0.82); - --bg-tertiary: rgba(212, 132, 154, 0.06); - --bg-hover: rgba(212, 132, 154, 0.09); - - // 文字:提高对比度,主色接近纯黑只带微弱紫调 - --text-primary: #1E1A22; - --text-secondary: #6B5F70; - --text-tertiary: #9A8A9E; - // 边框:粉色半透明,有存在感但不强硬 - --border-color: rgba(212, 132, 154, 0.18); - - --bg-gradient: linear-gradient(150deg, #F5EDF2 0%, #F0EAF6 50%, #EAF0F8 100%); - --primary-gradient: linear-gradient(135deg, #D4849A 0%, #E8A8B8 100%); - - // 卡片:高不透明度白,与背景形成明显层次 - --card-bg: rgba(255, 255, 255, 0.88); - --card-inner-bg: rgba(255, 255, 255, 0.95); - - --sent-card-bg: var(--primary); -} - -// Geist · 极简黑白 - 浅色 -[data-theme="geist"][data-mode="light"], -[data-theme="geist"]:not([data-mode]) { - --primary: #444444; - --primary-rgb: 68, 68, 68; - --primary-hover: #333333; - --primary-light: rgba(68, 68, 68, 0.08); + // ---- Backgrounds ---- --bg-primary: #ffffff; - --bg-secondary: rgba(250, 250, 250, 0.95); - --bg-tertiary: rgba(0, 0, 0, 0.03); - --bg-hover: rgba(0, 0, 0, 0.05); - --text-primary: #111111; - --text-secondary: #666666; - --text-tertiary: #999999; - --border-color: #eaeaea; - --border-radius: 6px; - --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.08); - --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.12); - --bg-gradient: linear-gradient(135deg, #ffffff 0%, #fafafa 100%); - --primary-gradient: linear-gradient(135deg, #444444 0%, #666666 100%); - --card-bg: rgba(250, 250, 250, 0.95); - --card-inner-bg: #f5f5f5; - --sent-card-bg: #444444; -} + --bg-secondary: #f9f9f9; + --bg-secondary-solid: #f9f9f9; + --bg-tertiary: #f0f0f0; + --bg-hover: #ececec; + --bg-sidebar: #f9f9f9; + --bg-gradient: linear-gradient(180deg, #ffffff 0%, #f7f7f8 100%); -// ==================== 深色主题 ==================== + // ---- Text ---- + --text-primary: #0d0d0d; + --text-secondary: #6e6e80; + --text-tertiary: #8e8ea0; -// 云上舞白 - 深色 -[data-theme="cloud-dancer"][data-mode="dark"] { - --primary: #C9A86C; - --primary-rgb: 201, 168, 108; - --primary-hover: #D9B87C; - --primary-light: rgba(201, 168, 108, 0.15); - --bg-primary: #1a1816; - --bg-secondary: rgba(40, 36, 32, 0.9); - --bg-secondary-solid: #282420; - --bg-tertiary: rgba(255, 255, 255, 0.05); - --bg-hover: rgba(255, 255, 255, 0.08); - --text-primary: #F0EEE9; - --text-secondary: #b3b0aa; - --text-tertiary: #807d78; - --border-color: rgba(255, 255, 255, 0.1); - --bg-gradient: linear-gradient(135deg, #1a1816 0%, #252220 100%); - --primary-gradient: linear-gradient(135deg, #8B7355 0%, #C9A86C 100%); - --card-bg: rgba(40, 36, 32, 0.9); - --card-inner-bg: #27231F; + // ---- Borders ---- + --border-color: rgba(0, 0, 0, 0.08); + --border-radius: 12px; + + // ---- Shadows ---- + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.06); + + // ---- Cards ---- + --card-bg: #f7f7f8; + --card-inner-bg: #ffffff; --sent-card-bg: var(--primary); + + // ---- On-primary foreground ---- + --on-primary: #ffffff; + + // ---- Layout ---- + --sidebar-width: 260px; } -// 刚玉蓝 - 深色 -[data-theme="corundum-blue"][data-mode="dark"] { - --primary: #6A9AAA; - --primary-rgb: 106, 154, 170; - --primary-hover: #7AAABA; - --primary-light: rgba(106, 154, 170, 0.15); - --bg-primary: #141a1c; - --bg-secondary: rgba(30, 40, 44, 0.9); - --bg-secondary-solid: #1e282c; - --bg-tertiary: rgba(255, 255, 255, 0.05); - --bg-hover: rgba(255, 255, 255, 0.08); - --text-primary: #E8EEF0; - --text-secondary: #a8b4b8; - --text-tertiary: #6a7a80; - --border-color: rgba(255, 255, 255, 0.1); - --bg-gradient: linear-gradient(135deg, #141a1c 0%, #1e282c 100%); - --primary-gradient: linear-gradient(135deg, #4A6670 0%, #6A9AAA 100%); - --card-bg: rgba(30, 40, 44, 0.9); - --card-inner-bg: #1D272A; +// ============================================= +// Dark mode +// ============================================= +[data-mode="dark"] { + // ---- Accent / Brand ---- + --primary: #10a37f; + --primary-rgb: 16, 163, 127; + --primary-hover: #1ab893; + --primary-light: rgba(16, 163, 127, 0.15); + --primary-gradient: linear-gradient(135deg, #10a37f 0%, #1ab893 100%); + + --danger: #f87171; + --warning: #fbbf24; + + // ---- Backgrounds ---- + --bg-primary: #212121; + --bg-secondary: #2f2f2f; + --bg-secondary-solid: #2f2f2f; + --bg-tertiary: #383838; + --bg-hover: #383838; + --bg-sidebar: #171717; + --bg-gradient: linear-gradient(180deg, #212121 0%, #1a1a1a 100%); + + // ---- Text ---- + --text-primary: #ececec; + --text-secondary: #b4b4b4; + --text-tertiary: #676767; + + // ---- Borders ---- + --border-color: rgba(255, 255, 255, 0.08); + + // ---- Shadows ---- + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4); + + // ---- Cards ---- + --card-bg: #2f2f2f; + --card-inner-bg: #353535; --sent-card-bg: var(--primary); + + // ---- On-primary foreground ---- + --on-primary: #ffffff; + + --primary-gradient: linear-gradient(135deg, #10a37f 0%, #1ab893 100%); } -// 冰猕猴桃汁绿 - 深色 -[data-theme="kiwi-green"][data-mode="dark"] { - --primary: #9ABA7C; - --primary-rgb: 154, 186, 124; - --primary-hover: #AACA8C; - --primary-light: rgba(154, 186, 124, 0.15); - --bg-primary: #161a14; - --bg-secondary: rgba(34, 42, 30, 0.9); - --bg-secondary-solid: #222a1e; - --bg-tertiary: rgba(255, 255, 255, 0.05); - --bg-hover: rgba(255, 255, 255, 0.08); - --text-primary: #E8F0E4; - --text-secondary: #a8b4a0; - --text-tertiary: #6a7a60; - --border-color: rgba(255, 255, 255, 0.1); - --bg-gradient: linear-gradient(135deg, #161a14 0%, #222a1e 100%); - --primary-gradient: linear-gradient(135deg, #7A9A5C 0%, #9ABA7C 100%); - --card-bg: rgba(34, 42, 30, 0.9); - --card-inner-bg: #21281D; - --sent-card-bg: var(--primary); -} - -// 辛辣红 - 深色 -[data-theme="spicy-red"][data-mode="dark"] { - --primary: #C06068; - --primary-rgb: 192, 96, 104; - --primary-hover: #D07078; - --primary-light: rgba(192, 96, 104, 0.15); - --bg-primary: #1a1416; - --bg-secondary: rgba(42, 32, 34, 0.9); - --bg-secondary-solid: #2a2022; - --bg-tertiary: rgba(255, 255, 255, 0.05); - --bg-hover: rgba(255, 255, 255, 0.08); - --text-primary: #F0E8E8; - --text-secondary: #b4a8aa; - --text-tertiary: #7a6a6c; - --border-color: rgba(255, 255, 255, 0.1); - --bg-gradient: linear-gradient(135deg, #1a1416 0%, #2a2022 100%); - --primary-gradient: linear-gradient(135deg, #8B4049 0%, #C06068 100%); - --card-bg: rgba(42, 32, 34, 0.9); - --card-inner-bg: #281F21; - --sent-card-bg: var(--primary); -} - -// 明水鸭色 - 深色 -[data-theme="teal-water"][data-mode="dark"] { - --primary: #7ABAAA; - --primary-rgb: 122, 186, 170; - --primary-hover: #8ACABA; - --primary-light: rgba(122, 186, 170, 0.15); - --bg-primary: #121a1a; - --bg-secondary: rgba(28, 42, 42, 0.9); - --bg-secondary-solid: #1c2a2a; - --bg-tertiary: rgba(255, 255, 255, 0.05); - --bg-hover: rgba(255, 255, 255, 0.08); - --text-primary: #E4F0F0; - --text-secondary: #a0b4b4; - --text-tertiary: #607a7a; - --border-color: rgba(255, 255, 255, 0.1); - --bg-gradient: linear-gradient(135deg, #121a1a 0%, #1c2a2a 100%); - --primary-gradient: linear-gradient(135deg, #5A8A8A 0%, #7ABAAA 100%); - --card-bg: rgba(28, 42, 42, 0.9); - --card-inner-bg: #1B2828; - --sent-card-bg: var(--primary); -} - -// 繁花如梦 - 深色(夜阑幽梦) -[data-theme="blossom-dream"][data-mode="dark"] { - // 光晕色(供伪元素使用,降低饱和度避免刺眼) - --blossom-pink: #C670C3; - --blossom-purple: #5F4B8B; - --blossom-blue: #3A2A50; - - // 主品牌色:藕粉/烟紫粉,降饱和度不刺眼 - --primary: #D19EBB; - --primary-rgb: 209, 158, 187; - --primary-hover: #DDB0C8; - --primary-light: rgba(209, 158, 187, 0.15); - - // 背景三层:极深黑灰底(去掉紫薯色),面板略浅,卡片再浅一级 - --bg-primary: #151316; - --bg-secondary: rgba(34, 30, 36, 0.92); - --bg-secondary-solid: #221E24; - --bg-tertiary: rgba(255, 255, 255, 0.04); - --bg-hover: rgba(209, 158, 187, 0.1); - - // 文字 - --text-primary: #F0EAF4; - --text-secondary: #A898AE; - --text-tertiary: #6A5870; - // 边框:极细白色内发光,剥离层级 - --border-color: rgba(255, 255, 255, 0.07); - - --bg-gradient: linear-gradient(150deg, #151316 0%, #1A1620 50%, #131018 100%); - --primary-gradient: linear-gradient(135deg, #D19EBB 0%, #A878A8 100%); - - // 卡片:比面板更亮一档,用深灰而非紫色 - --card-bg: rgba(42, 38, 46, 0.92); - --card-inner-bg: rgba(52, 48, 56, 0.96); - - --sent-card-bg: var(--primary); -} - -// Geist · 极简黑白 - 深色 -[data-theme="geist"][data-mode="dark"] { - --primary: #ededed; - --primary-rgb: 237, 237, 237; - --primary-hover: #d5d5d5; - --primary-light: rgba(237, 237, 237, 0.1); - --bg-primary: #1a1a1a; - --bg-secondary: rgba(34, 34, 34, 0.95); - --bg-secondary-solid: #222222; - --bg-tertiary: rgba(255, 255, 255, 0.04); - --bg-hover: rgba(255, 255, 255, 0.07); - --text-primary: #ededed; - --text-secondary: #999999; - --text-tertiary: #666666; - --border-color: #2e2e2e; - --border-radius: 6px; - --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4); - --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.5); - --bg-gradient: linear-gradient(135deg, #1a1a1a 0%, #222222 100%); - --primary-gradient: linear-gradient(135deg, #ededed 0%, #cccccc 100%); - --card-bg: rgba(34, 34, 34, 0.95); - --card-inner-bg: #2a2a2a; - --sent-card-bg: #3a3a3a; - // primary 是浅灰色,上方文字需要用深色 - --on-primary: #111111; -} - -// 重置样式 +// ============================================= +// Global reset +// ============================================= * { margin: 0; padding: 0; @@ -412,12 +118,13 @@ body { user-select: none; } - #app { height: 100%; } -// 滚动条样式 +// ============================================= +// Scrollbar +// ============================================= ::-webkit-scrollbar { width: 6px; height: 6px; @@ -436,17 +143,21 @@ body { } } -// 按钮基础样式 +// ============================================= +// Button base +// ============================================= .btn { display: inline-flex; align-items: center; justify-content: center; + gap: 6px; padding: 8px 16px; border: none; - border-radius: var(--border-radius); + border-radius: 8px; font-size: 14px; + font-weight: 500; cursor: pointer; - transition: all 0.2s; + transition: background 0.15s ease, opacity 0.15s ease; &-primary { background: var(--primary); @@ -455,6 +166,11 @@ body { &:hover { background: var(--primary-hover); } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } } &-secondary { @@ -462,20 +178,28 @@ body { color: var(--text-primary); &:hover { - background: var(--border-color); + background: var(--bg-hover); + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; } } } -// 卡片样式 +// ============================================= +// Card base +// ============================================= .card { - background: var(--bg-secondary); - border-radius: 16px; - box-shadow: var(--shadow-sm); + background: var(--card-bg); + border-radius: 12px; padding: 16px; } -// 全局 Switch 开关样式 +// ============================================= +// Switch toggle +// ============================================= .switch { position: relative; display: inline-block; @@ -525,7 +249,6 @@ body { } } - // 禁用状态 input:disabled+.switch-slider { opacity: 0.5; cursor: not-allowed;