mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-25 15:25:50 +00:00
双人年度报告后端实现
This commit is contained in:
138
src/pages/DualReportPage.tsx
Normal file
138
src/pages/DualReportPage.tsx
Normal file
@@ -0,0 +1,138 @@
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { Loader2, Search, Users } from 'lucide-react'
|
||||
import './DualReportPage.scss'
|
||||
|
||||
interface ContactRanking {
|
||||
username: string
|
||||
displayName: string
|
||||
avatarUrl?: string
|
||||
messageCount: number
|
||||
sentCount: number
|
||||
receivedCount: number
|
||||
lastMessageTime?: number | null
|
||||
}
|
||||
|
||||
function DualReportPage() {
|
||||
const navigate = useNavigate()
|
||||
const [year, setYear] = useState<number>(0)
|
||||
const [rankings, setRankings] = useState<ContactRanking[]>([])
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [loadError, setLoadError] = useState<string | null>(null)
|
||||
const [keyword, setKeyword] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(window.location.hash.split('?')[1] || '')
|
||||
const yearParam = params.get('year')
|
||||
const parsedYear = yearParam ? parseInt(yearParam, 10) : 0
|
||||
setYear(Number.isNaN(parsedYear) ? 0 : parsedYear)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
loadRankings()
|
||||
}, [])
|
||||
|
||||
const loadRankings = async () => {
|
||||
setIsLoading(true)
|
||||
setLoadError(null)
|
||||
try {
|
||||
const result = await window.electronAPI.analytics.getContactRankings(200)
|
||||
if (result.success && result.data) {
|
||||
setRankings(result.data)
|
||||
} else {
|
||||
setLoadError(result.error || '加载好友列表失败')
|
||||
}
|
||||
} catch (e) {
|
||||
setLoadError(String(e))
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const yearLabel = year === 0 ? '全部时间' : `${year}年`
|
||||
|
||||
const filteredRankings = useMemo(() => {
|
||||
if (!keyword.trim()) return rankings
|
||||
const q = keyword.trim().toLowerCase()
|
||||
return rankings.filter((item) => {
|
||||
return item.displayName.toLowerCase().includes(q) || item.username.toLowerCase().includes(q)
|
||||
})
|
||||
}, [rankings, keyword])
|
||||
|
||||
const handleSelect = (username: string) => {
|
||||
const yearParam = year === 0 ? 0 : year
|
||||
navigate(`/dual-report/view?username=${encodeURIComponent(username)}&year=${yearParam}`)
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="dual-report-page loading">
|
||||
<Loader2 size={32} className="spin" />
|
||||
<p>正在加载聊天排行...</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (loadError) {
|
||||
return (
|
||||
<div className="dual-report-page loading">
|
||||
<p>加载失败:{loadError}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="dual-report-page">
|
||||
<div className="page-header">
|
||||
<div>
|
||||
<h1>双人年度报告</h1>
|
||||
<p>选择一位好友,生成你们的专属聊天报告</p>
|
||||
</div>
|
||||
<div className="year-badge">
|
||||
<Users size={14} />
|
||||
<span>{yearLabel}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="search-bar">
|
||||
<Search size={16} />
|
||||
<input
|
||||
value={keyword}
|
||||
onChange={(e) => setKeyword(e.target.value)}
|
||||
placeholder="搜索好友(昵称/备注/wxid)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="ranking-list">
|
||||
{filteredRankings.map((item, index) => (
|
||||
<button
|
||||
key={item.username}
|
||||
className="ranking-item"
|
||||
onClick={() => handleSelect(item.username)}
|
||||
>
|
||||
<span className={`rank-badge ${index < 3 ? 'top' : ''}`}>{index + 1}</span>
|
||||
<div className="avatar">
|
||||
{item.avatarUrl
|
||||
? <img src={item.avatarUrl} alt={item.displayName} />
|
||||
: <span>{item.displayName.slice(0, 1) || '?'}</span>
|
||||
}
|
||||
</div>
|
||||
<div className="info">
|
||||
<div className="name">{item.displayName}</div>
|
||||
<div className="sub">{item.username}</div>
|
||||
</div>
|
||||
<div className="meta">
|
||||
<div className="count">{item.messageCount.toLocaleString()} 条</div>
|
||||
<div className="hint">总消息</div>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
{filteredRankings.length === 0 ? (
|
||||
<div className="empty">没有匹配的好友</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DualReportPage
|
||||
Reference in New Issue
Block a user