dvc890 commited on
Commit
2ab052a
·
verified ·
1 Parent(s): 648396d

Update pages/GameRandom.tsx

Browse files
Files changed (1) hide show
  1. pages/GameRandom.tsx +59 -11
pages/GameRandom.tsx CHANGED
@@ -3,7 +3,7 @@ import React, { useState, useEffect, useRef } from 'react';
3
  import { createPortal } from 'react-dom';
4
  import { api } from '../services/api';
5
  import { Student, GameSession, GameTeam, AchievementConfig } from '../types';
6
- import { Loader2, User, Users, Play, Pause, Gift, CheckCircle, XCircle, Award, Volume2, Settings, Maximize, Minimize, UserX } from 'lucide-react';
7
 
8
  export const GameRandom: React.FC = () => {
9
  const [loading, setLoading] = useState(true);
@@ -20,6 +20,10 @@ export const GameRandom: React.FC = () => {
20
  const [showResultModal, setShowResultModal] = useState(false);
21
  const [isFullscreen, setIsFullscreen] = useState(false);
22
 
 
 
 
 
23
  // Filter State
24
  const [excludedStudentIds, setExcludedStudentIds] = useState<Set<string>>(new Set());
25
  const [isFilterOpen, setIsFilterOpen] = useState(false);
@@ -41,6 +45,11 @@ export const GameRandom: React.FC = () => {
41
  return () => stopAnimation();
42
  }, []);
43
 
 
 
 
 
 
44
  const loadData = async () => {
45
  if (!homeroomClass) return setLoading(false);
46
  try {
@@ -67,23 +76,38 @@ export const GameRandom: React.FC = () => {
67
  };
68
 
69
  const getTargetList = () => {
70
- if (mode === 'TEAM') return teams;
71
-
72
- let baseList = students;
73
- if (scopeTeamId !== 'ALL') {
74
- const team = teams.find(t => t.id === scopeTeamId);
75
- if (team) {
76
- baseList = students.filter(s => team.members.includes(s._id || String(s.id)));
 
 
 
 
77
  }
 
 
78
  }
79
 
80
- // Filter out excluded students
81
- return baseList.filter(s => !excludedStudentIds.has(s._id || String(s.id)));
 
 
 
 
82
  };
83
 
84
  const startRandom = () => {
85
  const list = getTargetList();
86
- if (list.length === 0) return alert('当前范围内没有可选对象');
 
 
 
 
 
87
 
88
  setIsRunning(true);
89
  setShowResultModal(false);
@@ -116,6 +140,13 @@ export const GameRandom: React.FC = () => {
116
  const result = list[prev];
117
  setSelectedResult(result);
118
  setShowResultModal(true);
 
 
 
 
 
 
 
119
  return prev;
120
  });
121
  };
@@ -214,6 +245,23 @@ export const GameRandom: React.FC = () => {
214
  </select>
215
  )}
216
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  <button
218
  onClick={() => setIsFilterOpen(true)}
219
  className={`flex items-center gap-1 px-3 py-2 rounded-lg text-sm border font-medium ${excludedStudentIds.size > 0 ? 'bg-red-50 text-red-600 border-red-200' : 'bg-gray-50 text-gray-600 border-gray-200'}`}
 
3
  import { createPortal } from 'react-dom';
4
  import { api } from '../services/api';
5
  import { Student, GameSession, GameTeam, AchievementConfig } from '../types';
6
+ import { Loader2, User, Users, Play, Pause, Gift, CheckCircle, XCircle, Award, Volume2, Settings, Maximize, Minimize, UserX, RotateCcw, Repeat } from 'lucide-react';
7
 
8
  export const GameRandom: React.FC = () => {
9
  const [loading, setLoading] = useState(true);
 
20
  const [showResultModal, setShowResultModal] = useState(false);
21
  const [isFullscreen, setIsFullscreen] = useState(false);
22
 
23
+ // Avoidance / No Repeat State
24
+ const [avoidRepeat, setAvoidRepeat] = useState(false);
25
+ const [pickedIds, setPickedIds] = useState<Set<string>>(new Set());
26
+
27
  // Filter State
28
  const [excludedStudentIds, setExcludedStudentIds] = useState<Set<string>>(new Set());
29
  const [isFilterOpen, setIsFilterOpen] = useState(false);
 
45
  return () => stopAnimation();
46
  }, []);
