Files
WeFlow/src/pages/DualReportPage.tsx
2026-02-01 01:13:17 +08:00

139 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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