| |
| |
| |
| |
|
|
| (function() { |
| 'use strict'; |
|
|
| |
| |
|
|
| |
| function sha256(message) { |
| if (typeof CryptoJS !== 'undefined' && CryptoJS.SHA256) { |
| return CryptoJS.SHA256(message).toString(); |
| } else { |
| console.error(t('error.cryptoJsNotLoaded')); |
| throw new Error(t('error.encryptionLibraryNotLoaded')); |
| } |
| } |
|
|
| |
| let usersPaginator = null; |
| let editingId = null; |
| let container = null; |
|
|
| |
| const UserManagement = { |
| init: function(containerElement) { |
| container = containerElement; |
| this.initPaginators(); |
| this.initEventListeners(); |
|
|
| |
| const loadData = () => { |
| this.loadUsers(); |
| }; |
|
|
| |
| const checkI18nReady = () => { |
| if (window.i18next && window.i18next.t) { |
| try { |
| const test = window.i18next.t('common.loading'); |
| |
| if (test && test !== 'common.loading') { |
| loadData(); |
| return true; |
| } |
| } catch (e) { |
| |
| } |
| } |
| return false; |
| }; |
|
|
| |
| if (checkI18nReady()) { |
| return; |
| } |
|
|
| |
| const checkI18n = setInterval(() => { |
| if (checkI18nReady()) { |
| clearInterval(checkI18n); |
| } |
| }, 100); |
|
|
| |
| setTimeout(() => { |
| clearInterval(checkI18n); |
| console.warn('i18next initialization timeout or check failed, loading data anyway'); |
| loadData(); |
| }, 10000); |
| }, |
|
|
| initPaginators: function() { |
| |
| usersPaginator = createPaginator('users', { |
| initialPage: 1, |
| initialPageSize: 20, |
| onPageChange: (page, pageSize) => { |
| this.loadUsers(); |
| }, |
| onPageSizeChange: (pageSize) => { |
| this.loadUsers(); |
| } |
| }); |
| }, |
|
|
| initEventListeners: function() { |
| |
| const addUserBtn = container.querySelector('#addUserBtn'); |
| if (addUserBtn) { |
| addUserBtn.addEventListener('click', () => { |
| this.showUserForm(); |
| }); |
| } |
| }, |
|
|
| loadUsers: async function() { |
| if (!usersPaginator) return; |
|
|
| try { |
| const skip = usersPaginator.getSkip(); |
| const limit = usersPaginator.getLimit(); |
| const users = await apiGet(`/users/?skip=${skip}&limit=${limit}`); |
|
|
| |
| |
| let totalCount; |
| if (users.length < limit) { |
| totalCount = skip + users.length; |
| } else { |
| |
| try { |
| const nextPage = await apiGet(`/users/?skip=${skip + limit}&limit=1`); |
| if (nextPage.length > 0) { |
| |
| totalCount = (skip + limit) + 1; |
| } else { |
| |
| totalCount = skip + limit; |
| } |
| } catch (error) { |
| |
| totalCount = (skip + limit) + 1; |
| } |
| } |
|
|
| usersPaginator.setTotalCount(totalCount); |
| this.renderUsersTable(users); |
| } catch (error) { |
| console.error('加载用户失败:', error); |
| showError(`${t('user.loadFailed')}: ${error.message || t('error.unknownError')}`); |
| } |
| }, |
|
|
| renderUsersTable: function(users) { |
| const tbody = container.querySelector('#usersTableBody'); |
| const paginationContainer = container.querySelector('#usersPaginationContainer'); |
|
|
| |
| if (!tbody) { |
| console.warn('Users table body not found, container may have been cleared'); |
| return; |
| } |
|
|
| if (users.length === 0) { |
| tbody.innerHTML = `<tr><td colspan="10" class="loading">${t('user.noUsers')}</td></tr>`; |
| if (paginationContainer) paginationContainer.style.display = 'none'; |
| return; |
| } |
|
|
| tbody.innerHTML = users.map(user => ` |
| <tr> |
| <td>${user.id}</td> |
| <td>${escapeHtml(user.username)}</td> |
| <td>${escapeHtml(user.full_name || '-')}</td> |
| <td>${escapeHtml(user.organization || '-')}</td> |
| <td>${escapeHtml(user.team || '-')}</td> |
| <td>${escapeHtml(user.species || '-')}</td> |
| <td><span class="status-badge ${user.is_active ? 'active' : 'inactive'}"> |
| ${user.is_active ? t('status.active') : t('status.inactive')} |
| </span></td> |
| <td><span class="status-badge ${user.is_superuser ? 'superuser' : 'user'}"> |
| ${user.is_superuser ? t('user.isSuperuser') : t('user.isUser')} |
| </span></td> |
| <td>${formatDateTime(user.created_at)}</td> |
| <td> |
| <button class="btn btn-sm btn-primary" onclick="window.UserManagement.editUser(${user.id})">${t('actions.edit')}</button> |
| <button class="btn btn-sm btn-danger" onclick="window.UserManagement.deleteUser(${user.id})">${t('actions.delete')}</button> |
| </td> |
| </tr> |
| `).join(''); |
|
|
| |
| if (paginationContainer && usersPaginator) { |
| paginationContainer.style.display = 'block'; |
| usersPaginator.render(); |
| } |
| }, |
|
|
| showUserForm: function(user = null) { |
| editingId = user ? user.id : null; |
| const title = user ? t('user.editUser') : t('user.addUser'); |
| const content = ` |
| <form id="userForm"> |
| <div class="form-group"> |
| <label>${t('user.username')} *</label> |
| <input type="text" id="username" value="${user ? escapeHtml(user.username) : ''}" |
| required minlength="3" maxlength="50"> |
| </div> |
| <div class="form-group"> |
| <label>${t('user.fullName')}</label> |
| <input type="text" id="full_name" value="${user ? escapeHtml(user.full_name || '') : ''}" |
| maxlength="100"> |
| </div> |
| <div class="form-group"> |
| <label>${t('user.organization')}</label> |
| <input type="text" id="organization" value="${user ? escapeHtml(user.organization || '') : ''}" |
| maxlength="100"> |
| </div> |
| <div class="form-group"> |
| <label>${t('user.team')}</label> |
| <input type="text" id="team" value="${user ? escapeHtml(user.team || '') : ''}" |
| maxlength="100"> |
| </div> |
| <div class="form-group"> |
| <label>${t('user.species')}</label> |
| <input type="text" id="species" value="${user ? escapeHtml(user.species || '') : ''}" |
| maxlength="100"> |
| </div> |
| <div class="form-group"> |
| <label>${t('user.password')} ${user ? `(${t('user.leaveEmptyToKeep')})` : '*'}</label> |
| <input type="password" id="password" ${user ? '' : 'required'} minlength="6"> |
| </div> |
| <div class="form-group"> |
| <div class="form-check"> |
| <input type="checkbox" id="is_active" ${user && user.is_active ? 'checked' : ''}> |
| <label for="is_active">${t('status.active')}</label> |
| </div> |
| </div> |
| <div class="form-group"> |
| <div class="form-check"> |
| <input type="checkbox" id="is_superuser" ${user && user.is_superuser ? 'checked' : ''}> |
| <label for="is_superuser">${t('user.isSuperuser')}</label> |
| </div> |
| </div> |
| </form> |
| `; |
|
|
| openModal(title, content, async () => { |
| await this.saveUser(); |
| }); |
| }, |
|
|
| saveUser: async function() { |
| const username = document.getElementById('username').value; |
| const full_name = document.getElementById('full_name').value; |
| const organization = document.getElementById('organization').value; |
| const team = document.getElementById('team').value; |
| const species = document.getElementById('species').value; |
| const password = document.getElementById('password').value; |
| const is_active = document.getElementById('is_active').checked; |
| const is_superuser = document.getElementById('is_superuser').checked; |
|
|
| try { |
| const data = { |
| username, |
| full_name: full_name || null, |
| organization: organization || null, |
| team: team || null, |
| species: species || null, |
| is_active, |
| is_superuser |
| }; |
|
|
| if (password) { |
| |
| const passwordHash = sha256(password); |
| data.password = passwordHash; |
| } |
|
|
| if (editingId) { |
| await apiPut(`/users/${editingId}`, data); |
| showSuccess(t('user.updateSuccess')); |
| } else { |
| if (!password) { |
| alert(t('user.passwordRequired')); |
| return; |
| } |
| await apiPost('/users/', data); |
| showSuccess(t('user.createSuccess')); |
| } |
|
|
| closeModal(); |
| |
| if (!editingId && usersPaginator) { |
| usersPaginator.reset(); |
| } |
| this.loadUsers(); |
| } catch (error) { |
| showError(`${t('user.saveFailed')}: ${error.message || t('error.unknownError')}`); |
| } |
| }, |
|
|
| editUser: async function(userId) { |
| try { |
| const user = await apiGet(`/users/${userId}`); |
| this.showUserForm(user); |
| } catch (error) { |
| showError(`${t('user.loadFailed')}: ${error.message || t('error.unknownError')}`); |
| } |
| }, |
|
|
| deleteUser: async function(userId) { |
| if (!confirm(t('user.deleteConfirm'))) { |
| return; |
| } |
|
|
| try { |
| await apiDelete(`/users/${userId}`); |
| showSuccess(t('user.deleteSuccess')); |
| |
| if (usersPaginator) { |
| usersPaginator.adjustPageAfterDelete(); |
| } |
| this.loadUsers(); |
| } catch (error) { |
| showError(`${t('user.deleteFailed')}: ${error.message || t('error.unknownError')}`); |
| } |
| }, |
|
|
| destroy: function() { |
| |
| usersPaginator = null; |
| editingId = null; |
| container = null; |
| } |
| }; |
|
|
| |
| window.UserManagement = UserManagement; |
| })(); |
|
|