家长互动与沟通系统
有效的家校沟通能显著提升学生学习成果。本文讲解家长参与系统的设计。
家长门户设计
家长可见的信息
const parentPortalFeatures = {
studentProgress: {
currentGrades: true,
gradeHistory: true,
coursePerformance: true,
compareToClassAverage: true,
},
assignments: {
upcomingAssignments: true,
dueDate: true,
assignmentDescription: true,
submissionStatus: true,
},
attendance: {
presentAbsent: true,
tardyRecord: true,
attendanceTrend: true,
},
communication: {
teacherMessages: true,
schoolAnnouncements: true,
eventNotifications: true,
},
parentControls: {
notifications: true,
informationVisibility: true,
communicationPreferences: true,
},
};
const parentAccount = {
id: 'PARENT_001',
name: '张三',
email: 'parent@example.com',
phone: '13800000000',
linkedStudents: [
{
studentId: 'STU_001',
name: '张小明',
relationship: '父亲', // 父亲, 母亲, 监护人
permissions: ['view_all'], // view_all, view_limited, view_grades_only
},
],
notificationSettings: {
emailNotifications: true,
pushNotifications: true,
smsNotifications: false,
notificationFrequency: 'daily', // immediate, daily, weekly
},
language: 'zh',
timezone: 'Asia/Shanghai',
};
家长门户界面
完整的家长仪表板
function ParentPortal() {
const [parent, setParent] = useState(null);
const [students, setStudents] = useState([]);
const [selectedStudent, setSelectedStudent] = useState(null);
const [studentProgress, setStudentProgress] = useState(null);
useEffect(() => {
fetchParentInfo().then(setParent);
fetchLinkedStudents().then(setStudents);
}, []);
useEffect(() => {
if (selectedStudent) {
fetchStudentProgress(selectedStudent.id).then(setStudentProgress);
}
}, [selectedStudent]);
return (
<div className="parent-portal">
<header className="portal-header">
<h1>家长门户</h1>
<p>欢迎, {parent?.name}</p>
</header>
<div className="student-selector">
<label>选择学生:</label>
<select
value={selectedStudent?.id || ''}
onChange={(e) => {
const student = students.find(s => s.id === e.target.value);
setSelectedStudent(student);
}}
>
<option value="">-- 选择学生 --</option>
{students.map(student => (
<option key={student.id} value={student.id}>
{student.name}
</option>
))}
</select>
</div>
{selectedStudent && studentProgress && (
<>
<section className="student-overview">
<div className="overview-card">
<h2>学生概览</h2>
<div className="info-grid">
<div className="info-item">
<span>学生名字</span>
<strong>{selectedStudent.name}</strong>
</div>
<div className="info-item">
<span>班级</span>
<strong>{selectedStudent.className}</strong>
</div>
<div className="info-item">
<span>班主任</span>
<strong>{selectedStudent.classTeacher}</strong>
</div>
</div>
</div>
</section>
<section className="academic-performance">
<h2>学习成绩</h2>
<div className="performance-summary">
<div className="summary-card">
<h3>平均成绩</h3>
<div className="grade-value">{studentProgress.averageGrade.toFixed(1)}</div>
<p>班级平均: {studentProgress.classAverage.toFixed(1)}</p>
</div>
<div className="summary-card">
<h3>排名</h3>
<div className="rank-value">{studentProgress.rank} / {studentProgress.classSize}</div>
<p>排名百分比: {studentProgress.percentile}%</p>
</div>
</div>
<div className="courses-detail">
{studentProgress.courses.map(course => (
<div key={course.id} className="course-card">
<h4>{course.name}</h4>
<div className="grade-bar">
<div
className="grade-fill"
style={{width: `${(course.grade / 100) * 100}%`}}
>
{course.grade}
</div>
</div>
<div className="course-meta">
<span>教师: {course.teacher}</span>
<span>座位号: {course.seatNumber}</span>
</div>
</div>
))}
</div>
</section>
<section className="assignments-section">
<h2>作业</h2>
<div className="assignment-filter">
<button className="filter-btn active">全部</button>
<button className="filter-btn">待提交</button>
<button className="filter-btn">已提交</button>
<button className="filter-btn">逾期</button>
</div>
{studentProgress.assignments.map(assignment => (
<div key={assignment.id} className="assignment-item">
<div className="assignment-header">
<h4>{assignment.title}</h4>
<span className={`status status-${assignment.status}`}>
{assignment.status === 'submitted' ? '已提交' : '待提交'}
</span>
</div>
<p>{assignment.description}</p>
<div className="assignment-meta">
<span>📅 截止时间: {formatDate(assignment.dueDate)}</span>
{assignment.status === 'graded' && (
<span>📝 成绩: {assignment.grade}</span>
)}
</div>
</div>
))}
</section>
<section className="attendance-section">
<h2>出勤</h2>
<div className="attendance-summary">
<div className="attendance-stat">
<h4>出勤率</h4>
<div className="percentage">{studentProgress.attendanceRate}%</div>
</div>
<div className="attendance-stat">
<h4>缺席</h4>
<div className="count">{studentProgress.absenceCount}</div>
</div>
<div className="attendance-stat">
<h4>迟到</h4>
<div className="count">{studentProgress.tardyCount}</div>
</div>
</div>
<div className="attendance-chart">
<AttendanceChart data={studentProgress.attendanceData} />
</div>
</section>
</>
)}
</div>
);
}
沟通系统
家长-教师沟通
function ParentTeacherMessaging() {
const [conversations, setConversations] = useState([]);
const [selectedConversation, setSelectedConversation] = useState(null);
const [messageText, setMessageText] = useState('');
useEffect(() => {
fetchConversations().then(setConversations);
}, []);
const handleSendMessage = async (e) => {
e.preventDefault();
if (!messageText.trim()) return;
try {
const message = {
conversationId: selectedConversation.id,
senderType: 'parent',
content: messageText,
timestamp: new Date().toISOString(),
};
await api.post('/messages', message);
setMessageText('');
// 刷新对话
const updated = await fetchConversations();
setConversations(updated);
} catch (error) {
console.error('Failed to send message:', error);
}
};
const startNewConversation = async (teacherId) => {
try {
const conversation = await api.post('/conversations', {
participants: ['parent_id', teacherId],
subject: '关于我的孩子',
});
setConversations([...conversations, conversation]);
setSelectedConversation(conversation);
} catch (error) {
console.error('Failed to start conversation:', error);
}
};
return (
<div className="messaging-interface">
<div className="conversation-list">
<h2>消息</h2>
<div className="new-conversation">
<button onClick={() => startNewConversation()}>
+ 新建对话
</button>
</div>
{conversations.map(conv => (
<div
key={conv.id}
className={`conversation-item ${selectedConversation?.id === conv.id ? 'active' : ''}`}
onClick={() => setSelectedConversation(conv)}
>
<div className="conv-header">
<h4>{conv.participantName}</h4>
<span className="timestamp">{formatTime(conv.lastMessageTime)}</span>
</div>
<p className="conv-preview">{conv.lastMessage}</p>
{conv.unreadCount > 0 && (
<span className="unread-badge">{conv.unreadCount}</span>
)}
</div>
))}
</div>
{selectedConversation && (
<div className="conversation-view">
<div className="conversation-header">
<h2>{selectedConversation.participantName}</h2>
<p>关于: {selectedConversation.studentName}</p>
</div>
<div className="messages-container">
{selectedConversation.messages.map(msg => (
<div
key={msg.id}
className={`message ${msg.senderType === 'parent' ? 'sent' : 'received'}`}
>
<div className="message-content">
<p>{msg.content}</p>
{msg.attachments && msg.attachments.map(att => (
<div key={att.id} className="attachment">
📎 {att.name}
</div>
))}
</div>
<span className="message-time">{formatTime(msg.timestamp)}</span>
</div>
))}
</div>
<form onSubmit={handleSendMessage} className="message-form">
<textarea
value={messageText}
onChange={(e) => setMessageText(e.target.value)}
placeholder="输入你的消息..."
rows="3"
/>
<button type="submit">发送</button>
</form>
</div>
)}
</div>
);
}
通知系统
推送通知与提醒
class ParentNotificationManager {
constructor(parentId) {
this.parentId = parentId;
this.notificationTypes = {
GRADE_UPDATE: '成绩更新',
ASSIGNMENT_DUE: '作业截止提醒',
ATTENDANCE_ALERT: '出勤异常警报',
BEHAVIOR_REPORT: '行为报告',
EVENT_REMINDER: '活动提醒',
MESSAGE_RECEIVED: '收到新消息',
};
}
// 创建通知
async createNotification(type, data) {
const notification = {
parentId: this.parentId,
type,
title: this.getNotificationTitle(type, data),
message: this.formatNotificationMessage(type, data),
data,
createdAt: new Date().toISOString(),
read: false,
};
// 保存到数据库
await api.post('/notifications', notification);
// 发送推送通知
await this.sendPushNotification(notification);
return notification;
}
getNotificationTitle(type, data) {
const titles = {
GRADE_UPDATE: `${data.studentName} 新成绩`,
ASSIGNMENT_DUE: `${data.assignmentTitle} 即将截止`,
ATTENDANCE_ALERT: `${data.studentName} 出勤异常`,
BEHAVIOR_REPORT: `${data.studentName} 行为报告`,
};
return titles[type] || '新通知';
}
formatNotificationMessage(type, data) {
const messages = {
GRADE_UPDATE: `${data.courseName} 新成绩: ${data.grade}`,
ASSIGNMENT_DUE: `${data.assignmentTitle} 将在 ${formatDate(data.dueDate)} 截止`,
ATTENDANCE_ALERT: `${data.studentName} 已缺席 ${data.absenceCount} 天`,
BEHAVIOR_REPORT: `有新的行为反馈需要查看`,
};
return messages[type] || '点击查看详情';
}
// 获取未读通知
async getUnreadNotifications() {
const response = await api.get(`/parents/${this.parentId}/notifications?read=false`);
return response.data;
}
// 标记为已读
async markAsRead(notificationId) {
await api.put(`/notifications/${notificationId}`, { read: true });
}
// 发送推送通知
async sendPushNotification(notification) {
try {
// 如果父母启用了推送通知
if (await this.isPushEnabled()) {
// 使用推送服务
// 例如: Firebase Cloud Messaging
await pushService.send({
title: notification.title,
body: notification.message,
data: { notificationId: notification.id },
});
}
} catch (error) {
console.error('Failed to send push notification:', error);
}
}
async isPushEnabled() {
const parent = await api.get(`/parents/${this.parentId}`);
return parent.data.notificationSettings.pushNotifications;
}
}
学校公告系统
学校级别的沟通
function SchoolAnnouncements() {
const [announcements, setAnnouncements] = useState([]);
const [filter, setFilter] = useState('all');
useEffect(() => {
fetchAnnouncements().then(setAnnouncements);
}, []);
const filteredAnnouncements = announcements.filter(a => {
if (filter === 'important') return a.priority === 'high';
if (filter === 'events') return a.category === 'event';
if (filter === 'academic') return a.category === 'academic';
return true;
});
return (
<div className="announcements-section">
<h2>学校公告</h2>
<div className="filter-tabs">
<button
className={filter === 'all' ? 'active' : ''}
onClick={() => setFilter('all')}
>
全部公告
</button>
<button
className={filter === 'important' ? 'active' : ''}
onClick={() => setFilter('important')}
>
重要通知
</button>
<button
className={filter === 'events' ? 'active' : ''}
onClick={() => setFilter('events')}
>
活动
</button>
</div>
<div className="announcements-list">
{filteredAnnouncements.map(announcement => (
<div key={announcement.id} className="announcement-card">
<div className="announcement-header">
<h3>{announcement.title}</h3>
{announcement.priority === 'high' && (
<span className="priority-badge">重要</span>
)}
<span className="date">{formatDate(announcement.date)}</span>
</div>
<p className="announcement-content">{announcement.content}</p>
{announcement.attachments && (
<div className="attachments">
{announcement.attachments.map(att => (
<a key={att.id} href={att.url} className="attachment-link">
📎 {att.name}
</a>
))}
</div>
)}
</div>
))}
</div>
</div>
);
}
最佳实践
✅ 应该做的事:
- 定期的进度报告
- 及时的沟通渠道
- 清晰透明的信息
- 尊重隐私
- 易于理解的内容
❌ 不应该做的事:
- 过度共享学生信息
- 缺乏沟通机制
- 模糊的成绩报告
- 忽视家长反馈
- 过于复杂的界面
检查清单
- 门户信息准确
- 通知及时
- 沟通畅通
- 隐私受保护
- 易于使用


