dvc890 commited on
Commit
6c061ed
·
verified ·
1 Parent(s): 0d7a8a0

Upload 45 files

Browse files
Files changed (3) hide show
  1. pages/Games.tsx +17 -19
  2. server.js +5 -5
  3. services/api.ts +1 -1
pages/Games.tsx CHANGED
@@ -129,30 +129,28 @@ export const Games: React.FC = () => {
129
  <button onClick={() => setActiveGame('lucky')} className={`px-3 py-1.5 rounded-lg text-xs font-bold transition-all whitespace-nowrap ${activeGame === 'lucky' ? 'bg-red-100 text-red-700 border border-red-200' : 'bg-white text-gray-500 border border-transparent'}`}>
130
  🧧 抽奖
131
  </button>
132
- <button onClick={() => setActiveGame('random')} className={`px-3 py-1.5 rounded-lg text-xs font-bold transition-all whitespace-nowrap ${activeGame === 'random' ? 'bg-yellow-100 text-yellow-700 border border-yellow-200' : 'bg-white text-gray-500 border border-transparent'}`}>
133
- <Zap size={14} className="inline mr-1"/> 点名
134
- </button>
135
- <button onClick={() => setActiveGame('monster')} className={`px-3 py-1.5 rounded-lg text-xs font-bold transition-all whitespace-nowrap ${activeGame === 'monster' ? 'bg-purple-100 text-purple-700 border border-purple-200' : 'bg-white text-gray-500 border border-transparent'}`}>
136
- <Mic size={14} className="inline mr-1"/> 怪兽
137
- </button>
138
- <button onClick={() => setActiveGame('zen')} className={`px-3 py-1.5 rounded-lg text-xs font-bold transition-all whitespace-nowrap ${activeGame === 'zen' ? 'bg-teal-100 text-teal-700 border border-teal-200' : 'bg-white text-gray-500 border border-transparent'}`}>
139
- <Moon size={14} className="inline mr-1"/> 禅道
140
- </button>
 
 
 
 
141
  </div>
142
 
143
  <div className="flex-1 bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden relative">
144
- {/* Pass the selectedClass as homeroomClass prop override to children components if they use it */}
145
- {/* NOTE: Most child components were fetching `currentUser.homeroomClass`. We need to update them or patch the API call context.
146
- Since we can't easily change all child internal logic without rewriting them, we will use a key to force re-mount and
147
- pass the class context explicitly via props (we'd need to update children to accept props) OR
148
- we rely on the fact that `Game*` components usually fetch based on a prop or assume currentUser.
149
-
150
- UPDATE: I will assume children need `className` prop.
151
- */}
152
  {activeGame === 'mountain' ? <GameMountain className={selectedClass} /> :
153
  activeGame === 'lucky' ? <GameLucky className={selectedClass} /> :
154
- activeGame === 'random' ? <GameRandom className={selectedClass} /> :
155
- activeGame === 'zen' ? <GameZen className={selectedClass} /> : <GameMonster className={selectedClass} />}
 
 
156
  </div>
157
  </div>
158
  )}
 
129
  <button onClick={() => setActiveGame('lucky')} className={`px-3 py-1.5 rounded-lg text-xs font-bold transition-all whitespace-nowrap ${activeGame === 'lucky' ? 'bg-red-100 text-red-700 border border-red-200' : 'bg-white text-gray-500 border border-transparent'}`}>
130
  🧧 抽奖
131
  </button>
132
+ {isTeacher && (
133
+ <>
134
+ <button onClick={() => setActiveGame('random')} className={`px-3 py-1.5 rounded-lg text-xs font-bold transition-all whitespace-nowrap ${activeGame === 'random' ? 'bg-yellow-100 text-yellow-700 border border-yellow-200' : 'bg-white text-gray-500 border border-transparent'}`}>
135
+ <Zap size={14} className="inline mr-1"/> 点名
136
+ </button>
137
+ <button onClick={() => setActiveGame('monster')} className={`px-3 py-1.5 rounded-lg text-xs font-bold transition-all whitespace-nowrap ${activeGame === 'monster' ? 'bg-purple-100 text-purple-700 border border-purple-200' : 'bg-white text-gray-500 border border-transparent'}`}>
138
+ <Mic size={14} className="inline mr-1"/> 怪兽
139
+ </button>
140
+ <button onClick={() => setActiveGame('zen')} className={`px-3 py-1.5 rounded-lg text-xs font-bold transition-all whitespace-nowrap ${activeGame === 'zen' ? 'bg-teal-100 text-teal-700 border border-teal-200' : 'bg-white text-gray-500 border border-transparent'}`}>
141
+ <Moon size={14} className="inline mr-1"/> 禅道
142
+ </button>
143
+ </>
144
+ )}
145
  </div>
