| <!DOCTYPE html> |
| <html lang="en-US"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>QA 标注系统 - 用户中心</title> |
| <link rel="stylesheet" href="/static/css/manager.css"> |
| </head> |
| <body> |
| <div class="app-container"> |
| |
| <aside class="sidebar"> |
| <div class="sidebar-header"> |
| <h1 data-i18n="app.userCenter">用户中心</h1> |
| </div> |
| <nav class="sidebar-nav"> |
| |
| <div class="nav-group"> |
| <button class="nav-item active" data-section="my-tasks"> |
| <span class="nav-icon">📝</span> |
| <span class="nav-text" data-i18n="user.myTasks">我的任务</span> |
| </button> |
| <button class="nav-item" data-section="available-tasks"> |
| <span class="nav-icon">📋</span> |
| <span class="nav-text" data-i18n="user.availableTasks">可领取任务</span> |
| </button> |
| <button class="nav-item" data-section="seed-questions"> |
| <span class="nav-icon">🌱</span> |
| <span class="nav-text" data-i18n="nav.seedQuestionManagement">种子问题</span> |
| </button> |
| </div> |
| |
| <div class="nav-divider hidden" id="navDivider"></div> |
| |
| <div class="nav-group"> |
| <button class="nav-item nav-item-link hidden" id="goToManagerBtn"> |
| <span class="nav-icon">⚙️</span> |
| <span class="nav-text" data-i18n="app.manager">管理后台</span> |
| </button> |
| </div> |
| </nav> |
| <div class="sidebar-footer"> |
| <button class="btn-language-switch" id="languageSwitchBtn" title="切换语言 / Switch Language"> |
| <span id="currentLanguage">中文</span> |
| </button> |
| <button class="btn-logout" id="logoutBtn" data-i18n="actions.logout">退出登录</button> |
| </div> |
| </aside> |
|
|
| |
| <main class="main-content"> |
| |
| <section class="content-section active" id="my-tasks-section"> |
| <div class="section-content-wrapper"> |
| |
| <div id="user-my-tasks-container"></div> |
| </div> |
| </section> |
| |
| <section class="content-section" id="available-tasks-section"> |
| <div class="section-content-wrapper"> |
| |
| <div id="user-available-tasks-container"></div> |
| </div> |
| </section> |
| |
| <section class="content-section" id="seed-questions-section"> |
| <div class="section-content-wrapper"> |
| |
| <div id="user-seed-question-container"></div> |
| </div> |
| </section> |
| </main> |
| </div> |
|
|
| |
| <div class="modal" id="modal"> |
| <div class="modal-content"> |
| <div class="modal-header"> |
| <h3 id="modalTitle">标题</h3> |
| <button class="modal-close" id="modalClose">×</button> |
| </div> |
| <div class="modal-body" id="modalBody"> |
| |
| </div> |
| <div class="modal-footer"> |
| <button class="btn btn-secondary" id="modalCancel" data-i18n="actions.cancel">取消</button> |
| <button class="btn btn-primary" id="modalSubmit" data-i18n="actions.confirm">确定</button> |
| </div> |
| </div> |
| </div> |
|
|
| <script src="https://unpkg.com/i18next@23.7.11/dist/umd/i18next.min.js"></script> |
| <script src="/static/js/api.js"></script> |
| <script src="/static/js/pagination.js"></script> |
| <script src="/static/js/i18n-helper.js"></script> |
| <script src="/static/js/user.js"></script> |
|
|
| |
| <script> |
| |
| window.addEventListener('load', async function() { |
| |
| if (typeof i18next === 'undefined') { |
| console.error('i18next 库未加载'); |
| return; |
| } |
| |
| |
| const savedLanguage = localStorage.getItem('appLanguage') || 'en-US'; |
| |
| try { |
| |
| const [zhCN, enUS] = await Promise.all([ |
| fetch('/static/locales/zh-CN.json').then(r => r.json()), |
| fetch('/static/locales/en-US.json').then(r => r.json()) |
| ]); |
| |
| await i18next.init({ |
| lng: savedLanguage, |
| fallbackLng: 'en-US', |
| debug: false, |
| resources: { |
| 'zh-CN': { |
| translation: zhCN.translation |
| }, |
| 'en-US': { |
| translation: enUS.translation |
| } |
| } |
| }); |
| |
| |
| window.i18next = i18next; |
| window.t = function(key, options) { |
| return i18next.t(key, options); |
| }; |
| |
| |
| const i18nReadyEvent = new CustomEvent('i18next-ready', { |
| detail: { language: savedLanguage } |
| }); |
| window.dispatchEvent(i18nReadyEvent); |
| |
| |
| updatePageLanguage(); |
| |
| |
| const langBtn = document.getElementById('languageSwitchBtn'); |
| if (langBtn) { |
| langBtn.addEventListener('click', toggleLanguage); |
| } |
| } catch (error) { |
| console.error('i18next 初始化失败:', error); |
| } |
| }); |
| |
| |
| function toggleLanguage() { |
| if (!window.i18next) return; |
| |
| const currentLang = window.i18next.language; |
| const newLang = currentLang === 'zh-CN' ? 'en-US' : 'zh-CN'; |
| |
| window.i18next.changeLanguage(newLang, () => { |
| localStorage.setItem('appLanguage', newLang); |
| updatePageLanguage(); |
| }); |
| } |
| |
| |
| function updatePageLanguage() { |
| if (!window.i18next) return; |
| |
| const lang = window.i18next.language; |
| |
| |
| document.documentElement.lang = lang; |
| |
| |
| const langBtn = document.getElementById('currentLanguage'); |
| if (langBtn) { |
| langBtn.textContent = lang === 'zh-CN' ? '中文' : 'English'; |
| } |
| |
| |
| document.querySelectorAll('[data-i18n]').forEach(element => { |
| const key = element.getAttribute('data-i18n'); |
| const translation = window.i18next.t(key); |
| |
| |
| if (element.children.length === 1 && element.children[0].tagName === 'SPAN') { |
| |
| const span = element.children[0]; |
| if (span.hasAttribute('data-i18n')) { |
| span.textContent = window.i18next.t(span.getAttribute('data-i18n')); |
| } else { |
| element.textContent = translation; |
| } |
| } else { |
| element.textContent = translation; |
| } |
| }); |
| |
| |
| document.querySelectorAll('[data-i18n-title]').forEach(element => { |
| const key = element.getAttribute('data-i18n-title'); |
| element.title = window.i18next.t(key); |
| }); |
| |
| |
| document.querySelectorAll('[data-i18n-placeholder]').forEach(element => { |
| const key = element.getAttribute('data-i18n-placeholder'); |
| element.placeholder = window.i18next.t(key); |
| }); |
| |
| |
| document.querySelectorAll('select').forEach(select => { |
| select.querySelectorAll('option').forEach(option => { |
| const i18nKey = option.getAttribute('data-i18n'); |
| if (i18nKey) { |
| |
| if (option.firstChild) { |
| option.firstChild.textContent = window.i18next.t(i18nKey); |
| } else { |
| option.textContent = window.i18next.t(i18nKey); |
| } |
| } |
| }); |
| }); |
| |
| |
| document.title = window.i18next.t('app.title') + ' - ' + window.i18next.t('app.userCenter'); |
| |
| |
| const activeSection = document.querySelector('.content-section.active'); |
| const activeSectionId = activeSection ? activeSection.id : ''; |
| |
| |
| const sectionContainers = { |
| 'my-tasks-section': 'user-my-tasks-container', |
| 'available-tasks-section': 'user-available-tasks-container', |
| 'seed-questions-section': 'user-seed-question-container' |
| }; |
| |
| |
| Object.keys(sectionContainers).forEach(sectionId => { |
| if (sectionId !== activeSectionId) { |
| const containerId = sectionContainers[sectionId]; |
| const container = document.getElementById(containerId); |
| if (container) { |
| container.innerHTML = ''; |
| } |
| } |
| }); |
| |
| |
| if (activeSection) { |
| const sectionId = activeSection.id; |
| |
| |
| if (sectionId === 'my-tasks-section' && window.MyTasks) { |
| window.MyTasks.loadTasks(); |
| } else if (sectionId === 'available-tasks-section' && window.AvailableTasks) { |
| window.AvailableTasks.loadTasks(); |
| } else if (sectionId === 'seed-questions-section' && window.UserSeedQuestion) { |
| window.UserSeedQuestion.loadSeedQuestions(); |
| } |
| } |
| } |
| </script> |
| </body> |
| </html> |
|
|