From 96796519d1fdf6158186bb159dcaa3453ca0c399 Mon Sep 17 00:00:00 2001 From: Shuming Liu Date: Tue, 8 Sep 2026 11:34:39 +0800 Subject: [PATCH] optimized --- app.py | 44 +++++++++++++++++++++++++++++++++++++++++++- static/student.js | 26 ++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/app.py b/app.py index d422e00..9a5395a 100644 --- a/app.py +++ b/app.py @@ -412,7 +412,7 @@ class GroupStore: return False, "你已加入其他组,不能创建组;请先退出当前组", None if student["must_change_password"]: return False, "首次登录必须先修改密码", None - group_name = group_name.strip() or "未命名小组" + group_name = group_name.strip() or student["name"] with self.lock, self.connection: count = self.connection.execute( "SELECT COUNT(*) AS n FROM groups WHERE class_name=?", (student["class_name"],) @@ -497,6 +497,34 @@ class GroupStore: ) return True, "组长已解散小组" if is_leader else "已退出小组" + def leader_remove_member(self, leader_student_id: int, member_student_id: int) -> tuple[bool, str]: + with self.lock, self.connection: + leader_group = self.connection.execute( + """SELECT m.group_id,g.name,g.class_name,g.leader_student_id + FROM group_members m JOIN groups g ON g.id=m.group_id + WHERE m.student_id=?""", + (leader_student_id,), + ).fetchone() + if leader_group is None or leader_group["leader_student_id"] != leader_student_id: + return False, "只有组长可以移出组员" + if leader_student_id == member_student_id: + return False, "组长不能移出自己;如需退出请解散小组" + if self.is_locked(leader_group["class_name"]): + return False, "老师已停止分组操作,当前只能查看" + member = self.connection.execute( + """SELECT st.name + FROM group_members m JOIN students st ON st.id=m.student_id + WHERE m.student_id=? AND m.group_id=?""", + (member_student_id, leader_group["group_id"]), + ).fetchone() + if member is None: + return False, "该学生不在你的小组中" + self.connection.execute( + "DELETE FROM group_members WHERE student_id=? AND group_id=?", + (member_student_id, leader_group["group_id"]), + ) + return True, f"已将 {member['name']} 移出小组" + def switch_class(self, class_name: str) -> tuple[bool, str]: with self.lock: row = self.connection.execute( @@ -825,6 +853,7 @@ class GroupRequestHandler: return json_response(HTTPStatus.OK, {"ok": True, "authenticated": False}) active = STORE.active_class() common = { + "ok": True, "authenticated": True, "role": session["role"], "active_class": active, @@ -923,6 +952,19 @@ class GroupRequestHandler: {"ok": ok, "message": message, "require_confirm": (not ok and "确认后才会执行" in message)}, ) + if action == "groups/remove-member" and method == "POST": + session, error = self.require_student() + if error: + return error + data = self.read_json() + ok, message = STORE.leader_remove_member( + session["student_id"], int(data.get("student_id", 0)) + ) + return json_response( + HTTPStatus.OK if ok else HTTPStatus.BAD_REQUEST, + {"ok": ok, "message": message}, + ) + if action == "teacher/class" and method == "POST": _, error = self.require_teacher() if error: diff --git a/static/student.js b/static/student.js index d34aafe..80585f2 100644 --- a/static/student.js +++ b/static/student.js @@ -39,7 +39,7 @@ function renderMyGroup() {
- +

创建后你是组长。每组成员(包括组长)最多 ${state.snapshot.group_limit} 人。已加入别人的组时不能创建。

@@ -75,6 +75,7 @@ function renderGroups() { } grid.innerHTML = state.snapshot.groups.map(group => { const own = state.membership && state.membership.group_id === group.id; + const ownLeader = own && state.membership.leader_student_id === state.user.id; const locked = state.snapshot.locked; const full = group.full; let disabledReason = ''; @@ -87,7 +88,10 @@ function renderGroups() { ${group.member_count}/${state.snapshot.group_limit} 人 ${own ? '我的当前组' : ``} @@ -100,6 +104,15 @@ function renderGroups() { } submitJoin(Number(button.dataset.group)); })); + grid.querySelectorAll('button[data-kick]').forEach(button => button.addEventListener('click', () => { + if (button.dataset.reason) { + toast(button.dataset.reason, true); + return; + } + if (requireConfirm(`确定将 ${button.dataset.name} 移出小组吗?`)) { + submitKick(Number(button.dataset.kick)); + } + })); } function renderState() { @@ -160,6 +173,15 @@ async function submitJoin(groupId, confirmed = false) { if (result.ok) await loadState(); } +async function submitKick(studentId) { + const result = await api('groups/remove-member', { + method: 'POST', + json: { student_id: studentId } + }); + toast(result.message, !result.ok); + if (result.ok) await loadState(); +} + async function submitLeave(confirmed = false) { const result = await api('leave', { method: 'POST', json: { confirmed } }); if (!result.ok && result.require_confirm) {