import React, { useState, useEffect } from 'react'; import { useSearchParams } from 'react-router'; import { Table, Button, message, Input, QRCode, Modal, Tabs } from 'antd'; // import type { ColumnType } from 'antd/es/table'; import type { GetProp , TableProps} from 'antd'; import dayjs from 'dayjs'; import { useCurrentQuestion, useRoomMessages, useAnswerManagement, useSocketRoom, useAnswerCache } from './hooks/useSocketClient.ts'; import type { Answer, CumulativeResult } from './types.ts'; type ColumnType = GetProp[number] // 当前答题情况组件 function CurrentAnswers({ answers, columns }: { answers: Answer[], columns: any[] }) { return (
`${record.userId}-${record.date}`} pagination={false} /> ); } // 每日统计组件 function DailyStatistics({ dailyAnswers, columns }: { dailyAnswers: {[key: string]: Answer[]}, columns: any[] }) { return (
({ date }))} rowKey="date" pagination={false} /> ); } // 累计结果组件 function CumulativeResults({ results, columns }: { results: CumulativeResult[], columns: any[] }) { return (
); } // 二维码组件 function QRCodeSection({ classroom }: { classroom: string }) { return (
扫码参与训练
); } export default function ExamAdmin() { const [searchParams] = useSearchParams(); const classroom = searchParams.get('classroom'); useSocketRoom(classroom); const {currentQuestion} = useCurrentQuestion(classroom); const lastMessage = useRoomMessages(classroom); const { submitAnswersToBackend, autoSettlement, restartTraining } = useAnswerManagement(classroom); const [answers, setAnswers] = useState([]); const [dailyAnswers, setDailyAnswers] = useState<{[key: string]: Answer[]}>({}); const [currentDate, setCurrentDate] = useState(''); const [currentPrice, setCurrentPrice] = useState('0'); const [mark, setMark] = useState(''); const [activeTab, setActiveTab] = useState('current'); // 使用新的 useAnswerCache hook const { answers: cachedAnswers } = useAnswerCache(classroom, currentDate); // 更新答案状态 useEffect(() => { if (cachedAnswers && cachedAnswers.length > 0) { setAnswers(cachedAnswers); } }, [cachedAnswers]); // 更新每日答题情况 useEffect(() => { if (currentDate && cachedAnswers) { setDailyAnswers((prev: {[key: string]: Answer[]}) => ({ ...prev, [currentDate]: cachedAnswers })); } }, [currentDate, cachedAnswers]); useEffect(() => { if (currentQuestion) { console.log('currentQuestion', currentQuestion); setCurrentDate(currentQuestion.date); setCurrentPrice(String(currentQuestion.price)); } }, [currentQuestion]); // 添加结算函数 const handleSettlement = async () => { if (!classroom || answers.length === 0) return; try { await autoSettlement(currentDate); message.success('结算成功'); } catch (error) { console.error('结算失败:', error); message.error('结算失败'); } }; // 修改提交函数 const handleSubmit = async () => { if (!classroom || answers.length === 0) return; try { await submitAnswersToBackend(currentDate); message.success('答案提交成功'); setAnswers([]); } catch (error: any) { console.error('提交答案失败:', error); message.error(error?.message || '提交答案失败'); } }; // 重新开始 const handleRestart = async () => { try { await restartTraining(); setAnswers([]); setDailyAnswers({}); setCurrentDate(''); setCurrentPrice('0'); message.success('已重新开始'); } catch (error) { console.error('重新开始失败:', error); message.error('重新开始失败'); } }; const columns = [ { title: '昵称', dataIndex: 'userId', key: 'userId', }, { title: '日期', dataIndex: 'date', key: 'date', render: (text: string) => text ? dayjs(text).format('YYYY-MM-DD') : '-', }, { title: '持股', dataIndex: 'holdingStock', key: 'holdingStock', }, { title: '持币', dataIndex: 'holdingCash', key: 'holdingCash', }, { title: '价格', dataIndex: 'price', key: 'price', render: (text: string | undefined) => text ? parseFloat(text).toFixed(2) : '-', }, { title: '收益(元)', dataIndex: 'profitAmount', key: 'profitAmount', render: (text: number | undefined) => text !== undefined ? text.toFixed(2) : '-', }, { title: '盈亏率', dataIndex: 'profitPercent', key: 'profitPercent', render: (text: number | undefined) => text !== undefined ? `${text.toFixed(2)}%` : '-', } ]; const resultColumns: ColumnType[] = [ { title: '昵称', dataIndex: 'userId', key: 'userId', }, { title: '累计盈亏(元)', dataIndex: 'totalProfitAmount', key: 'totalProfitAmount', render: (text: number | undefined) => text !== undefined ? text.toFixed(2) : '-', }, { title: '累计盈亏率', dataIndex: 'totalProfitPercent', key: 'totalProfitPercent', render: (text: number | undefined) => text !== undefined ? `${text.toFixed(2)}%` : '-', }, ]; const dailyAnswersColumns = [ { title: '日期', dataIndex: 'date', key: 'date', render: (text: string) => dayjs(text).format('YYYY-MM-DD'), }, { title: '答题人数', key: 'count', render: (_: any, record: { date: string }) => dailyAnswers[record.date]?.length || 0, }, { title: '持股人数', key: 'holdingStockCount', render: (_: any, record: { date: string }) => dailyAnswers[record.date]?.filter((a: any) => a.holdingStock === '1').length || 0, }, { title: '持币人数', key: 'holdingCashCount', render: (_: any, record: { date: string }) => dailyAnswers[record.date]?.filter((a: any) => a.holdingCash === '1').length || 0, } ]; // 计算累计结果的函数 const calculateCumulativeResults = (dailyAnswers: {[key: string]: Answer[]}): CumulativeResult[] => { const userResults = new Map(); // 按日期排序 const sortedDates = Object.keys(dailyAnswers).sort((a: string, b: string) => new Date(a).getTime() - new Date(b).getTime() ); sortedDates.forEach(date => { const answers = dailyAnswers[date] || []; answers.forEach((answer: Answer) => { const userId = answer.userId; // 直接使用服务端计算好的收益数据 const profitAmount = answer.profitAmount || 0; const profitPercent = answer.profitPercent || 0; if (!userResults.has(userId)) { userResults.set(userId, { userId, totalProfitAmount: 0, totalProfitPercent: 0 }); } const currentResult = userResults.get(userId)!; currentResult.totalProfitAmount += profitAmount; currentResult.totalProfitPercent += profitPercent; userResults.set(userId, currentResult); }); }); return Array.from(userResults.values()); }; const items = [ { key: 'current', label: '当前答题情况', children: , }, { key: 'daily', label: '每日答题统计', children: , }, { key: 'cumulative', label: '累计结果', children: , }, ]; return (

答题卡管理

教室号: {classroom} 当前日期: {currentDate} 当前价格: {currentPrice}
{/* 主要内容区域 */}
{/* 底部按钮组 */}
setMark(e.target.value)} placeholder="标记" style={{ width: 200 }} />
{/* 二维码区域 */}
); }