optimized
This commit is contained in:
44
app.py
44
app.py
@@ -412,7 +412,7 @@ class GroupStore:
|
|||||||
return False, "你已加入其他组,不能创建组;请先退出当前组", None
|
return False, "你已加入其他组,不能创建组;请先退出当前组", None
|
||||||
if student["must_change_password"]:
|
if student["must_change_password"]:
|
||||||
return False, "首次登录必须先修改密码", None
|
return False, "首次登录必须先修改密码", None
|
||||||
group_name = group_name.strip() or "未命名小组"
|
group_name = group_name.strip() or student["name"]
|
||||||
with self.lock, self.connection:
|
with self.lock, self.connection:
|
||||||
count = self.connection.execute(
|
count = self.connection.execute(
|
||||||
"SELECT COUNT(*) AS n FROM groups WHERE class_name=?", (student["class_name"],)
|
"SELECT COUNT(*) AS n FROM groups WHERE class_name=?", (student["class_name"],)
|
||||||
@@ -497,6 +497,34 @@ class GroupStore:
|
|||||||
)
|
)
|
||||||
return True, "组长已解散小组" if is_leader else "已退出小组"
|
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]:
|
def switch_class(self, class_name: str) -> tuple[bool, str]:
|
||||||
with self.lock:
|
with self.lock:
|
||||||
row = self.connection.execute(
|
row = self.connection.execute(
|
||||||
@@ -825,6 +853,7 @@ class GroupRequestHandler:
|
|||||||
return json_response(HTTPStatus.OK, {"ok": True, "authenticated": False})
|
return json_response(HTTPStatus.OK, {"ok": True, "authenticated": False})
|
||||||
active = STORE.active_class()
|
active = STORE.active_class()
|
||||||
common = {
|
common = {
|
||||||
|
"ok": True,
|
||||||
"authenticated": True,
|
"authenticated": True,
|
||||||
"role": session["role"],
|
"role": session["role"],
|
||||||
"active_class": active,
|
"active_class": active,
|
||||||
@@ -923,6 +952,19 @@ class GroupRequestHandler:
|
|||||||
{"ok": ok, "message": message, "require_confirm": (not ok and "确认后才会执行" in message)},
|
{"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":
|
if action == "teacher/class" and method == "POST":
|
||||||
_, error = self.require_teacher()
|
_, error = self.require_teacher()
|
||||||
if error:
|
if error:
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ function renderMyGroup() {
|
|||||||
<form id="createForm">
|
<form id="createForm">
|
||||||
<label for="groupName">创建新组</label>
|
<label for="groupName">创建新组</label>
|
||||||
<div class="form-line">
|
<div class="form-line">
|
||||||
<input id="groupName" maxlength="30" placeholder="输入组名,例如:第 1 组" required>
|
<input id="groupName" maxlength="30" value="${text(state.user.name)}" placeholder="默认使用你的姓名" required>
|
||||||
<button data-working type="submit">创建</button>
|
<button data-working type="submit">创建</button>
|
||||||
</div>
|
</div>
|
||||||
<p class="hint">创建后你是组长。每组成员(包括组长)最多 ${state.snapshot.group_limit} 人。已加入别人的组时不能创建。</p>
|
<p class="hint">创建后你是组长。每组成员(包括组长)最多 ${state.snapshot.group_limit} 人。已加入别人的组时不能创建。</p>
|
||||||
@@ -75,6 +75,7 @@ function renderGroups() {
|
|||||||
}
|
}
|
||||||
grid.innerHTML = state.snapshot.groups.map(group => {
|
grid.innerHTML = state.snapshot.groups.map(group => {
|
||||||
const own = state.membership && state.membership.group_id === group.id;
|
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 locked = state.snapshot.locked;
|
||||||
const full = group.full;
|
const full = group.full;
|
||||||
let disabledReason = '';
|
let disabledReason = '';
|
||||||
@@ -87,7 +88,10 @@ function renderGroups() {
|
|||||||
<span class="chip ${full ? 'full' : ''}">${group.member_count}/${state.snapshot.group_limit} 人</span>
|
<span class="chip ${full ? 'full' : ''}">${group.member_count}/${state.snapshot.group_limit} 人</span>
|
||||||
</div>
|
</div>
|
||||||
<ul class="member-list">
|
<ul class="member-list">
|
||||||
${group.members.length ? group.members.map(member => `<li>${memberLine(member)}</li>`).join('') : '<li><span class="hint">暂无成员</span></li>'}
|
${group.members.length ? group.members.map(member => `<li>${memberLine(member)}${ownLeader && !member.is_leader
|
||||||
|
? `<button class="small danger" data-working data-kick="${member.id}" data-name="${text(member.name)}"
|
||||||
|
${locked ? 'disabled data-reason="老师已停止分组操作"' : ''}>移出</button>` : ''}</li>`).join('')
|
||||||
|
: '<li><span class="hint">暂无成员</span></li>'}
|
||||||
</ul>
|
</ul>
|
||||||
${own ? '<span class="chip active">我的当前组</span>'
|
${own ? '<span class="chip active">我的当前组</span>'
|
||||||
: `<button class="small" data-working data-group="${group.id}" data-reason="${text(disabledReason)}">加入</button>`}
|
: `<button class="small" data-working data-group="${group.id}" data-reason="${text(disabledReason)}">加入</button>`}
|
||||||
@@ -100,6 +104,15 @@ function renderGroups() {
|
|||||||
}
|
}
|
||||||
submitJoin(Number(button.dataset.group));
|
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() {
|
function renderState() {
|
||||||
@@ -160,6 +173,15 @@ async function submitJoin(groupId, confirmed = false) {
|
|||||||
if (result.ok) await loadState();
|
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) {
|
async function submitLeave(confirmed = false) {
|
||||||
const result = await api('leave', { method: 'POST', json: { confirmed } });
|
const result = await api('leave', { method: 'POST', json: { confirmed } });
|
||||||
if (!result.ok && result.require_confirm) {
|
if (!result.ok && result.require_confirm) {
|
||||||
|
|||||||
Reference in New Issue
Block a user