mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-25 15:25:50 +00:00
142 lines
4.4 KiB
TypeScript
142 lines
4.4 KiB
TypeScript
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
|
||
wechatId?: string
|
||
messageCount: number
|
||
sentCount: number
|
||
receivedCount: number
|
||
lastMessageTime?: number | null
|
||
}
|
||
|
||
function DualReportPage() {
|
||
const navigate = useNavigate()
|
||
const [year] = useState<number>(() => {
|
||
const params = new URLSearchParams(window.location.hash.split('?')[1] || '')
|
||
const yearParam = params.get('year')
|
||
const parsedYear = yearParam ? parseInt(yearParam, 10) : 0
|
||
return Number.isNaN(parsedYear) ? 0 : parsedYear
|
||
})
|
||
const [rankings, setRankings] = useState<ContactRanking[]>([])
|
||
const [isLoading, setIsLoading] = useState(true)
|
||
const [loadError, setLoadError] = useState<string | null>(null)
|
||
const [keyword, setKeyword] = useState('')
|
||
|
||
useEffect(() => {
|
||
void loadRankings(year)
|
||
}, [year])
|
||
|
||
const loadRankings = async (reportYear: number) => {
|
||
setIsLoading(true)
|
||
setLoadError(null)
|
||
try {
|
||
const isAllTime = reportYear <= 0
|
||
const beginTimestamp = isAllTime ? 0 : Math.floor(new Date(reportYear, 0, 1).getTime() / 1000)
|
||
const endTimestamp = isAllTime ? 0 : Math.floor(new Date(reportYear, 11, 31, 23, 59, 59).getTime() / 1000)
|
||
const result = await window.electronAPI.analytics.getContactRankings(200, beginTimestamp, endTimestamp)
|
||
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) => {
|
||
const wechatId = (item.wechatId || '').toLowerCase()
|
||
return item.displayName.toLowerCase().includes(q) || wechatId.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="搜索好友(昵称/微信号)"
|
||
/>
|
||
</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.wechatId || '\u672A\u8bbe\u7f6e\u5fae\u4fe1\u53f7'}</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
|