mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-25 15:25:50 +00:00
年度报告ui实现
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
padding: 40px 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-icon {
|
.header-icon {
|
||||||
@@ -25,6 +26,63 @@
|
|||||||
margin: 0 0 48px;
|
margin: 0 0 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.report-sections {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 32px;
|
||||||
|
width: min(760px, 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-section {
|
||||||
|
width: 100%;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 28px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-desc {
|
||||||
|
margin: 8px 0 0;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: color-mix(in srgb, var(--primary) 12%, transparent);
|
||||||
|
color: var(--primary);
|
||||||
|
border: 1px solid color-mix(in srgb, var(--primary) 30%, transparent);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-hint {
|
||||||
|
margin: 12px 0 0;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
.year-grid {
|
.year-grid {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -34,6 +92,12 @@
|
|||||||
margin-bottom: 48px;
|
margin-bottom: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.report-section .year-grid {
|
||||||
|
justify-content: flex-start;
|
||||||
|
max-width: none;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
.year-card {
|
.year-card {
|
||||||
width: 120px;
|
width: 120px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
@@ -104,6 +168,13 @@
|
|||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.secondary {
|
||||||
|
background: var(--card-bg);
|
||||||
|
color: var(--text-primary);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.spin {
|
.spin {
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { Calendar, Loader2, Sparkles } from 'lucide-react'
|
import { Calendar, Loader2, Sparkles, Users } from 'lucide-react'
|
||||||
import './AnnualReportPage.scss'
|
import './AnnualReportPage.scss'
|
||||||
|
|
||||||
|
type YearOption = number | 'all'
|
||||||
|
|
||||||
function AnnualReportPage() {
|
function AnnualReportPage() {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const [availableYears, setAvailableYears] = useState<number[]>([])
|
const [availableYears, setAvailableYears] = useState<number[]>([])
|
||||||
const [selectedYear, setSelectedYear] = useState<number | null>(null)
|
const [selectedYear, setSelectedYear] = useState<YearOption | null>(null)
|
||||||
|
const [selectedPairYear, setSelectedPairYear] = useState<YearOption | null>(null)
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
const [isGenerating, setIsGenerating] = useState(false)
|
const [isGenerating, setIsGenerating] = useState(false)
|
||||||
const [loadError, setLoadError] = useState<string | null>(null)
|
const [loadError, setLoadError] = useState<string | null>(null)
|
||||||
@@ -23,6 +26,7 @@ function AnnualReportPage() {
|
|||||||
if (result.success && result.data && result.data.length > 0) {
|
if (result.success && result.data && result.data.length > 0) {
|
||||||
setAvailableYears(result.data)
|
setAvailableYears(result.data)
|
||||||
setSelectedYear(result.data[0])
|
setSelectedYear(result.data[0])
|
||||||
|
setSelectedPairYear(result.data[0])
|
||||||
} else if (!result.success) {
|
} else if (!result.success) {
|
||||||
setLoadError(result.error || '加载年度数据失败')
|
setLoadError(result.error || '加载年度数据失败')
|
||||||
}
|
}
|
||||||
@@ -35,7 +39,7 @@ function AnnualReportPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleGenerateReport = async () => {
|
const handleGenerateReport = async () => {
|
||||||
if (!selectedYear) return
|
if (!selectedYear || selectedYear === 'all') return
|
||||||
setIsGenerating(true)
|
setIsGenerating(true)
|
||||||
try {
|
try {
|
||||||
navigate(`/annual-report/view?year=${selectedYear}`)
|
navigate(`/annual-report/view?year=${selectedYear}`)
|
||||||
@@ -67,21 +71,39 @@ function AnnualReportPage() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const yearOptions: YearOption[] = availableYears.length > 0
|
||||||
|
? ['all', ...availableYears]
|
||||||
|
: []
|
||||||
|
|
||||||
|
const getYearLabel = (value: YearOption | null) => {
|
||||||
|
if (!value) return ''
|
||||||
|
return value === 'all' ? '全部时间' : `${value} 年`
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="annual-report-page">
|
<div className="annual-report-page">
|
||||||
<Sparkles size={32} className="header-icon" />
|
<Sparkles size={32} className="header-icon" />
|
||||||
<h1 className="page-title">年度报告</h1>
|
<h1 className="page-title">年度报告</h1>
|
||||||
<p className="page-desc">选择年份,生成你的微信聊天年度回顾</p>
|
<p className="page-desc">选择年份,生成你的微信聊天年度回顾</p>
|
||||||
|
|
||||||
|
<div className="report-sections">
|
||||||
|
<section className="report-section">
|
||||||
|
<div className="section-header">
|
||||||
|
<div>
|
||||||
|
<h2 className="section-title">总年度报告</h2>
|
||||||
|
<p className="section-desc">包含所有会话与消息</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="year-grid">
|
<div className="year-grid">
|
||||||
{availableYears.map(year => (
|
{yearOptions.map(option => (
|
||||||
<div
|
<div
|
||||||
key={year}
|
key={option}
|
||||||
className={`year-card ${selectedYear === year ? 'selected' : ''}`}
|
className={`year-card ${option === 'all' ? 'all-time' : ''} ${selectedYear === option ? 'selected' : ''}`}
|
||||||
onClick={() => setSelectedYear(year)}
|
onClick={() => setSelectedYear(option)}
|
||||||
>
|
>
|
||||||
<span className="year-number">{year}</span>
|
<span className="year-number">{option === 'all' ? '全部' : option}</span>
|
||||||
<span className="year-label">年</span>
|
<span className="year-label">{option === 'all' ? '时间' : '年'}</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -89,7 +111,7 @@ function AnnualReportPage() {
|
|||||||
<button
|
<button
|
||||||
className="generate-btn"
|
className="generate-btn"
|
||||||
onClick={handleGenerateReport}
|
onClick={handleGenerateReport}
|
||||||
disabled={!selectedYear || isGenerating}
|
disabled={!selectedYear || selectedYear === 'all' || isGenerating}
|
||||||
>
|
>
|
||||||
{isGenerating ? (
|
{isGenerating ? (
|
||||||
<>
|
<>
|
||||||
@@ -99,10 +121,47 @@ function AnnualReportPage() {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Sparkles size={20} />
|
<Sparkles size={20} />
|
||||||
<span>生成 {selectedYear} 年度报告</span>
|
<span>生成 {getYearLabel(selectedYear)} 年度报告</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
{selectedYear === 'all' ? (
|
||||||
|
<p className="section-hint">全部时间报告功能准备中</p>
|
||||||
|
) : null}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="report-section">
|
||||||
|
<div className="section-header">
|
||||||
|
<div>
|
||||||
|
<h2 className="section-title">双人年度报告</h2>
|
||||||
|
<p className="section-desc">选择一位好友,只看你们的私聊</p>
|
||||||
|
</div>
|
||||||
|
<div className="section-badge">
|
||||||
|
<Users size={16} />
|
||||||
|
<span>私聊</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="year-grid">
|
||||||
|
{yearOptions.map(option => (
|
||||||
|
<div
|
||||||
|
key={`pair-${option}`}
|
||||||
|
className={`year-card ${option === 'all' ? 'all-time' : ''} ${selectedPairYear === option ? 'selected' : ''}`}
|
||||||
|
onClick={() => setSelectedPairYear(option)}
|
||||||
|
>
|
||||||
|
<span className="year-number">{option === 'all' ? '全部' : option}</span>
|
||||||
|
<span className="year-label">{option === 'all' ? '时间' : '年'}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button className="generate-btn secondary" disabled>
|
||||||
|
<Users size={20} />
|
||||||
|
<span>选择好友并生成报告</span>
|
||||||
|
</button>
|
||||||
|
<p className="section-hint">双人年度报告入口已留出,功能在开发中</p>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user