dvc890 commited on
Commit
77a8561
·
verified ·
1 Parent(s): 2d4f4e3

Upload 62 files

Browse files
Files changed (2) hide show
  1. pages/UserList.tsx +32 -8
  2. types.ts +7 -1
pages/UserList.tsx CHANGED
@@ -2,7 +2,7 @@
2
  import React, { useState, useEffect } from 'react';
3
  import { User, UserRole, UserStatus, School, ClassInfo } from '../types';
4
  import { api } from '../services/api';
5
- import { Loader2, Check, X, Trash2, Edit, Briefcase, GraduationCap, AlertCircle, Bot } from 'lucide-react';
6
  import { ConfirmModal } from '../components/ConfirmModal';
7
  import { Toast, ToastState } from '../components/Toast';
8
 
@@ -44,14 +44,28 @@ export const UserList: React.FC = () => {
44
  useEffect(() => { loadData(); }, []);
45
 
46
  const handleApprove = async (user: User) => {
 
 
 
 
 
 
47
  setConfirmModal({
48
  isOpen: true,
49
  title: '批准注册',
50
- message: `确认批准 ${user.trueName || user.username} 的注册申请?`,
51
  onConfirm: async () => {
52
- await api.users.update(user._id || String(user.id), { status: UserStatus.ACTIVE });
53
- setToast({ show: true, message: '已批准', type: 'success' });
54
- loadData();
 
 
 
 
 
 
 
 
55
  }
56
  });
57
  };
@@ -249,9 +263,19 @@ export const UserList: React.FC = () => {
249
  {schools.map(s => <option key={s._id} value={s._id}>{s.name}</option>)}
250
  </select>
251
  ) : (
252
- <div className={`flex items-center space-x-1 group ${isAdmin && !isSelf ? 'cursor-pointer' : ''}`} onClick={() => isAdmin && !isSelf && setEditingUser(user)}>
253
- <span className="text-gray-700">{getSchoolName(user.schoolId)}</span>
254
- {isAdmin && !isSelf && <Edit size={12} className="text-gray-300 opacity-0 group-hover:opacity-100"/>}
 
 
 
 
 
 
 
 
 
 
255
  </div>
256
  )}
257
  </td>
 
2
  import React, { useState, useEffect } from 'react';
3
  import { User, UserRole, UserStatus, School, ClassInfo } from '../types';
4
  import { api } from '../services/api';
5
+ import { Loader2, Check, X, Trash2, Edit, Briefcase, GraduationCap, AlertCircle, Bot, Building } from 'lucide-react';
6
  import { ConfirmModal } from '../components/ConfirmModal';
7
  import { Toast, ToastState } from '../components/Toast';
8
 
 
44
  useEffect(() => { loadData(); }, []);
45
 
46
  const handleApprove = async (user: User) => {
47
+ // If pending school creation, notify admin
48
+ let confirmMsg = `确认批准 ${user.trueName || user.username} 的注册申请?`;
49
+ if (user.pendingSchoolData) {
50
+ confirmMsg = `⚠️ 重要:该用户申请创建新学校 [${user.pendingSchoolData.name}]。\n\n批准后,系统将自动创建该学校并将此账号设为校长。\n确定要批准吗?`;
51
+ }
52
+
53
  setConfirmModal({
54
  isOpen: true,
55
  title: '批准注册',
56
+ message: confirmMsg,
57
  onConfirm: async () => {
58
+ try {
59
+ await api.users.update(user._id || String(user.id), { status: UserStatus.ACTIVE });
60
+ setToast({ show: true, message: '已批准', type: 'success' });
61
+ loadData();
62
+ // If created school, force refresh school list event for header
63
+ if (user.pendingSchoolData) {
64
+ window.dispatchEvent(new Event('school-updated'));
65
+ }
66
+ } catch (e: any) {
67
+ setToast({ show: true, message: '操作失败: ' + (e.message || '未知错误'), type: 'error' });
68
+ }
69
  }
70
  });
71
  };
 
263
  {schools.map(s => <option key={s._id} value={s._id}>{s.name}</option>)}
264
  </select>
265
  ) : (
266
+ <div className={`flex flex-col ${isAdmin && !isSelf ? 'cursor-pointer group' : ''}`} onClick={() => isAdmin && !isSelf && setEditingUser(user)}>
267
+ {user.pendingSchoolData ? (
268
+ <div className="bg-amber-50 p-2 rounded border border-amber-100">
269
+ <span className="text-[10px] bg-amber-200 text-amber-800 px-1.5 py-0.5 rounded w-fit mb-1 font-bold flex items-center gap-1"><Building size={10}/> 申请创建学校</span>
270
+ <div className="text-gray-800 font-bold text-xs">{user.pendingSchoolData.name}</div>
271
+ <div className="text-gray-400 text-[10px] font-mono">CODE: {user.pendingSchoolData.code}</div>
272
+ </div>
273
+ ) : (
274
+ <div className="flex items-center space-x-1">
275
+ <span className="text-gray-700">{getSchoolName(user.schoolId)}</span>
276
+ {isAdmin && !isSelf && <Edit size={12} className="text-gray-300 opacity-0 group-hover:opacity-100"/>}
277
+ </div>
278
+ )}
279
  </div>
280
  )}
281
  </td>
types.ts CHANGED
@@ -35,6 +35,11 @@ export interface User {
35
  targetClass?: string;
36
  status: 'PENDING' | 'REJECTED';
37
  };
 
 
 
 
 
38
  studentNo?: string;
39
  parentName?: string;
40
  parentPhone?: string;
@@ -290,6 +295,7 @@ export interface ExchangeRule {
290
  rewardType: 'DRAW_COUNT' | 'ITEM';
291
  rewardName: string;
292
  rewardValue: number;
 
293
  }
294
 
295
  export interface AchievementConfig {
@@ -382,4 +388,4 @@ export interface AIChatMessage {
382
  audio?: string;
383
  isAudioMessage?: boolean;
384
  timestamp: number;
385
- }
 
35
  targetClass?: string;
36
  status: 'PENDING' | 'REJECTED';
37
  };
38
+ // For Principal creating a new school
39
+ pendingSchoolData?: {
40
+ name: string;
41
+ code: string;
42
+ };
43
  studentNo?: string;
44
  parentName?: string;
45
  parentPhone?: string;
 
295
  rewardType: 'DRAW_COUNT' | 'ITEM';
296
  rewardName: string;
297
  rewardValue: number;
298
+ achievementId?: string;
299
  }
300
 
301
  export interface AchievementConfig {
 
388
  audio?: string;
389
  isAudioMessage?: boolean;
390
  timestamp: number;
391
+ }