refactor(ui): ChatGPT-style visual overhaul for app shell and analytics pages

This commit is contained in:
Jason
2026-05-04 12:38:14 +08:00
parent becec65ee3
commit 7dc7888869
13 changed files with 756 additions and 1129 deletions

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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 (
<div className="analytics-entry-page">
<div className="analytics-welcome-shell">
<ChatAnalysisHeader currentMode="private" />
<div className="analytics-welcome-container analytics-welcome-container--mode">
<div className="welcome-content">
<div className="icon-wrapper">
<BarChart2 size={40} />
<div className="analytics-welcome-body">
<div className="analytics-welcome-content">
<div className="analytics-welcome-icon">
<BarChart2 size={32} />
</div>
<h1></h1>
<p>
WeFlow <br />
<br />
</p>
<div className="action-cards">
<button onClick={handleLoadCache}>
<div className="card-icon">
<History size={24} />
<div className="analytics-welcome-actions">
<button className="analytics-welcome-card" onClick={handleLoadCache} type="button">
<History size={20} />
<div className="analytics-welcome-card-text">
<span className="analytics-welcome-card-title"></span>
<span className="analytics-welcome-card-meta">
: {formatLastTime(lastLoadTime)}
</span>
</div>
<h3></h3>
<span><br />(: {formatLastTime(lastLoadTime)})</span>
</button>
<button onClick={handleNewAnalysis}>
<div className="card-icon">
<RefreshCcw size={24} />
<button className="analytics-welcome-card" onClick={handleNewAnalysis} type="button">
<RefreshCcw size={20} />
<div className="analytics-welcome-card-text">
<span className="analytics-welcome-card-title"></span>
<span className="analytics-welcome-card-meta"></span>
</div>
<h3></h3>
<span><br />()</span>
</button>
</div>
</div>

View File

@@ -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);
}
}

View File

@@ -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 (
<div className="chat-analytics-hub-page">
<div className="chat-analytics-hub-content">
<div className="chat-analytics-hub-badge">
<BarChart3 size={16} />
<span></span>
</div>
<h1></h1>
<p className="chat-analytics-hub-desc">
<div className="analytics-hub">
<div className="analytics-hub-inner">
<h1 className="analytics-hub-title"></h1>
<p className="analytics-hub-desc">
</p>
<div className="chat-analytics-hub-grid">
<div className="analytics-hub-grid">
<button
type="button"
className="chat-analytics-entry-card"
className="analytics-hub-card"
onClick={() => navigate('/analytics/private')}
>
<div className="entry-card-icon">
<MessageSquare size={24} />
<div className="analytics-hub-card-icon">
<MessageSquare size={22} />
</div>
<div className="entry-card-header">
<h2></h2>
<ArrowRight size={18} />
<div className="analytics-hub-card-body">
<div className="analytics-hub-card-header">
<h2></h2>
<ArrowRight size={16} className="analytics-hub-card-arrow" />
</div>
<p></p>
</div>
<p></p>
<span className="entry-card-cta"></span>
</button>
<button
type="button"
className="chat-analytics-entry-card"
className="analytics-hub-card"
onClick={() => navigate('/analytics/group')}
>
<div className="entry-card-icon group">
<Users size={24} />
<div className="analytics-hub-card-icon analytics-hub-card-icon--group">
<Users size={22} />
</div>
<div className="entry-card-header">
<h2></h2>
<ArrowRight size={18} />
<div className="analytics-hub-card-body">
<div className="analytics-hub-card-header">
<h2></h2>
<ArrowRight size={16} className="analytics-hub-card-arrow" />
</div>
<p></p>
</div>
<p></p>
<span className="entry-card-cta"></span>
</button>
</div>
</div>

View File

@@ -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;
}
}

View File

@@ -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 (
<div className="home-page">
<div className="home-bg-blobs">
<div className="blob blob-1"></div>
<div className="blob blob-2"></div>
<div className="blob blob-3"></div>
</div>
<div className="home-content">
<div className="hero">
<h1 className="hero-title">WeFlow</h1>
<p className="hero-subtitle"></p>
<h1 className="home-title">WeFlow</h1>
<p className="home-subtitle"></p>
<div className="home-grid">
{features.map((f) => (
<button
key={f.path}
className="home-feature-card"
onClick={() => navigate(f.path)}
type="button"
>
<f.icon size={20} />
<div className="home-feature-text">
<span className="home-feature-label">{f.label}</span>
<span className="home-feature-desc">{f.desc}</span>
</div>
</button>
))}
</div>
</div>
</div>