47
 
48
+ // Clear picked IDs when mode changes to avoid ID confusion
49
+ useEffect(() => {
50
+ setPickedIds(new Set());
51
+ }, [mode]);
52
+
53
  const loadData = async () => {
54
  if (!homeroomClass) return setLoading(false);
55
  try {
 
76
  };
77
 
78
  const getTargetList = () => {
79
+ let baseList: any[] = [];
80
+
81
+ if (mode === 'TEAM') {
82
+ baseList = teams;
83
+ } else {
84
+ baseList = students;
85
+ if (scopeTeamId !== 'ALL') {
86
+ const team = teams.find(t => t.id === scopeTeamId);
87
+ if (team) {
88
+ baseList = students.filter(s => team.members.includes(s._id || String(s.id)));
89
+ }
90
  }
91
+ // Filter out excluded (absent) students
92
+ baseList = baseList.filter(s => !excludedStudentIds.has(s._id || String(s.id)));
93
  }
94
 
95
+ // NEW: Filter out previously picked if mode is on
96
+ if (avoidRepeat) {
97
+ baseList = baseList.filter(item => !pickedIds.has(mode === 'TEAM' ? item.id : (item._id || String(item.id))));
98
+ }
99
+
100
+ return baseList;
101
  };
102
 
103
  const startRandom = () => {
104
  const list = getTargetList();
105
+ if (list.length === 0) {
106
+ if (avoidRepeat && pickedIds.size > 0) {
107
+ return alert('本轮所有候选对象已全部点完!请点击上方“重置记录”重新开始。');
108
+ }
109
+ return alert('当前范围内没有可选对象');
110
+ }
111
 
112
  setIsRunning(true);
113
  setShowResultModal(false);
 
140
  const result = list[prev];
141
  setSelectedResult(result);
142
  setShowResultModal(true);
143
+
144
+ // Add to picked history
145
+ if (avoidRepeat) {
146
+ const id = mode === 'TEAM' ? result.id : (result._id || String(result.id));
147
+ setPickedIds(prev => new Set(prev).add(id));
148
+ }
149
+
150
  return prev;
151
  });
152
  };
 
245
  </select>
246
  )}
247
 
248
+ {/* Avoid Repeat Logic */}
249
+ <div className={`flex items-center gap-2 px-3 py-2 rounded-lg border transition-colors ${avoidRepeat ? 'bg-indigo-50 border-indigo-200' : 'bg-gray-50 border-gray-200'}`}>
250
+ <div className="flex items-center gap-2">
251
+ <input type="checkbox" id="avoidRepeat" checked={avoidRepeat} onChange={e => setAvoidRepeat(e.target.checked)} className="w-4 h-4 text-indigo-600 rounded focus:ring-indigo-500 cursor-pointer"/>
252
+ <label htmlFor="avoidRepeat" className={`text-sm font-bold cursor-pointer ${avoidRepeat ? 'text-indigo-700' : 'text-gray-600'}`}>不重复</label>
253
+ </div>
254
+ {avoidRepeat && pickedIds.size > 0 && (
255
+ <>
256
+ <div className="w-px h-4 bg-indigo-200 mx-1"></div>
257
+ <span className="text-xs text-indigo-500 font-mono">已点{pickedIds.size}</span>
258
+ <button onClick={() => setPickedIds(new Set())} className="text-indigo-400 hover:text-indigo-700 p-1 rounded-full hover:bg-indigo-100" title="重��记录">
259
+ <RotateCcw size={14}/>
260
+ </button>
261
+ </>
262
+ )}
263
+ </div>
264
+
265
  <button
266
  onClick={() => setIsFilterOpen(true)}
267
  className={`flex items-center gap-1 px-3 py-2 rounded-lg text-sm border font-medium ${excludedStudentIds.size > 0 ? 'bg-red-50 text-red-600 border-red-200' : 'bg-gray-50 text-gray-600 border-gray-200'}`}