Commit fcf7434e fcf7434e643b4eef3f65708c80c188e54ba4d7a0 by 沈秋雨

feat(review): 添加分页跳转和记住暗号功能

- 新增分页跳转下拉菜单,实现快速跳转任意页码
- 增加分页栏样式,调整按钮与选择框的尺寸与间距
- 在登录面板添加“记住暗号”复选框及相关样式
- 登录时读取本地存储的暗号并自动填充
- 登录成功时根据复选框状态保存或清除本地存储的暗号
- 分页按钮和跳转选择框事件联动,实现页面数据刷新更新
1 parent cbe31001
...@@ -517,6 +517,26 @@ ...@@ -517,6 +517,26 @@
517 align-items: center; 517 align-items: center;
518 } 518 }
519 519
520 .page-bar {
521 display: flex;
522 align-items: center;
523 gap: 6px;
524 margin-bottom: 10px;
525 }
526
527 .page-bar button {
528 height: 30px;
529 padding: 0 12px;
530 font-size: 13px;
531 }
532
533 .page-bar select {
534 height: 30px;
535 font-size: 13px;
536 min-width: 80px;
537 text-align: center;
538 }
539
520 .review-bar button { 540 .review-bar button {
521 background: #fff; 541 background: #fff;
522 color: var(--text); 542 color: var(--text);
...@@ -574,6 +594,8 @@ ...@@ -574,6 +594,8 @@
574 .login-panel input { width: 100%; box-sizing: border-box; } 594 .login-panel input { width: 100%; box-sizing: border-box; }
575 .login-panel button { width: 100%; margin-top: 16px; } 595 .login-panel button { width: 100%; margin-top: 16px; }
576 .login-error { min-height: 20px; margin-top: 10px; color: #b42318; font-size: 13px; } 596 .login-error { min-height: 20px; margin-top: 10px; color: #b42318; font-size: 13px; }
597 .login-remember { display: flex; align-items: center; gap: 6px; margin-top: 10px; font-size: 13px; color: var(--muted); cursor: pointer; user-select: none; }
598 .login-remember input { width: auto; height: auto; margin: 0; cursor: pointer; }
577 599
578 @media (max-width: 1100px) { 600 @media (max-width: 1100px) {
579 main { grid-template-columns: 1fr; height: auto; } 601 main { grid-template-columns: 1fr; height: auto; }
...@@ -594,6 +616,7 @@ ...@@ -594,6 +616,7 @@
594 <input id="loginReviewer" maxlength="64" autocomplete="username" required> 616 <input id="loginReviewer" maxlength="64" autocomplete="username" required>
595 <label for="loginAccessCode">暗号</label> 617 <label for="loginAccessCode">暗号</label>
596 <input id="loginAccessCode" type="password" autocomplete="current-password" required> 618 <input id="loginAccessCode" type="password" autocomplete="current-password" required>
619 <label class="login-remember"><input id="rememberAccessCode" type="checkbox"> 记住暗号</label>
597 <button id="loginBtn" type="submit">进入编辑</button> 620 <button id="loginBtn" type="submit">进入编辑</button>
598 <div id="loginError" class="login-error"></div> 621 <div id="loginError" class="login-error"></div>
599 </form> 622 </form>
...@@ -652,11 +675,12 @@ ...@@ -652,11 +675,12 @@
652 <span id="sampleCount" class="pill"></span> 675 <span id="sampleCount" class="pill"></span>
653 </div> 676 </div>
654 <div class="section-body"> 677 <div class="section-body">
655 <div id="queryList" class="query-list"></div> 678 <div class="page-bar">
656 <div class="review-bar" style="margin-top:10px">
657 <button id="prevPageBtn" class="secondary">上一页</button> 679 <button id="prevPageBtn" class="secondary">上一页</button>
680 <select id="pageJumpSelect"></select>
658 <button id="nextPageBtn" class="secondary">下一页</button> 681 <button id="nextPageBtn" class="secondary">下一页</button>
659 </div> 682 </div>
683 <div id="queryList" class="query-list"></div>
660 </div> 684 </div>
661 </div> 685 </div>
662 </aside> 686 </aside>
...@@ -706,6 +730,7 @@ ...@@ -706,6 +730,7 @@
706 reloadBtn: document.getElementById('reloadBtn'), 730 reloadBtn: document.getElementById('reloadBtn'),
707 prevPageBtn: document.getElementById('prevPageBtn'), 731 prevPageBtn: document.getElementById('prevPageBtn'),
708 nextPageBtn: document.getElementById('nextPageBtn'), 732 nextPageBtn: document.getElementById('nextPageBtn'),
733 pageJumpSelect: document.getElementById('pageJumpSelect'),
709 734
710 metrics: document.getElementById('metrics'), 735 metrics: document.getElementById('metrics'),
711 sampleCount: document.getElementById('sampleCount'), 736 sampleCount: document.getElementById('sampleCount'),
...@@ -718,7 +743,8 @@ ...@@ -718,7 +743,8 @@
718 loginReviewer: document.getElementById('loginReviewer'), 743 loginReviewer: document.getElementById('loginReviewer'),
719 loginAccessCode: document.getElementById('loginAccessCode'), 744 loginAccessCode: document.getElementById('loginAccessCode'),
720 loginBtn: document.getElementById('loginBtn'), 745 loginBtn: document.getElementById('loginBtn'),
721 loginError: document.getElementById('loginError') 746 loginError: document.getElementById('loginError'),
747 rememberAccessCode: document.getElementById('rememberAccessCode')
722 }; 748 };
723 749
724 function newClientToken() { 750 function newClientToken() {
...@@ -728,7 +754,9 @@ ...@@ -728,7 +754,9 @@
728 754
729 function showReviewLogin(prefillReviewer = '') { 755 function showReviewLogin(prefillReviewer = '') {
730 els.loginReviewer.value = prefillReviewer; 756 els.loginReviewer.value = prefillReviewer;
731 els.loginAccessCode.value = ''; 757 const savedCode = localStorage.getItem('l2AccessCode') || '';
758 els.loginAccessCode.value = savedCode;
759 els.rememberAccessCode.checked = Boolean(savedCode);
732 els.loginError.textContent = ''; 760 els.loginError.textContent = '';
733 els.loginOverlay.classList.remove('hidden'); 761 els.loginOverlay.classList.remove('hidden');
734 window.setTimeout(() => (prefillReviewer ? els.loginAccessCode : els.loginReviewer).focus(), 0); 762 window.setTimeout(() => (prefillReviewer ? els.loginAccessCode : els.loginReviewer).focus(), 0);
...@@ -748,6 +776,11 @@ ...@@ -748,6 +776,11 @@
748 state.authToken = session.session_token; 776 state.authToken = session.session_token;
749 localStorage.setItem('l2ReviewerName', state.reviewer); 777 localStorage.setItem('l2ReviewerName', state.reviewer);
750 sessionStorage.setItem('l2ReviewAuthToken', state.authToken); 778 sessionStorage.setItem('l2ReviewAuthToken', state.authToken);
779 if (els.rememberAccessCode.checked) {
780 localStorage.setItem('l2AccessCode', accessCode);
781 } else {
782 localStorage.removeItem('l2AccessCode');
783 }
751 els.reviewerInput.value = state.reviewer; 784 els.reviewerInput.value = state.reviewer;
752 els.loginAccessCode.value = ''; 785 els.loginAccessCode.value = '';
753 els.loginOverlay.classList.add('hidden'); 786 els.loginOverlay.classList.add('hidden');
...@@ -767,6 +800,11 @@ ...@@ -767,6 +800,11 @@
767 sessionStorage.setItem('l2ReviewClientToken', state.clientToken); 800 sessionStorage.setItem('l2ReviewClientToken', state.clientToken);
768 state.authToken = sessionStorage.getItem('l2ReviewAuthToken') || ''; 801 state.authToken = sessionStorage.getItem('l2ReviewAuthToken') || '';
769 els.loginReviewer.value = state.reviewer; 802 els.loginReviewer.value = state.reviewer;
803 const savedCode = localStorage.getItem('l2AccessCode') || '';
804 if (savedCode) {
805 els.loginAccessCode.value = savedCode;
806 els.rememberAccessCode.checked = true;
807 }
770 808
771 if (state.reviewer && state.authToken) { 809 if (state.reviewer && state.authToken) {
772 try { 810 try {
...@@ -1054,6 +1092,12 @@ ...@@ -1054,6 +1092,12 @@
1054 } 1092 }
1055 els.prevPageBtn.disabled = state.page <= 1; 1093 els.prevPageBtn.disabled = state.page <= 1;
1056 els.nextPageBtn.disabled = !state.hasMore; 1094 els.nextPageBtn.disabled = !state.hasMore;
1095 // 填充页码跳转下拉
1096 const jumpOptions = [];
1097 for (let p = 1; p <= totalPages; p++) {
1098 jumpOptions.push(`<option value="${p}" ${p === state.page ? 'selected' : ''}>第 ${p} 页</option>`);
1099 }
1100 els.pageJumpSelect.innerHTML = jumpOptions.join('');
1057 } 1101 }
1058 1102
1059 function selectedQueryRows() { 1103 function selectedQueryRows() {
...@@ -1704,6 +1748,15 @@ ...@@ -1704,6 +1748,15 @@
1704 await loadGroups(); 1748 await loadGroups();
1705 renderAll(); 1749 renderAll();
1706 }); 1750 });
1751 els.pageJumpSelect.addEventListener('change', async () => {
1752 const target = Number(els.pageJumpSelect.value);
1753 if (!target || target === state.page) return;
1754 state.page = target;
1755 state.queryId = '';
1756 state.candidateId = '';
1757 await loadGroups();
1758 renderAll();
1759 });
1707 1760
1708 async function bootstrap() { 1761 async function bootstrap() {
1709 await initReviewerIdentity(); 1762 await initReviewerIdentity();
......