rearranged contents

This commit is contained in:
2026-09-09 17:54:42 +08:00
parent ac6d2cf3b4
commit d96a4ff0cd
3 changed files with 98 additions and 45 deletions

View File

@@ -96,6 +96,34 @@ h3 { margin: 0; font-size: 17px; }
border: 1px solid var(--border); color: #334155; border: 1px solid var(--border); color: #334155;
} }
.class-tab.active { border-color: transparent; background: var(--primary); color: #fff; } .class-tab.active { border-color: transparent; background: var(--primary); color: #fff; }
.class-panel { margin-bottom: 14px; }
.class-panel .status-row { margin-top: 14px; }
.admin-tabs {
display: flex; gap: 6px; align-items: center; overflow-x: auto;
padding: 7px; margin: 0 0 16px; background: rgba(255,255,255,.94);
border: 1px solid var(--border); border-radius: 14px; box-shadow: 0 5px 16px rgba(15,23,42,.05);
}
.admin-tab {
flex: 1 0 auto; min-width: 118px; height: 40px; border-radius: 10px;
background: transparent; color: var(--muted); font-size: 14px; font-weight: 650;
white-space: nowrap; transition: color .15s, background-color .15s, transform .15s;
}
.admin-tab:hover:not(:disabled) { background: #eef2f7; color: var(--text); transform: none; }
.admin-tab.active { background: var(--primary); color: #fff; }
.admin-tab.active:hover:not(:disabled) { background: var(--primary-dark); color: #fff; }
.tab-panel { margin-bottom: 0; }
.statistics { margin: 0 0 20px; }
.create-group-line { margin-bottom: 24px; }
.groups-head { margin-top: 4px; }
.import-label {
display: inline-flex; align-items: center; height: 38px; padding: 0 14px;
border: 0; border-radius: 8px; background: #eef2f7; color: var(--text);
font-size: 14px; font-weight: 500; line-height: 1; cursor: pointer; transition: .15s;
}
.import-label:hover { background: #e2e8f0; }
.settings-divider { height: 1px; margin: 26px 0 22px; background: var(--border); }
.settings-password-line { margin-top: 14px; }
.grid { .grid {
display: grid; grid-template-columns: repeat(auto-fill,minmax(290px,1fr)); gap: 16px; display: grid; grid-template-columns: repeat(auto-fill,minmax(290px,1fr)); gap: 16px;
@@ -153,4 +181,10 @@ h3 { margin: 0; font-size: 17px; }
.brand span { display: none; } .brand span { display: none; }
.group-card { padding: 13px; } .group-card { padding: 13px; }
.member-actions button { padding: 0 6px; } .member-actions button { padding: 0 6px; }
.admin-tabs { gap: 5px; padding: 6px; margin-bottom: 13px; }
.admin-tab { min-width: 96px; height: 38px; font-size: 13px; }
.class-panel, .import-label, .create-group-line { margin-bottom: 14px; }
.form-line { flex-direction: column; align-items: stretch; }
.form-line button, .form-line span { width: 100%; text-align: center; }
.form-line span { height: auto; color: var(--muted); font-size: 13px; }
} }

View File

@@ -31,19 +31,49 @@
</div> </div>
<div id="mainArea" class="hidden"> <div id="mainArea" class="hidden">
<section class="panel"> <section class="panel class-panel">
<div class="section-head"><h2>账号安全</h2></div> <div class="section-head">
<form class="form-line" id="teacherPasswordForm"> <h2>当前班级</h2>
<input id="teacherOldPassword" type="password" autocomplete="current-password" placeholder="原密码" required> <div class="actions">
<input id="teacherNewPassword" type="password" autocomplete="new-password" minlength="8" placeholder="新密码(至少 8 位)" required> <button class="success" id="exportButton" type="button">导出当前班 Excel</button>
<input id="teacherConfirmPassword" type="password" autocomplete="new-password" minlength="8" placeholder="确认新密码" required> <label class="ghost button small import-label">
<button data-working type="submit">修改密码</button> 上传名单<input id="importFile" type="file" accept=".xlsx" class="hidden">
</form> </label>
<div class="notice" id="teacherPasswordNotice"></div> <button class="warning" id="lockButton" type="button">停止学生操作</button>
</div>
</div>
<div class="class-tabs" id="classTabs"></div>
<div class="status-row" id="managementStatus"></div>
<p class="hint">点击班级即切换当前开放登录班级,并查看/管理该班分组。其它班学生不能登录。</p>
</section> </section>
<section class="panel"> <nav class="admin-tabs" role="tablist" aria-label="教师管理功能">
<div class="section-head"><h2>分组设置</h2></div> <button class="admin-tab active" id="groupsTab" data-tab="groups" aria-controls="groupsPanel" aria-selected="true" type="button">小组管理</button>
<button class="admin-tab" id="ungroupedTab" data-tab="ungrouped" aria-controls="ungroupedPanel" aria-selected="false" type="button">未分组学生</button>
<button class="admin-tab" id="settingsTab" data-tab="settings" aria-controls="settingsPanel" aria-selected="false" type="button">设置与安全</button>
</nav>
<section class="panel tab-panel" id="groupsPanel" role="tabpanel" aria-labelledby="groupsTab">
<div class="statistics" id="statistics"></div>
<div class="section-head"><h2>手工新建小组</h2></div>
<form class="form-line create-group-line" id="createGroupForm">
<input id="newGroupName" maxlength="30" placeholder="输入新组名,例如:第 9 组" required>
<button data-working type="submit">创建小组</button>
</form>
<div class="section-head groups-head"><h2>全部小组(成员直接可见)</h2></div>
<div class="grid" id="groupsGrid"></div>
</section>
<section class="panel tab-panel hidden" id="ungroupedPanel" role="tabpanel" aria-labelledby="ungroupedTab">
<div class="section-head"><h2>未分组学生</h2></div>
<div id="ungroupedList"></div>
</section>
<section class="panel tab-panel hidden" id="settingsPanel" role="tabpanel" aria-labelledby="settingsTab">
<div class="section-head"><h2>设置与安全</h2></div>
<h3>分组上限</h3>
<form class="form-line" id="groupLimitForm"> <form class="form-line" id="groupLimitForm">
<input id="groupLimit" type="number" min="2" max="100" required> <input id="groupLimit" type="number" min="2" max="100" required>
<span>人</span> <span>人</span>
@@ -51,42 +81,16 @@
</form> </form>
<p class="hint">调整后立即对所有班级生效;已有小组不会被自动拆分。</p> <p class="hint">调整后立即对所有班级生效;已有小组不会被自动拆分。</p>
<div class="notice" id="groupLimitNotice"></div> <div class="notice" id="groupLimitNotice"></div>
</section>
<section class="panel"> <div class="settings-divider"></div>
<div class="section-head"> <h3>账号安全</h3>
<h2>班级管理</h2> <form class="form-line settings-password-line" id="teacherPasswordForm">
<div class="actions"> <input id="teacherOldPassword" type="password" autocomplete="current-password" placeholder="原密码" required>
<button class="success" id="exportButton" type="button">导出当前班 Excel</button> <input id="teacherNewPassword" type="password" autocomplete="new-password" minlength="8" placeholder="新密码(至少 8 位)" required>
<label class="ghost button small" style="display:inline-flex;align-items:center;padding:0 10px;background:#eef2f7;color:#18212f;border-radius:8px;height:38px"> <input id="teacherConfirmPassword" type="password" autocomplete="new-password" minlength="8" placeholder="确认新密码" required>
上传名单<input id="importFile" type="file" accept=".xlsx" class="hidden"> <button data-working type="submit">修改密码</button>
</label>
<button class="warning" id="lockButton" type="button">停止学生操作</button>
</div>
</div>
<div class="class-tabs" id="classTabs"></div>
<div class="status-row" style="margin-top:14px" id="managementStatus"></div>
<p class="hint">点击班级即切换当前开放登录班级,并查看/管理该班分组。其它班学生不能登录。</p>
</section>
<div class="statistics" id="statistics"></div>
<section class="panel">
<div class="section-head"><h2>手工新建小组</h2></div>
<form class="form-line" id="createGroupForm">
<input id="newGroupName" maxlength="30" placeholder="输入新组名,例如:第 9 组" required>
<button data-working type="submit">创建小组</button>
</form> </form>
</section> <div class="notice" id="teacherPasswordNotice"></div>
<section class="panel">
<div class="section-head"><h2>全部小组(成员直接可见)</h2></div>
<div class="grid" id="groupsGrid"></div>
</section>
<section class="panel">
<div class="section-head"><h2>未分组学生</h2></div>
<div id="ungroupedList"></div>
</section> </section>
</div> </div>
</main> </main>

View File

@@ -1,5 +1,6 @@
const state = { classes: [], snapshot: null, activeClass: '', username: '' }; const state = { classes: [], snapshot: null, activeClass: '', username: '' };
const el = id => document.getElementById(id); const el = id => document.getElementById(id);
const adminTabs = ['groups', 'ungrouped', 'settings'];
function renderUnauthenticated() { function renderUnauthenticated() {
el('loginArea').classList.remove('hidden'); el('loginArea').classList.remove('hidden');
@@ -7,6 +8,16 @@ function renderUnauthenticated() {
el('teacherToolbar').classList.add('hidden'); el('teacherToolbar').classList.add('hidden');
} }
function switchAdminTab(tabName) {
if (!adminTabs.includes(tabName)) tabName = 'groups';
document.querySelectorAll('.admin-tab').forEach(button => {
const active = button.dataset.tab === tabName;
button.classList.toggle('active', active);
button.setAttribute('aria-selected', String(active));
el(`${button.dataset.tab}Panel`).classList.toggle('hidden', !active);
});
}
function renderState() { function renderState() {
el('loginArea').classList.add('hidden'); el('loginArea').classList.add('hidden');
el('mainArea').classList.remove('hidden'); el('mainArea').classList.remove('hidden');
@@ -19,6 +30,10 @@ function renderState() {
renderUngrouped(); renderUngrouped();
} }
document.querySelectorAll('.admin-tab').forEach(button => {
button.addEventListener('click', () => switchAdminTab(button.dataset.tab));
});
function renderClassTabs() { function renderClassTabs() {
el('classTabs').innerHTML = state.classes.map(item => ` el('classTabs').innerHTML = state.classes.map(item => `
<button class="class-tab ${item.class_name === state.activeClass ? 'active' : ''}" <button class="class-tab ${item.class_name === state.activeClass ? 'active' : ''}"