146
 
147
  <div className="flex-1 bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden relative">
 
 
 
 
 
 
 
 
148
  {activeGame === 'mountain' ? <GameMountain className={selectedClass} /> :
149
  activeGame === 'lucky' ? <GameLucky className={selectedClass} /> :
150
+ (isTeacher && activeGame === 'random') ? <GameRandom className={selectedClass} /> :
151
+ (isTeacher && activeGame === 'zen') ? <GameZen className={selectedClass} /> :
152
+ (isTeacher && activeGame === 'monster') ? <GameMonster className={selectedClass} /> :
153
+ <div className="h-full flex items-center justify-center text-gray-400">请选择游戏</div>}
154
  </div>
155
  </div>
156
  )}
server.js CHANGED
@@ -95,18 +95,19 @@ const generateStudentNo = async () => {
95
  // Insert helper route for fetching teachers of a class
96
  app.get('/api/classes/:className/teachers', async (req, res) => {
97
  const { className } = req.params;
 
98
  const schoolId = req.headers['x-school-id'];
99
 
100
  // 1. Get Homeroom Teachers
101
  const cls = await ClassModel.findOne({
102
- $expr: { $eq: [{ $concat: ["$grade", "$className"] }, className] },
103
  schoolId
104
  });
105
 
106
  let teacherIds = new Set(cls ? (cls.homeroomTeacherIds || []) : []);
107
 
108
  // 2. Get Subject Teachers from Courses
109
- const courses = await Course.find({ className, schoolId });
110
  courses.forEach(c => {
111
  if(c.teacherId) teacherIds.add(c.teacherId);
112
  });
@@ -115,9 +116,8 @@ app.get('/api/classes/:className/teachers', async (req, res) => {
115
  res.json(teachers);
116
  });
117
 
118
- // ... (Existing Routes until Games) ...
119
-
120
- // GAMES: Lucky Config (Modified to support ownerId query)
121
  app.get('/api/games/lucky-config', async (req, res) => {
122
  const filter = getQueryFilter(req);
123
  // If explicit ownerId passed (e.g. from student view), use it.
 
95
  // Insert helper route for fetching teachers of a class
96
  app.get('/api/classes/:className/teachers', async (req, res) => {
97
  const { className } = req.params;
98
+ const cleanName = decodeURIComponent(className).trim();
99
  const schoolId = req.headers['x-school-id'];
100
 
101
  // 1. Get Homeroom Teachers
102
  const cls = await ClassModel.findOne({
103
+ $expr: { $eq: [{ $concat: ["$grade", "$className"] }, cleanName] },
104
  schoolId
105
  });
106
 
107
  let teacherIds = new Set(cls ? (cls.homeroomTeacherIds || []) : []);
108
 
109
  // 2. Get Subject Teachers from Courses
110
+ const courses = await Course.find({ className: cleanName, schoolId });
111
  courses.forEach(c => {
112
  if(c.teacherId) teacherIds.add(c.teacherId);
113
  });
 
116
  res.json(teachers);
117
  });
118
 
119
+ // ... (Rest of the file follows)
120
+ // ...
 
121
  app.get('/api/games/lucky-config', async (req, res) => {
122
  const filter = getQueryFilter(req);
123
  // If explicit ownerId passed (e.g. from student view), use it.
services/api.ts CHANGED
@@ -118,7 +118,7 @@ export const api = {
118
  delete: (id: string) => request(`/users/${id}`, { method: 'DELETE' }),
119
  applyClass: (data: { userId: string, type: 'CLAIM'|'RESIGN', targetClass?: string, action: 'APPLY'|'APPROVE'|'REJECT' }) =>
120
  request('/users/class-application', { method: 'POST', body: JSON.stringify(data) }),
121
- getTeachersForClass: (className: string) => request(`/classes/${className}/teachers`),
122
  },
123
 
124
  students: {
 
118
  delete: (id: string) => request(`/users/${id}`, { method: 'DELETE' }),
119
  applyClass: (data: { userId: string, type: 'CLAIM'|'RESIGN', targetClass?: string, action: 'APPLY'|'APPROVE'|'REJECT' }) =>
120
  request('/users/class-application', { method: 'POST', body: JSON.stringify(data) }),
121
+ getTeachersForClass: (className: string) => request(`/classes/${encodeURIComponent(className)}/teachers`),
122
  },
123
 
124
  students: {