flowchart TD
User[用户] --> F[前端 React]
F --> Q[查询 RPC get_results]
Q --> P[Vote Program 账户]
P --> Data[反序列化投票数据<br/>投票选项/各选项票数]
Data --> Chart[可视化渲染
条形图/饼图/表格]
Chart --> Live[WebSocket 实时更新<br/>solana_account_sub]
Live --> Q
style P fill:#c8e6c9
style Chart fill:#e3f2fd
结果展示需要实时性。Solana 的 400ms 出块时间让"实时更新"成为可能。
17.6.1 实时数据获取
typescript
// hooks/usePolls.ts — 获取所有投票
import { useEffect, useState } from 'react';
import { PublicKey } from '@solana/web3.js';
export const usePolls = (program: any) => {
const [polls, setPolls] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
const fetchPolls = async () => {
if (!program) return;
// Anchor 自动索引所有 VoteAccount
const accounts = await program.account.voteAccount.all();
// 按创建时间排序
const sorted = accounts.sort((a: any, b: any) =>
(b.account.createdAt - a.account.createdAt)
);
setPolls(sorted);
setLoading(false);
};
useEffect(() => { fetchPolls(); }, [program]);
// 每 5 秒自动刷新
useEffect(() => {
const interval = setInterval(fetchPolls, 5000);
return () => clearInterval(interval);
}, [program]);
return { polls, loading, refetch: fetchPolls };
};17.6.2 结果卡片组件
tsx
// components/ResultsCard.tsx
export const ResultsCard = ({ poll }: { poll: any }) => {
const { question, options, votes, isActive, endTime } = poll.account;
const totalVotes = votes.reduce((a: number, b: number) => a + b, 0);
return (
<div className="results-card">
<h3>{question}</h3>
<div className="status-badge">
{isActive ? <span className="active">● 进行中</span> : <span className="closed">✓ 已结束</span>}
{isActive && <span>剩余 {Math.max(0, Math.floor((endTime - Date.now()/1000)/60))} 分钟</span>}
</div>
{options.map((opt: string, i: number) => {
const pct = totalVotes ? (votes[i] / totalVotes * 100) : 0;
return (
<div key={i} className="result-bar">
<div className="label">{opt}</div>
<div className="bar-container">
<div className="bar" style={{width: `${pct}%`}} />
</div>
<div className="count">{votes[i]} 票 ({pct.toFixed(1)}%)</div>
</div>
);
})}
<div className="total">总计: {totalVotes} 票</div>
</div>
);
};17.6.3 概率统计(可选高级)
typescript
// 计算置信区间(Wilson 得分)
function wilsonScore(votes: number, total: number, z: number = 1.96) {
const p = votes / total;
const n = total;
const phat = (p + z*z/(2*n)) / (1 + z*z/n);
const error = z * Math.sqrt((p*(1-p) + z*z/(4*n)) / n) / (1 + z*z/n);
return { lower: phat - error, upper: phat + error };
}当票数较小时,Wilson 区间给出更保守的估计。教学版可省略。
, 前往 → 17.7 动画与实时更新 |*
评论
0评论加载中…