// 全局变量 const REFRESH_INTERVAL = 10000; // 10秒 let currentPage = 1; let totalPages = 1; let itemsPerPage = 10; let currentSortField = 'created_at'; let currentSortOrder = 'desc'; // 页面加载完成后执行 $(document).ready(function () { // 初始化应用 initializeApplication(); // 每60秒检查一次服务状态 setInterval(checkTaskStatus, 60 * 1000); // 绑定遮罩关闭按钮事件 - 使用事件委托确保对动态元素也有效 $(document).on('click', '.close-overlay', function () { hideLoading(); }); // 绑定确认删除按钮事件 $(document).on('click', '#confirmDeleteBtn', function () { deleteAccount(); }); }); // 添加防抖函数实现 function debounce(func, wait = 300) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // 添加节流函数实现 function throttle(func, limit = 300) { let inThrottle; return function (...args) { if (!inThrottle) { func(...args); inThrottle = true; setTimeout(() => (inThrottle = false), limit); } }; } // 应用初始化函数 - 提高代码组织性 function initializeApplication() { // 绑定分页事件必须在DOM准备好后立即执行 bindPaginationEvents(); // 绑定排序事件 bindSortEvents(); // 确保不使用ID排序 if (currentSortField === 'id') { currentSortField = 'created_at'; } // 绑定模态框内按钮事件 bindModalEvents(); // 绑定验证码相关事件 bindVerificationEvents(); // 为所有模态框绑定全局事件 setupGlobalModalEvents(); // 加载配置 loadConfig(); // 初始加载数据 loadAccounts(1, itemsPerPage); // 设置定时刷新 setupTaskRefresh(); // 绑定所有事件处理函数 bindEventHandlers(); // 初始清理背景遮罩,确保页面加载时没有残留 cleanupModalBackdrops(); // 在自动刷新指示器中添加最后更新时间显示 $('.position-fixed.bottom-0.end-0 .d-flex').append( '最后更新: --:--:--' ); // 立即更新一次时间,显示页面初始加载时间 updateLastRefreshTime(); } // 设置全局模态框事件 function setupGlobalModalEvents() { // 为所有模态框的隐藏事件添加背景清理 $(document).on('hidden.bs.modal', '.modal', function () { // 延迟清理背景,避免与Bootstrap的处理冲突 setTimeout(() => cleanupModalBackdrops(), 150); }); // 处理所有模态框的显示事件,确保不会有多个模态框同时打开 $(document).on('show.bs.modal', '.modal', function (event) { // 如果已经有其他模态框打开,先关闭它们 const otherModals = $('.modal.show').not(this); if (otherModals.length > 0) { otherModals.modal('hide'); // 给予足够时间让其他模态框关闭 setTimeout(() => cleanupModalBackdrops(), 300); } }); } // 事件处理绑定函数 - 将所有事件绑定集中在一起 function bindEventHandlers() { // 重新绑定刷新按钮事件,使用防抖处理 $('#refresh-btn') .off('click') .on( 'click', debounce(function () { fetchAccounts(); }, 500) ); // 搜索按钮点击事件,使用防抖处理 $('#search-btn') .off('click') .on( 'click', debounce(function () { filterAccounts(); }, 300) ); // 搜索框回车事件,使用防抖处理 $('#search-input') .off('keypress') .on( 'keypress', debounce(function (e) { if (e.which === 13) { filterAccounts(); } }, 300) ); // 清除搜索按钮点击事件 $('#clear-search-btn') .off('click') .on('click', function () { $('#search-input').val(''); filterAccounts(); }); // 开始注册按钮点击事件,使用节流处理避免快速多次点击 $('#start-registration') .off('click') .on( 'click', throttle(function () { startTaskManually(); }, 1000) ); // 停止注册按钮点击事件,使用节流处理避免快速多次点击 $('#stop-registration') .off('click') .on( 'click', throttle(function () { stopTaskManually(); }, 1000) ); // 自定义邮箱注册按钮点击事件,使用节流处理 $('#custom-registration') .off('click') .on( 'click', throttle(function () { registerWithCustomEmail(); }, 1000) ); // 自定义邮箱输入框回车事件 $('#custom-email') .off('keypress') .on('keypress', function (e) { if (e.which === 13) { registerWithCustomEmail(); } }); // 导出账号按钮点击事件 $('#export-accounts-btn') .off('click') .on( 'click', debounce(function () { exportAccounts(); }, 500) ); // 导入账号按钮点击事件 $('#import-accounts-btn') .off('click') .on('click', function () { $('#import-file-input').click(); }); // 重启服务按钮点击事件,使用节流处理 $('#restart-service-btn') .off('click') .on( 'click', throttle(function () { restartService(); }, 3000) ); // 较长的节流时间防止频繁重启 // 重置机器ID按钮点击事件,使用节流处理 $('#reset-machine-btn') .off('click') .on( 'click', throttle(function () { resetMachineId(); }, 3000) ); // 较长的节流时间防止频繁重置 // 编辑配置按钮点击事件 $('#edit-config-btn') .off('click') .on('click', function (e) { // 阻止事件冒泡,防止可能的表单提交 e.preventDefault(); e.stopPropagation(); enableConfigForm(true); }); // 取消配置编辑按钮点击事件 $('#cancel-config-btn') .off('click') .on('click', function (e) { // 阻止事件冒泡,防止可能的表单提交 e.preventDefault(); e.stopPropagation(); enableConfigForm(false); loadConfig(); // 重新加载原始配置 }); // 保存配置表单提交事件,使用节流处理 $('#config-form') .off('submit') .on( 'submit', throttle(function (e) { e.preventDefault(); saveConfig(); }, 1000) ); // 每页显示数量下拉框变化事件,使用防抖处理 $('#per-page') .off('change') .on( 'change', debounce(function () { itemsPerPage = parseInt($(this).val()); currentPage = 1; // 重置到第一页 renderAccountsTable(); }, 300) ); // 导入文件选择变化事件 $('#import-file-input') .off('change') .on('change', function (e) { const file = e.target.files[0]; if (file) { importAccounts(file); } }); // 确认导入按钮事件 $('#confirm-import-btn') .off('click') .on('click', function () { // 导入确认逻辑 const overwrite = $('#import-overwrite-check').is(':checked'); // 实际导入操作 $('#import-confirm-modal').modal('hide'); cleanupModalBackdrops(); }); // 邮箱类型变化事件 $('#email-type') .off('change') .on('change', function () { const type = $(this).val(); if (type === 'tempemail') { $('#tempemail-fields').show(); $('#zmail-fields').hide(); } else { $('#tempemail-fields').hide(); $('#zmail-fields').show(); } }); // 代理开关事件 $('#use-proxy') .off('change') .on('change', function () { toggleProxySettings(); }); // 动态User-Agent开关事件 $('#dynamic-useragent') .off('change') .on('change', function () { const useragentContainer = $('#useragent-input-container'); if ($(this).is(':checked')) { useragentContainer.hide(); } else { useragentContainer.show(); } }); // 邮箱代理开关事件 $('#email-proxy-enabled') .off('change') .on('change', function () { const proxyAddressContainer = $('#email-proxy-address-container'); if ($(this).is(':checked')) { proxyAddressContainer.show(); } else { proxyAddressContainer.hide(); } }); } // 全局变量 let accounts = []; let filteredAccounts = []; let refreshTimer; // 显示加载遮罩 function showLoading(message = '加载中,请稍候...') { // 如果遮罩已经显示,只更新消息 const loadingOverlay = document.getElementById('loading-overlay'); if (loadingOverlay.classList.contains('show')) { $('#loading-overlay p').text(message); return; } // 否则显示遮罩 loadingOverlay.classList.add('show'); $('#loading-overlay p').text(message); // 确保关闭按钮事件绑定 - 直接绑定不通过jQuery委托 document.querySelector('.close-overlay').onclick = function () { hideLoading(); }; } // 隐藏加载遮罩 function hideLoading() { const loadingOverlay = document.getElementById('loading-overlay'); loadingOverlay.classList.remove('show'); } // 加载账号数据 function loadAccounts( page = 1, perPage = itemsPerPage, search = '', sortField = currentSortField, sortOrder = currentSortOrder, showLoadingOverlay = true ) { // 只在需要时显示加载遮罩 if (showLoadingOverlay) { showLoading(); } // 构建URL查询参数 let params = new URLSearchParams({ page: page, per_page: perPage, sort_by: sortField, order: sortOrder, }); if (search) { params.append('search', search); } const url = `/accounts?${params.toString()}`; $.ajax({ url: url, method: 'GET', success: function (response) { if (response.success) { accounts = response.data; // 更新分页和排序信息 currentPage = response.pagination.page; totalPages = response.pagination.total_pages; itemsPerPage = response.pagination.per_page; currentSortField = response.sort.field; currentSortOrder = response.sort.order; // 新增:更新账号统计信息 const totalAccounts = response.pagination.total_count || 0; const maxAccounts = parseInt($('#max-accounts').text()) || 10; const remainingSlots = Math.max(0, maxAccounts - totalAccounts); $('#current-count').text(totalAccounts); $('#remaining-slots').text(`剩余: ${remainingSlots}`); // 计算使用百分比 const usagePercent = maxAccounts > 0 ? Math.round((totalAccounts / maxAccounts) * 100) : 0; // 更新进度条 $('.battery-progress').attr('data-percent', usagePercent); $('.battery-percent').text(`${usagePercent}%`); // 更新排序控件 $('#sort-field').val(currentSortField); $('#sort-order').val(currentSortOrder); // 添加淡入效果 $('#accounts-table').css('opacity', 0); // 更新UI updateAccountsTable(accounts); updatePagination(currentPage, totalPages); $('#total-accounts').text(response.pagination.total_count); // 更新每页记录数下拉框 $('#per-page').val(itemsPerPage); // 淡入表格 $('#accounts-table').animate({ opacity: 1 }, 300); if (showLoadingOverlay) { hideLoading(); } } else { if (showLoadingOverlay) { hideLoading(); } showAlert('danger', '加载账号失败: ' + response.message); } }, error: function (xhr) { if (showLoadingOverlay) { hideLoading(); } showAlert( 'danger', '加载账号失败: ' + (xhr.responseJSON?.detail || xhr.statusText) ); }, }); } // 更新分页控件 function updatePagination(currentPage, totalPages) { // 清除现有页码 $('.pagination .page-number').remove(); // 决定显示哪些页码 let pages = []; // 限制显示的页码数量,使UI更加紧凑 if (totalPages <= 5) { // 总页数少于5,显示所有页码 for (let i = 1; i <= totalPages; i++) { pages.push(i); } } else { // 总页数大于5,使用更紧凑的布局 // 总是显示第一页 pages.push(1); // 当前页在前3页 if (currentPage <= 3) { pages.push(2, 3, 4, '...', totalPages); } // 当前页在后3页 else if (currentPage >= totalPages - 2) { pages.push( '...', totalPages - 3, totalPages - 2, totalPages - 1, totalPages ); } // 当前页在中间 else { pages.push( '...', currentPage - 1, currentPage, currentPage + 1, '...', totalPages ); } } // 创建页码元素 - 简化HTML生成 let pageElements = ''; pages.forEach((page) => { if (page === '...') { pageElements += `
  • `; } else { const isActive = page === currentPage ? 'active' : ''; pageElements += `
  • ${page}
  • `; } }); // 使用insertBefore而不是after,确保顺序正确 $(pageElements).insertBefore('#next-page'); // 更新上一页/下一页按钮状态 $('#prev-page').toggleClass('disabled', currentPage === 1); $('#next-page').toggleClass('disabled', currentPage === totalPages); // 更新分页信息文本 $('#current-page').text(currentPage); $('#total-pages').text(totalPages); } // 修复分页事件绑定 - 确保在DOM加载完成后绑定 function bindPaginationEvents() { // 上一页按钮 - 使用事件委托 $(document).on('click', '#prev-page:not(.disabled) a', function (e) { e.preventDefault(); if (currentPage > 1) { loadAccounts(currentPage - 1, itemsPerPage, $('#search-input').val()); } }); // 下一页按钮 - 使用事件委托 $(document).on('click', '#next-page:not(.disabled) a', function (e) { e.preventDefault(); if (currentPage < totalPages) { loadAccounts(currentPage + 1, itemsPerPage, $('#search-input').val()); } }); // 页码点击 - 使用事件委托确保动态生成的元素也能响应 $(document).on( 'click', '.page-number:not(.disabled) .page-link', function (e) { e.preventDefault(); const page = parseInt($(this).attr('data-page')); if (!isNaN(page)) { loadAccounts(page, itemsPerPage, $('#search-input').val()); } } ); // 每页显示记录数变更 $(document).on('change', '#per-page', function () { itemsPerPage = parseInt($(this).val()); loadAccounts(1, itemsPerPage, $('#search-input').val()); }); } // 修改搜索函数 function filterAccounts() { const searchTerm = $('#search-input').val().trim(); loadAccounts(1, itemsPerPage, searchTerm); } // 更新账号表格 function updateAccountsTable(accounts) { const accountsBody = $('#accounts-tbody'); accountsBody.empty(); // 计算当前页的起始索引 const startIndex = (currentPage - 1) * itemsPerPage; // 确保每个账号都有ID并且所有字段都有值 accounts.forEach((account, index) => { if (!account.id) account.id = Date.now() + index; // 确保所有字段都不为undefined,防止jQuery错误 account.email = account.email || ''; account.token = account.token || ''; account.password = account.password || ''; account.status = account.status || 'active'; account.usage_limit = account.usage_limit || ''; account.created_at = account.created_at || ''; account.user = account.user || ''; }); // 如果没有数据,显示空状态 if (accounts.length === 0) { accountsBody.html(`

    暂无账号数据

    `); return; } // 渲染每行数据 accounts.forEach((account, index) => { // 完整的行模板,包含所有单元格内容 const row = ` ${ startIndex + index + 1 } ${account.email} ${ account.status === 'active' ? '正常' : account.status === 'disabled' ? '停用' : '删除' } ${maskPassword( account.password )} ${renderTokenColumn(account.token, account.id, account.email)} ${renderUsageProgress(account.usage_limit)} ${account.created_at || '未知'}
    ${ account.status !== 'active' ? `` : '' } ${ account.status !== 'disabled' ? `` : '' } ${ account.status !== 'deleted' ? `` : '' }
    `; accountsBody.append(row); }); // 绑定事件 bindTableEvents(); // 更新表头排序指示 $('.sortable').removeClass('asc desc'); $(`.sortable[data-field="${currentSortField}"]`).addClass(currentSortOrder); } // 渲染账号表格 function renderAccountsTable() { const accountsBody = $('#accounts-tbody'); accountsBody.empty(); if (filteredAccounts.length === 0) { // 添加空状态提示 accountsBody.html(`

    暂无账号数据

    `); return; } // 计算当前页的数据 const startIndex = (currentPage - 1) * itemsPerPage; const endIndex = Math.min(startIndex + itemsPerPage, filteredAccounts.length); const currentPageData = filteredAccounts.slice(startIndex, endIndex); // 确保每个账号对象的字段都有值 currentPageData.forEach((account, index) => { if (!account.id) account.id = Date.now() + index; account.email = account.email || ''; account.token = account.token || ''; account.password = account.password || ''; account.status = account.status || 'active'; account.usage_limit = account.usage_limit || ''; account.created_at = account.created_at || ''; }); // 渲染每行数据 currentPageData.forEach((account, index) => { // 完整的行模板,包含所有单元格内容 const row = ` ${ startIndex + index + 1 } ${account.email} ${ account.status === 'active' ? '正常' : account.status === 'disabled' ? '停用' : '删除' } ${maskPassword( account.password )} ${renderTokenColumn(account.token, account.id, account.email)} ${renderUsageProgress(account.usage_limit)} ${account.created_at || '未知'}
    ${ account.status !== 'active' ? `` : '' } ${ account.status !== 'disabled' ? `` : '' } ${ account.status !== 'deleted' ? `` : '' }
    `; accountsBody.append(row); }); // 绑定事件 bindTableEvents(); renderPagination(); } // 渲染分页 function renderPagination() { const totalPages = Math.ceil(filteredAccounts.length / itemsPerPage); const pagination = $('#pagination'); pagination.empty(); if (totalPages <= 1) { return; } const paginationNav = $(''); const paginationUl = $(''); // 上一页按钮 paginationUl.append(`
  • `); // 页码按钮 for (let i = 1; i <= totalPages; i++) { paginationUl.append(`
  • ${i}
  • · `); } // 下一页按钮 paginationUl.append(`
  • `); paginationNav.append(paginationUl); pagination.append(paginationNav); } // 更改页码 function changePage(page) { currentPage = page; renderAccountsTable(); } // 获取账号用量详情并更新数据库 function getAccountUsage(email) { showLoading(); fetch(`/account/${encodeURIComponent(email)}/usage`) .then((res) => res.json()) .then((data) => { hideLoading(); if (data.success) { // 创建并显示用量信息模态框 const modal = $(` `); $('body').append(modal); const modalInstance = new bootstrap.Modal(modal[0]); modalInstance.show(); // 模态框关闭时移除DOM并清理背景 modal[0].addEventListener('hidden.bs.modal', function () { modal.remove(); cleanupModalBackdrops(); }); } }) .catch((error) => { console.error('获取账号用量失败:', error); showAlert('获取账号用量失败', 'danger'); hideLoading(); }); } // 更新账号用量到数据库 function updateAccountUsageLimit(email, usageLimit) { fetch(`/account/${encodeURIComponent(email)}/update-usage`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ usage_limit: usageLimit }), }) .then((res) => res.json()) .then((data) => { if (data.success) { console.log(`账号 ${email} 用量数据已更新到数据库`); } else { console.error(`更新账号 ${email} 用量数据失败:`, data.message); } }) .catch((error) => { console.error(`更新账号 ${email} 用量数据时发生错误:`, error); }); } // 修复任务状态更新问题 function startTaskManually() { showLoading(); // 清除自定义邮箱注册标记 window.customEmailRegistration = false; fetch('/registration/start', { method: 'GET', }) .then((res) => res.json()) .then((data) => { hideLoading(); if (data.success) { showAlert('定时任务已成功启动', 'success'); checkTaskStatus(); // 清除可能存在的验证码提示 $('#verification-tip-alert').hide(); // 短暂延迟后再启动验证码检查,确保UI已更新 setTimeout(() => { // 启动验证码检查 startVerificationCodeCheck(); showAlert( '任务注册已启动,系统将自动处理验证码,如需手动输入将会提示', 'info' ); }, 500); } else { showAlert(`启动任务失败: ${data.message || '未知错误'}`, 'danger'); } }) .catch((error) => { console.error('启动任务时发生错误:', error); hideLoading(); showAlert('启动任务失败,请稍后重试', 'danger'); }); } // 同样添加到停止任务函数 function stopTaskManually() { showLoading(); fetch('/registration/stop', { method: 'GET', }) .then((res) => res.json()) .then((data) => { hideLoading(); if (data.success) { showAlert('定时任务已成功停止', 'success'); // 隐藏验证码提示框 $('#verification-tip-alert').hide(); // 立即更新任务状态 - 添加这段代码 fetch('/registration/status') .then((res) => res.json()) .then((statusData) => { updateTaskStatusUI(statusData); }); // 停止验证码检查 stopVerificationCodeCheck(); } else { showAlert(`停止任务失败: ${data.message || '未知错误'}`, 'danger'); } }) .catch((error) => { console.error('停止任务时发生错误:', error); hideLoading(); showAlert('停止任务失败,请稍后重试', 'danger'); }); } // 复制到剪贴板 function copyToClipboard(text) { navigator.clipboard .writeText(text) .then(() => { showAlert('复制成功,Token已复制到剪贴板', 'success'); }) .catch((err) => { console.error('复制失败:', err); showAlert('复制失败', 'danger'); }); } // 显示通知 function showAlert(message, type, isSpecial = false) { const alertId = 'alert-' + Date.now(); const alertClass = isSpecial ? `alert-${type} special-alert animate__animated animate__bounceIn` : `alert-${type} animate__animated animate__fadeInRight`; const alert = $(` `); $('#alert-container').append(alert); // 5秒后自动消失 setTimeout(() => { $(`#${alertId}`).alert('close'); }, 5000); } // 日期时间格式化 function formatDateTime(dateTimeString) { if (!dateTimeString) return '-'; try { const date = new Date(dateTimeString); if (isNaN(date.getTime())) return dateTimeString; const year = date.getFullYear(); const month = (date.getMonth() + 1).toString().padStart(2, '0'); const day = date.getDate().toString().padStart(2, '0'); const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); const seconds = date.getSeconds().toString().padStart(2, '0'); return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; } catch (error) { return dateTimeString; } } // 修改掩码函数,增加对用户名的特殊处理 function maskText(text, showChars = 6, isUsername = false) { if (!text) return ''; // 用户名特殊处理 - 只显示前1/3 if (isUsername) { const showLength = Math.ceil(text.length / 3); if (text.length <= showLength) return text; return `${text.substring(0, showLength)}...`; } // 其他文本使用标准处理 if (text.length <= showChars) return text; return `${text.substring(0, showChars)}...`; } // 隐藏密码 function maskPassword(password) { if (!password) return ''; return '•'.repeat(password.length); } // 页面加载动画 document.addEventListener('DOMContentLoaded', function () { // 默认隐藏验证码提示框 $('#verification-tip-alert').hide(); // 修改动画类添加代码,删除对已删除元素的引用 const elements = [ { selector: '.card', animation: 'animate__fadeIn', delay: 0.2 }, ]; elements.forEach((item) => { const elems = document.querySelectorAll(item.selector); elems.forEach((el, index) => { el.classList.add('animate__animated', item.animation); if (item.delay) { const delay = item.stagger ? item.delay * (index + 1) : item.delay; el.style.animationDelay = `${delay}s`; } }); }); }); // 烟花动画实现 const Fireworks = { canvas: null, ctx: null, particles: [], init: function () { this.canvas = document.getElementById('fireworks-canvas'); this.ctx = this.canvas.getContext('2d'); this.resizeCanvas(); window.addEventListener('resize', () => this.resizeCanvas()); }, resizeCanvas: function () { this.canvas.width = window.innerWidth; this.canvas.height = window.innerHeight; }, start: function () { this.canvas.style.display = 'block'; this.particles = []; // 创建5次烟花,间隔300ms for (let i = 0; i < 5; i++) { setTimeout(() => { const x = Math.random() * this.canvas.width; const y = Math.random() * this.canvas.height * 0.6; this.createParticles(x, y); }, i * 300); } this.animate(); // 5秒后停止动画 setTimeout(() => { this.canvas.style.display = 'none'; }, 5000); }, createParticles: function (x, y) { const colors = ['#ff595e', '#ffca3a', '#8ac926', '#1982c4', '#6a4c93']; for (let i = 0; i < 80; i++) { const particle = { x: x, y: y, size: Math.random() * 4 + 1, color: colors[Math.floor(Math.random() * colors.length)], velocity: { x: (Math.random() - 0.5) * 8, y: (Math.random() - 0.5) * 8, }, alpha: 1, decay: Math.random() * 0.02 + 0.01, }; this.particles.push(particle); } }, animate: function () { if (this.particles.length === 0) return; requestAnimationFrame(() => this.animate()); this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); for (let i = 0; i < this.particles.length; i++) { const p = this.particles[i]; // 添加重力 p.velocity.y += 0.05; // 更新位置 p.x += p.velocity.x; p.y += p.velocity.y; // 减少透明度 p.alpha -= p.decay; // 绘制粒子 this.ctx.save(); this.ctx.globalAlpha = p.alpha; this.ctx.fillStyle = p.color; this.ctx.beginPath(); this.ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); this.ctx.fill(); this.ctx.restore(); // 移除消失的粒子 if (p.alpha <= 0) { this.particles.splice(i, 1); i--; } } }, }; // 初始化烟花 Fireworks.init(); // 绑定表格交互事件 function bindTableEvents() { // 显示/隐藏用户名 $('.toggle-username') .off('click') .on('click', function () { const username = $(this).data('username'); const usernameText = $(this).prev('.username-text'); if (usernameText.text() === username) { usernameText.text(maskText(username, 6, true)); } else { usernameText.text(username); } }); // 显示/隐藏密码 $('.toggle-password') .off('click') .on('click', function () { const password = $(this).data('password'); const passwordText = $(this).prev('.password-text'); if (passwordText.text() === password) { passwordText.text(maskPassword(password)); } else { passwordText.text(password); } }); // 显示/隐藏Token $('.toggle-token') .off('click') .on('click', function () { const token = $(this).data('token'); const tokenText = $(this).prev('.token-text'); if (tokenText.text() === token) { tokenText.text(maskText(token)); } else { tokenText.text(token); } }); // 查看Token按钮 $('.view-token-btn') .off('click') .on('click', function () { const token = $(this).data('token') || ''; const accountId = $(this).attr('data-account-id') || $(this).closest('tr').data('account-id') || Date.now(); // 确保token不为空 if (!token) { showAlert('Token数据为空或无效', 'danger'); return; } // 设置模态框内容 $('#tokenFullText').val(token); // 确保使用DOM原生方法设置属性,避免jQuery缓存问题 document .getElementById('useTokenBtn') .setAttribute('data-account-id', String(accountId)); // 确保每次打开模态框时都重新绑定复制按钮事件 $('#copyTokenBtn') .off('click') .on('click', function () { const textToCopy = $('#tokenFullText').val(); copyToClipboard(textToCopy); }); new bootstrap.Modal(document.getElementById('tokenViewModal')).show(); }); // 复制按钮 $('.copy-btn') .off('click') .on('click', function () { const textToCopy = $(this).data('copy'); copyToClipboard(textToCopy); }); // 获取用量按钮 $('.get-usage-btn') .off('click') .on('click', function () { const email = $(this).data('email'); getAccountUsage(email); }); // 查看使用记录按钮 $('.view-records-btn') .off('click') .on('click', function () { const email = $(this).data('email'); const id = $(this).data('id'); getAccountUsageRecords(email, id); }); // 删除按钮 $('.delete-account-btn') .off('click') .on('click', function () { const email = $(this).data('email'); const id = $(this).data('id'); // 检查ID是否有效 if (!id) { showAlert('无法删除:账号ID无效', 'danger'); return; } // 设置确认对话框的值 $('#deleteEmailConfirm').text(email); $('#deleteIdConfirm').text(id); $('#deleteAccountId').val(id); // 显示删除确认对话框 const deleteModal = new bootstrap.Modal( document.getElementById('deleteConfirmModal') ); deleteModal.show(); }); // 状态操作按钮 $('.status-action') .off('click') .on('click', function (e) { e.preventDefault(); const email = $(this).data('email'); const id = $(this).data('id'); const status = $(this).data('status'); updateAccountStatus(email, id, status); }); } // 更新删除确认按钮事件处理 function deleteAccount() { const accountId = $('#deleteAccountId').val(); const email = $('#deleteEmailConfirm').text(); if (!accountId || accountId === '无') { showAlert('账号ID无效,无法执行删除操作', 'danger'); return; } showLoading('账号删除中...'); // 构建API URL const apiUrl = `/account/id/${accountId}?hard_delete=true`; fetch(apiUrl, { method: 'DELETE', }) .then((res) => res.json()) .then((data) => { $('#deleteConfirmModal').modal('hide'); cleanupModalBackdrops(); // 添加清理 hideLoading(); if (data.success) { showAlert(`账号 ${email} 已成功删除`, 'success'); // 重新加载账号列表 loadAccounts(currentPage, itemsPerPage); } else { showAlert(`删除失败: ${data.message || '未知错误'}`, 'danger'); } }) .catch((error) => { console.error('删除账号时发生错误:', error); $('#deleteConfirmModal').modal('hide'); cleanupModalBackdrops(); // 添加清理 hideLoading(); showAlert('删除账号失败,请稍后重试', 'danger'); }); } // 修改updateAccountStatus函数,确保正确发送请求体 function updateAccountStatus(email, id, status) { showLoading(); // 优先使用ID API,如果ID存在的话 const apiUrl = id ? `/account/id/${id}/status` : `/account/${encodeURIComponent(email)}/status`; fetch(apiUrl, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ status: status }), // 确保这里的字段名是status }) .then((res) => res.json()) .then((data) => { hideLoading(); if (data.success) { let statusText = ''; if (status === 'active') statusText = '正常'; else if (status === 'disabled') statusText = '停用'; else if (status === 'deleted') statusText = '删除'; showAlert( `账号${ id ? '(ID:' + id + ')' : '' } ${email} 已成功设置为${statusText}状态`, 'success' ); loadAccounts(1, itemsPerPage); } else { showAlert(`更新账号状态失败: ${data.message || '未知错误'}`, 'danger'); } }) .catch((error) => { console.error('更新账号状态时发生错误:', error); hideLoading(); showAlert('更新账号状态失败,请稍后重试', 'danger'); }); } // 完全重构额度显示函数,精确匹配参考代码 function renderUsageProgress(usageLimit) { // 计算使用进度 const premiumUsed = 150 - usageLimit; const premiumTotal = 150; const premiumRemaining = premiumTotal - premiumUsed; const premiumPercent = Math.round((premiumUsed / premiumTotal) * 100); return `
    ${premiumUsed} / ${premiumTotal} (剩余: ${premiumRemaining})
    ${premiumPercent}%
    `; } // 修改Token列的渲染方式 function renderTokenColumn(token, accountId, email) { // 确保所有参数都有默认值,防止undefined const safeToken = token || ''; const safeAccountId = accountId || Date.now(); const safeEmail = email || ''; return ` `; } // 加载配置函数 function loadConfig() { showLoading(); $.ajax({ url: '/config', method: 'GET', success: function (response) { if (response.success) { const config = response.data; // 现有字段设置... // 设置代理配置 $('#use-proxy').prop('checked', config.USE_PROXY === 'True'); $('#proxy-type').val(config.PROXY_TYPE || 'http'); $('#proxy-host').val(config.PROXY_HOST || ''); $('#proxy-port').val(config.PROXY_PORT || ''); $('#proxy-timeout').val(config.PROXY_TIMEOUT || '10'); $('#proxy-username').val(config.PROXY_USERNAME || ''); $('#proxy-password').val(config.PROXY_PASSWORD || ''); // 触发动态UA的change事件 $('#dynamic-useragent').trigger('change'); // 根据是否启用代理来显示/隐藏代理设置 toggleProxySettings(); $('#browser-useragent').val(config.BROWSER_USER_AGENT); $('#accounts-limit').val(config.MAX_ACCOUNTS); $('#captcha-method').val(config.EMAIL_CODE_TYPE || 'API'); $('#email-domains').val(config.EMAIL_DOMAINS); $('#email-username').val(config.EMAIL_USERNAME); $('#email-pin').val(config.EMAIL_PIN); $('#browser-path').val(config.BROWSER_PATH || ''); $('#cursor-path').val(config.CURSOR_PATH || ''); if (config.EMAIL_DOMAIN) { // 获取第一个域名作为示例 const firstDomain = config.EMAIL_DOMAIN; // 更新输入框提示 $('#email-username').attr( 'placeholder', `仅输入用户名部分,例如:ddcat28(完整地址将是 ddcat28@${firstDomain})` ); // 添加域名显示标签 if (!$('#email-domain-suffix').length) { $('#email-username').after( `@${firstDomain}` ); // 将输入框和域名标签包装在input-group中 $('#email-username, #email-domain-suffix').wrapAll( '
    ' ); } else { $('#email-domain-suffix').text(`@${firstDomain}`); } } $('#email-type').val(config.EMAIL_TYPE); $('#email-proxy-enabled').prop( 'checked', config.EMAIL_PROXY_ENABLED || false ); if (config.EMAIL_PROXY_ENABLED) { $('#email-proxy-address').val(config.EMAIL_PROXY_ADDRESS); $('#email-api').val(config.EMAIL_API); } if (config.EMAIL_TYPE == 'tempemail') { $('#tempemail-fields').show(); $('#zmail-fields').hide(); } else if (config.EMAIL_TYPE == 'zmail') { $('#tempemail-fields').hide(); $('#zmail-fields').show(); } // 配置加载完毕后,立即更新任务状态显示 checkTaskStatus(); hideLoading(); } else { showAlert('danger', '加载配置失败: ' + response.message); hideLoading(); } }, error: function (xhr) { hideLoading(); showAlert('danger', '加载配置失败: ' + xhr.statusText); }, }); } // 添加代理设置的显示/隐藏控制 function toggleProxySettings() { if ($('#use-proxy').is(':checked')) { $('#proxy-settings').show(); } else { $('#proxy-settings').hide(); } } // 添加配置保存回调,支持重启 function saveConfig() { showLoading(); // 修复:不依赖this上下文,直接获取动态UA的状态 const isDynamicUA = $('#dynamic-useragent').is(':checked'); const configData = { BROWSER_HEADLESS: $('#browser-headless').val() === 'true', DYNAMIC_USERAGENT: isDynamicUA, BROWSER_USER_AGENT: isDynamicUA ? '' : $('#browser-useragent').val(), MAX_ACCOUNTS: parseInt($('#accounts-limit').val()), EMAIL_CODE_TYPE: $('#captcha-method').val(), EMAIL_DOMAINS: $('#email-domains').val(), EMAIL_USERNAME: $('#email-username').val(), EMAIL_PIN: $('#email-pin').val(), BROWSER_PATH: $('#browser-path').val(), CURSOR_PATH: $('#cursor-path').val(), // 代理设置(确保这些字段存在) USE_PROXY: $('#use-proxy').is(':checked'), PROXY_TYPE: $('#proxy-type').val(), PROXY_HOST: $('#proxy-host').val(), PROXY_PORT: $('#proxy-port').val(), PROXY_TIMEOUT: parseInt($('#proxy-timeout').val()) || 10, PROXY_USERNAME: $('#proxy-username').val(), PROXY_PASSWORD: $('#proxy-password').val(), }; $.ajax({ url: '/config', method: 'POST', contentType: 'application/json', data: JSON.stringify(configData), success: function (response) { hideLoading(); if (response.success) { // 保存成功后立即更新任务状态显示 checkTaskStatus(); // 添加重启询问提示 showConfirmDialog( '配置已成功保存', '需要重启服务才能使更改生效。是否立即重启服务?', function () { // 确认重启 restartService(); } ); enableConfigForm(false); } else { showAlert('danger', '保存配置失败: ' + response.message); } }, error: function (xhr) { hideLoading(); showAlert('danger', '保存配置失败: ' + xhr.statusText); }, }); } // 添加确认对话框函数 function showConfirmDialog(title, message, confirmCallback) { // 如果已存在对话框,先移除 if ($('#confirm-dialog').length) { $('#confirm-dialog').remove(); } const dialogHTML = ` `; $('body').append(dialogHTML); const modal = new bootstrap.Modal(document.getElementById('confirm-dialog')); modal.show(); $('#confirm-yes').click(function () { modal.hide(); if (typeof confirmCallback === 'function') { confirmCallback(); } }); } // 更新重启服务函数 function restartService() { showLoading('服务正在重新配置,请稍候...'); $.ajax({ url: '/restart', method: 'POST', success: function (response) { if (response.success) { // 显示成功消息 hideLoading(); showAlert( 'success', response.message || '服务配置已更新,正在刷新页面...' ); // 延迟3秒后刷新页面 setTimeout(function () { window.location.reload(); }, 3000); } else { hideLoading(); showAlert( 'danger', '重启服务失败: ' + (response.message || '未知错误') ); } }, error: function (xhr) { hideLoading(); showAlert('danger', '重启服务请求失败,请手动刷新页面'); // 延迟5秒后尝试刷新页面 setTimeout(function () { window.location.reload(); }, 5000); }, }); } // 启用/禁用配置表单 function enableConfigForm(enable) { const inputs = $('#config-form select, #config-form input'); if (enable) { inputs.prop('disabled', false); // 如果动态UA已启用,保持UA输入框禁用 if ($('#dynamic-useragent').prop('checked')) { $('#browser-useragent').prop('disabled', true); } // 显示按钮容器而不是单个按钮 $('#config-actions').show(); $('#edit-config-btn').hide(); } else { inputs.prop('disabled', true); // 隐藏按钮容器 $('#config-actions').hide(); $('#edit-config-btn').show(); } } // 动态User-Agent切换逻辑 $('#dynamic-useragent').change(function () { const isDynamicUA = $(this).prop('checked'); if (isDynamicUA) { $('#browser-useragent').prop('disabled', true); $('#useragent-input-container').addClass('text-muted'); } else { // 只有在编辑模式下才启用输入框 const isEditMode = !$('#edit-config-btn').is(':visible'); $('#browser-useragent').prop('disabled', !isEditMode); $('#useragent-input-container').removeClass('text-muted'); } }); // 修改任务状态显示函数,保留状态处理逻辑 function updateTaskStatusDisplay(statusData) { // 获取UI元素引用 const statusBadge = $('#registration-status'); const taskStatusText = $('#task-status-text'); const taskIcon = $('#task-status i'); // 直接使用服务器返回的统计数据 const stats = statusData; // 计算实际使用的账号数量 const usedCount = stats.active_count || 0; const maxAccounts = stats.max_accounts || 10; const remainingSlots = Math.max(0, maxAccounts - usedCount); // 更新显示 $('#current-count').text(usedCount); $('#max-accounts').text(maxAccounts); $('#remaining-slots').text(`剩余: ${remainingSlots}`); // 计算使用百分比 const usagePercent = maxAccounts > 0 ? Math.round((usedCount / maxAccounts) * 100) : 0; // 更新进度条 $('.battery-progress').attr('data-percent', usagePercent); $('.battery-percent').text(`${usagePercent}%`); // 更新任务详情 if (statusData.registration_details) { const details = statusData.registration_details; // 更新统计信息 if (details.statistics) { $('#total-runs').text(details.statistics.total_runs); $('#successful-runs').text(details.statistics.successful_runs); $('#failed-runs').text(details.statistics.failed_runs); $('#success-rate').text(details.statistics.success_rate); } } // 根据任务状态更新UI switch (statusData.task_status) { case 'running': statusBadge .removeClass('bg-success bg-warning bg-danger') .addClass('bg-primary'); statusBadge.text('运行中'); taskStatusText.text(statusData.status_message || '任务正在运行中'); taskIcon .removeClass('fa-check-circle fa-pause-circle fa-times-circle') .addClass('fa-spinner fa-spin'); taskIcon .removeClass('text-success text-warning text-danger') .addClass('text-primary'); // 显示/隐藏按钮 $('#start-registration').hide(); $('#stop-registration').show(); $('#registration-details').show(); break; case 'stopped': default: statusBadge .removeClass('bg-primary bg-warning bg-danger') .addClass('bg-success'); statusBadge.text('空闲中'); taskStatusText.text( statusData.status_message || '系统空闲中,可以开始新任务' ); taskIcon .removeClass('fa-spinner fa-spin fa-pause-circle fa-times-circle') .addClass('fa-check-circle'); taskIcon .removeClass('text-primary text-warning text-danger') .addClass('text-success'); // 显示/隐藏按钮 $('#start-registration').show(); $('#stop-registration').hide(); $('#registration-details').hide(); break; } } // 绑定排序事件 function bindSortEvents() { // 字段排序变化 $('#sort-field').change(function () { currentSortField = $(this).val(); loadAccounts( 1, itemsPerPage, $('#search-input').val(), currentSortField, currentSortOrder ); }); // 排序方向变化 $('#sort-order').change(function () { currentSortOrder = $(this).val(); loadAccounts( 1, itemsPerPage, $('#search-input').val(), currentSortField, currentSortOrder ); }); } // 修改表头排序配置,移除ID相关设置 function addTableHeaderSorting() { // 可排序的列 - 移除ID相关配置 const sortableColumns = { 'th-email': 'email', 'th-date': 'created_at', 'th-usage': 'usage_limit', }; // 为表头添加排序类和点击事件 Object.keys(sortableColumns).forEach((thId) => { const $th = $(`#${thId}`); $th.addClass('sortable'); // 设置初始排序指示 if (sortableColumns[thId] === currentSortField) { $th.addClass(currentSortOrder); } $th.click(function () { const field = sortableColumns[thId]; // 如果点击当前排序列,切换排序方向 if (field === currentSortField) { currentSortOrder = currentSortOrder === 'asc' ? 'desc' : 'asc'; } else { // 否则,切换排序列并设置默认为降序 currentSortField = field; currentSortOrder = 'desc'; } // 更新排序控件 $('#sort-field').val(currentSortField); $('#sort-order').val(currentSortOrder); // 重新加载数据 loadAccounts( 1, itemsPerPage, $('#search-input').val(), currentSortField, currentSortOrder ); }); }); } // 设置定时任务刷新 function setupTaskRefresh() { // 清除可能存在的旧定时器 if (refreshTimer) { clearInterval(refreshTimer); } // 设置新的定时刷新 refreshTimer = setInterval(function () { // 检查任务状态 checkTaskStatus(); // 如果在账号管理页面,刷新账号列表 if ($('#tasks-accounts').hasClass('active')) { // 静默刷新,不显示loading框 loadAccounts( currentPage, itemsPerPage, $('#search-input').val(), currentSortField, currentSortOrder, false ); // 更新最后刷新时间 updateLastRefreshTime(); } }, REFRESH_INTERVAL); // 初始加载任务状态 checkTaskStatus(); } // 更新最后刷新时间的函数 function updateLastRefreshTime() { const now = new Date(); const timeString = now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0') + ':' + now.getSeconds().toString().padStart(2, '0'); $('#last-update-time').text(timeString); } // 检查任务状态 function checkTaskStatus() { fetch('/registration/status') .then((response) => response.json()) .then((data) => { // 确保有账号统计数据 updateTaskStatusDisplay(data); // 更新任务运行时间和下次运行时间 let registration_details = data.registration_details; if (registration_details.last_run) { $('#last-run').text(formatDateTime(data.registration_details.last_run)); } if (data.registration_details.next_run) { const nextRunTime = new Date(registration_details.next_run * 1000); const now = new Date(); const timeLeft = Math.max(0, Math.floor((nextRunTime - now) / 1000)); if (timeLeft > 0) { $('#next-run').text( `${formatDateTime( registration_details.next_run * 1000 )} (还有${formatTimeLeft(timeLeft)})` ); } else { $('#next-run').text( `${formatDateTime(registration_details.next_run * 1000)}` ); } } else { $('#next-run').text('未排程'); } // 更新注册进度和消息 if (registration_details.registration_progress) { $('#registration-progress').text( registration_details.registration_progress ); } if (registration_details.registration_message) { $('#registration-message').text( registration_details.registration_message ); } }) .catch((error) => { console.error('获取任务状态出错:', error); }); } // 格式化日期时间 function formatDateTime(timestamp) { const date = new Date(timestamp); return date.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false, }); } // 格式化剩余时间 function formatTimeLeft(seconds) { if (seconds < 60) { return `${seconds}秒`; } else if (seconds < 3600) { return `${Math.floor(seconds / 60)}分${seconds % 60}秒`; } else { const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); return `${hours}小时${minutes}分`; } } // 添加重置机器ID函数 function resetMachineId() { showLoading(); $.ajax({ url: '/reset-machine', method: 'GET', success: function (response) { hideLoading(); if (response.success) { showAlert('success', '成功重置机器ID。' + (response.message || '')); // 询问是否需要重启服务以应用更改 setTimeout(function () { showConfirmDialog( '重启服务', '机器ID已重置,建议重启服务以确保更改生效。是否立即重启?', function () { restartService(); } ); }, 1000); } else { showAlert( 'danger', '重置机器ID失败: ' + (response.message || '未知错误') ); } }, error: function (xhr) { hideLoading(); showAlert( 'danger', '重置机器ID失败: ' + (xhr.responseJSON?.message || xhr.statusText || '未知错误') ); }, }); } // 导出账号函数 function exportAccounts() { showLoading(); // 直接使用浏览器下载功能 const downloadLink = document.createElement('a'); downloadLink.href = '/accounts/export'; downloadLink.download = 'cursor_accounts.json'; document.body.appendChild(downloadLink); downloadLink.click(); document.body.removeChild(downloadLink); setTimeout(() => { hideLoading(); showAlert('success', '账号导出请求已发送,文件将自动下载'); }, 1000); } // 导入账号函数 function importAccounts(file) { showLoading(); const formData = new FormData(); formData.append('file', file); // 发送导入请求 $.ajax({ url: '/accounts/import', method: 'POST', data: formData, processData: false, contentType: false, success: function (response) { hideLoading(); if (response.success) { showAlert('success', response.message); // 刷新账号列表 loadAccounts(1, itemsPerPage); } else { showAlert('danger', '导入账号失败: ' + response.message); } }, error: function (xhr) { hideLoading(); showAlert( 'danger', '导入账号失败: ' + (xhr.responseJSON?.detail || xhr.statusText) ); }, }); } // 获取账号使用记录 function getAccountUsageRecords(email, id) { showLoading(); // 设置模态框中的账号邮箱 $('#recordEmail').text(email); // 清空记录列表 $('#usageRecordBody').empty(); fetch(`/account/${id}/usage-records`) .then((response) => response.json()) .then((data) => { hideLoading(); if (data.success) { const records = data.records; if (records && records.length > 0) { // 隐藏无记录提示 $('#no-records').hide(); // 显示记录 records.forEach((record) => { const row = ` ${formatDateTime(record.created_at)} ${record.ip || '-'} ${record.user_agent || '-'} `; $('#usageRecordBody').append(row); }); } else { // 显示无记录提示 $('#usageRecordBody').empty(); $('#no-records').show(); } // 显示模态框 new bootstrap.Modal(document.getElementById('usageRecordModal')).show(); } else { showAlert(`获取使用记录失败: ${data.message || '未知错误'}`, 'danger'); } }) .catch((error) => { console.error('获取使用记录时发生错误:', error); hideLoading(); showAlert('获取使用记录失败,请稍后重试', 'danger'); }); } // 使用自定义邮箱注册 function registerWithCustomEmail() { const email = $('#custom-email').val().trim(); if (!email) { showCustomRegistrationStatus('请输入邮箱地址', 'danger'); return; } // 验证邮箱格式 if (!validateEmail(email)) { showCustomRegistrationStatus('邮箱格式不正确', 'danger'); return; } showCustomRegistrationStatus('注册中,请稍候...', 'info'); $('#custom-registration').prop('disabled', true); // 设置全局标记,指示当前是自定义邮箱注册场景 window.customEmailRegistration = true; // 启动验证码检查前清除任何可能的提示 $('#verification-tip-alert').hide(); // 短暂延迟后再启动验证码检查,确保UI已更新 setTimeout(() => { // 启动验证码检查 startVerificationCodeCheck(); }, 500); // 调用API $.ajax({ url: '/registration/custom', method: 'POST', contentType: 'application/json', data: JSON.stringify({ email: email }), success: function (response) { if (response.success) { showCustomRegistrationStatus('注册成功!', 'success'); // 重置表单 $('#custom-email').val(''); // 刷新账号列表 setTimeout(function () { loadAccounts(1, itemsPerPage); // 注册成功后停止验证码检查 stopVerificationCodeCheck(); // 清除自定义邮箱注册标记 window.customEmailRegistration = false; }, 2000); } else { showCustomRegistrationStatus('注册失败: ' + response.message, 'danger'); // 注册失败后停止验证码检查 stopVerificationCodeCheck(); // 清除自定义邮箱注册标记 window.customEmailRegistration = false; } $('#custom-registration').prop('disabled', false); }, error: function (xhr) { let message = '注册失败'; try { const response = JSON.parse(xhr.responseText); message = response.message || '未知错误'; } catch (e) { message = xhr.statusText || '服务器错误'; } showCustomRegistrationStatus('注册失败: ' + message, 'danger'); $('#custom-registration').prop('disabled', false); // 注册错误后停止验证码检查 stopVerificationCodeCheck(); // 清除自定义邮箱注册标记 window.customEmailRegistration = false; }, }); } // 显示自定义注册状态 function showCustomRegistrationStatus(message, type) { const statusDiv = $('#custom-registration-status'); const messageSpan = $('#custom-registration-message'); statusDiv .removeClass('alert-info alert-success alert-danger') .addClass('alert-' + type) .show(); messageSpan.text(message); if (type === 'success') { setTimeout(function () { statusDiv.hide(); }, 5000); } } // 验证邮箱格式 function validateEmail(email) { const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return re.test(String(email).toLowerCase()); } // 绑定模态框内按钮事件 function bindModalEvents() { // 使用Token按钮 - 使用节流处理 $('#useTokenBtn') .off('click') .on( 'click', throttle(function () { // 直接从DOM元素获取属性 const accountId = document .getElementById('useTokenBtn') .getAttribute('data-account-id') || ''; if (!accountId) { showAlert('无法获取账号ID,请刷新页面后重试', 'danger'); return; } showLoading('正在使用Token...'); fetch(`/account/use-token/${accountId}`, { method: 'POST', }) .then((res) => { if (!res.ok) { throw new Error(`服务器返回状态码: ${res.status}`); } return res.json(); }) .then((data) => { hideLoading(); if (data.success) { showAlert(data.message, 'success'); $('#tokenViewModal').modal('hide'); cleanupModalBackdrops(); setTimeout(() => fetchAccounts(), 1000); } else { showAlert( `使用Token失败: ${data.message || '未知错误'}`, 'danger' ); } }) .catch((error) => { console.error('使用Token时发生错误:', error); hideLoading(); showAlert(`使用Token失败: ${error.message}`, 'danger'); }); }, 1000) ); // 监听模态框隐藏事件 $('.modal') .off('hidden.bs.modal') .on('hidden.bs.modal', function () { // 延迟调用清理,避免与Bootstrap自身的处理冲突 setTimeout(() => cleanupModalBackdrops(), 50); }); // 监听复制Token按钮 - 使用节流处理 $('#copyTokenBtn') .off('click') .on( 'click', throttle(function () { const token = $('#tokenFullText').val(); if (token) { copyToClipboard(token); showAlert('Token已复制到剪贴板', 'success'); } else { showAlert('没有Token可复制', 'warning'); } }, 300) ); } // 验证码请求检查 let verificationCheckTimer; function startVerificationCodeCheck() { // 清除可能存在的计时器 if (verificationCheckTimer) { clearInterval(verificationCheckTimer); } // 获取当前操作模式 - 检查是否在任务注册页面或自定义邮箱注册 const isTaskRegistration = $('#task-status').length > 0 && $('#task-status').is(':visible') && !window.customEmailRegistration; const isCustomEmailRegistration = window.customEmailRegistration === true; // 根据不同场景显示不同的提示文案 if (isTaskRegistration) { // 任务注册场景 $('#verification-tip-content').text( '在任务注册过程中,如果自动获取验证码失败,系统将自动转为手动输入模式,请留意弹窗提示并及时输入验证码。' ); } else if (isCustomEmailRegistration) { // 自定义邮箱注册场景 $('#verification-tip-content').text( '使用自定义邮箱注册时,需要手动输入验证码,请检查您的邮箱并在弹窗中及时输入验证码。' ); } else { // 默认场景 $('#verification-tip-content').text( '在注册过程中,系统将请求邮箱验证码。如需手动输入验证码,请留意弹窗提示并及时输入。' ); } // 显示验证码提示框 $('#verification-tip-alert').show(); // 每5秒检查一次是否有等待验证码输入的请求 verificationCheckTimer = setInterval(checkPendingVerification, 5000); } function stopVerificationCodeCheck() { if (verificationCheckTimer) { clearInterval(verificationCheckTimer); verificationCheckTimer = null; // 隐藏验证码提示框 $('#verification-tip-alert').hide(); // 清除自定义邮箱注册标记 window.customEmailRegistration = false; // 调用后端接口清理所有待处理的验证码请求 fetch('/verification/clear') .then((response) => response.json()) .then((data) => { if (data.success) { console.log('已清理待处理的验证码请求:', data.message); // 如果当前有打开的验证码输入弹窗,关闭它 if ($('#codeInputModal').hasClass('show')) { const modalElement = document.getElementById('codeInputModal'); const modalInstance = bootstrap.Modal.getInstance(modalElement); if (modalInstance) { modalInstance.hide(); setTimeout(() => cleanupModalBackdrops(), 300); } } } }) .catch((error) => { console.error('清理验证码请求失败:', error); }); } } function checkPendingVerification() { // 如果验证码模态框已经打开,不要重复检查和打开 if ($('#codeInputModal').hasClass('show')) { return; } fetch('/verification/pending') .then((response) => { if (!response.ok) { throw new Error(`服务器返回状态码: ${response.status}`); } return response.json(); }) .then((data) => { if (data.success && data.data && data.data.length > 0) { // 检查是否有失败的验证码请求 const failedRequest = data.data.find((req) => req.status === 'failed'); if (failedRequest) { // 显示验证失败信息并更新内容,但不停止验证码检查,因为系统会转为手动模式 showAlert( `自动获取验证码失败: ${ failedRequest.message || '请检查邮箱设置' }。系统已自动转为手动输入模式,请等待验证码输入弹窗。`, 'warning' ); return; } // 处理正常的验证码请求 const pendingRequest = data.data[0]; // 取第一个请求 // 确保没有其他模态框正在显示 if (!$('.modal.show').length) { showVerificationModal(pendingRequest); } else { // 延迟500ms后重试 setTimeout(checkPendingVerification, 500); } } }) .catch((error) => { console.error('检查验证码请求失败:', error); // 错误后延长重试时间 clearTimeout(verificationCheckTimer); verificationCheckTimer = setTimeout(checkPendingVerification, 10000); }); } function showVerificationModal(pendingRequest) { try { // 检查pendingRequest是否有效 if (!pendingRequest || !pendingRequest.email || !pendingRequest.id) { console.error('无效的验证请求:', pendingRequest); return; } // 设置邮箱显示 $('#verificationEmailDisplay').text(pendingRequest.email); // 设置请求ID $('#pendingEmailId').val(pendingRequest.id); // 清空验证码输入框 $('#verificationCode').val(''); // 判断是否是自动失败后的手动输入请求 const isAutoFailureCase = pendingRequest.hasOwnProperty('auto_failure') && pendingRequest.auto_failure === true; // 判断是否是任务注册场景或自定义邮箱注册场景 const isTaskRegistration = $('#task-status').length > 0 && $('#task-status').is(':visible') && !window.customEmailRegistration; const isCustomEmailRegistration = window.customEmailRegistration === true || (pendingRequest.email.includes('@') && !pendingRequest.email.includes('tempmail.plus') && !pendingRequest.email.includes('zmail.plus')); // 设置模态框标题和内容,根据场景提供不同提示 if (isTaskRegistration && isAutoFailureCase) { // 任务注册场景 - 自动获取失败后的手动输入 $('#codeInputModalLabel').text('任务注册 - 手动输入验证码'); $('#verification-message').html( '
    自动获取验证码失败,请手动输入验证码以继续注册流程。
    ' ); $('#verification-code-hint').text( '请检查邮箱中的验证邮件,通常验证码为6位数字,位于邮件正文中。' ); } else if (isTaskRegistration) { // 任务注册场景 - 普通手动输入 $('#codeInputModalLabel').text('任务注册 - 输入验证码'); $('#verification-message').html(''); $('#verification-code-hint').text( '通常验证码为6位数字,在邮件正文中可以找到。' ); } else if (isCustomEmailRegistration) { // 自定义邮箱注册场景 $('#codeInputModalLabel').text('自定义邮箱注册 - 输入验证码'); $('#verification-message').html( '
    请查看您的邮箱,输入收到的验证码完成注册。
    ' ); $('#verification-code-hint').text( '请检查您的邮箱收件箱及垃圾邮件文件夹,验证码通常为6位数字。' ); } else { // 默认场景 $('#codeInputModalLabel').text('请输入验证码'); $('#verification-message').html(''); $('#verification-code-hint').text('通常验证码为6位数字,在邮件正文中'); } // 显示模态框前先清理可能存在的背景 cleanupModalBackdrops(); // 显示模态框 const codeModal = new bootstrap.Modal( document.getElementById('codeInputModal') ); codeModal.show(); // 聚焦到验证码输入框 setTimeout(() => { $('#verificationCode').focus(); }, 500); // 显示提示 showAlert(`请为邮箱 ${pendingRequest.email} 输入验证码`, 'info'); } catch (error) { console.error('显示验证码模态框时发生错误:', error); } } // 绑定验证码相关事件 function bindVerificationEvents() { // 提交验证码按钮 $('#submitCodeBtn').click(function () { submitVerificationCode(); }); // 验证码输入框回车事件 $('#verificationCode').keypress(function (e) { if (e.which === 13) { submitVerificationCode(); } }); // 模态框关闭事件 $('#codeInputModal').on('hidden.bs.modal', function () { // 清空数据 $('#verificationEmailDisplay').text(''); $('#pendingEmailId').val(''); $('#verificationCode').val(''); // 清理背景 cleanupModalBackdrops(); }); } // 清理Bootstrap模态框背景遮罩和相关样式 - 增强可靠性 function cleanupModalBackdrops() { try { // 立即执行一次基本清理 const immediateBackdrops = $('.modal-backdrop'); // 如果找到超过1个背景,立即移除多余的 if (immediateBackdrops.length > 1) { // 保留第一个,移除其他 immediateBackdrops.slice(1).remove(); } // 再等待Bootstrap模态框动画完成后彻底清理 setTimeout(() => { try { // 检查是否还有显示中的模态框 const visibleModals = $('.modal.show').length; // 只有在没有显示中的模态框时才移除所有背景 if (visibleModals === 0) { // 获取所有模态框背景 const backdrops = $('.modal-backdrop'); if (backdrops.length > 0) { backdrops.remove(); // 重置body的样式 $('body').removeClass('modal-open'); $('body').css('overflow', ''); $('body').css('padding-right', ''); // 额外确保任何可能的内联样式都被移除 $('body').attr('style', ''); } } else { // 即使有模态框显示,也确保只有一个背景 const backdrops = $('.modal-backdrop'); if (backdrops.length > 1) { backdrops.slice(1).remove(); } } // 确保文档滚动正常 $(document).off('scroll.bs.modal'); } catch (error) { console.error('延迟清理背景时发生错误:', error); } }, 300); // 延迟确保在Bootstrap的模态框关闭动画完成后执行 } catch (error) { console.error('清理模态框背景时发生错误:', error); // 尝试最基本的清理 try { $('.modal-backdrop').remove(); $('body') .removeClass('modal-open') .css('overflow', '') .css('padding-right', ''); } catch (e) { console.error('紧急清理模态框背景时发生错误:', e); } } } // 修改submitVerificationCode函数使用通用清理方法 function submitVerificationCode() { const code = $('#verificationCode').val().trim(); const id = $('#pendingEmailId').val(); if (!code) { showAlert('请输入验证码', 'warning'); return; } if (!id) { showAlert('验证请求ID无效', 'danger'); return; } // 显示加载状态并防止重复提交 if ($('#submitCodeBtn').prop('disabled')) { return; } $('#submitCodeBtn') .prop('disabled', true) .html(' 提交中...'); // 提交验证码 fetch('/verification/submit', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ id: id, code: code, }), }) .then((response) => { if (!response.ok) { throw new Error(`服务器返回状态码: ${response.status}`); } return response.json(); }) .then((data) => { if (data.success) { showAlert(`验证码已成功提交 (${code})`, 'success'); // 先记录模态框实例以便后续使用 const modalElement = document.getElementById('codeInputModal'); const modalInstance = bootstrap.Modal.getInstance(modalElement); if (modalInstance) { // 清空数据,防止重复提交 $('#verificationEmailDisplay').text(''); $('#pendingEmailId').val(''); $('#verificationCode').val(''); // 关闭模态框并清理背景 modalInstance.hide(); setTimeout(() => cleanupModalBackdrops(), 300); // 如果验证成功,可以重新加载账号列表 showAlert('等待账号注册完成...', 'info'); setTimeout(fetchAccounts, 10000); // 增加等待时间到10秒 // 任务可能已经完成,检查是否需要隐藏提示 fetch('/registration/status') .then((res) => res.json()) .then((status) => { if (status.task_status === 'stopped') { $('#verification-tip-alert').hide(); stopVerificationCodeCheck(); } }) .catch((error) => console.error('获取任务状态失败:', error)); } } else { showAlert(`提交验证码失败: ${data.message || '未知错误'}`, 'danger'); } }) .catch((error) => { console.error('提交验证码失败:', error); showAlert(`提交验证码失败: ${error.message}`, 'danger'); }) .finally(() => { // 恢复按钮状态,延迟恢复防止快速点击 setTimeout(() => { $('#submitCodeBtn').prop('disabled', false).html('提交验证码'); }, 1000); }); } // 打开删除确认模态框 function confirmDeleteAccount(accountId) { $('#deleteAccountId').val(accountId); const modal = new bootstrap.Modal( document.getElementById('deleteConfirmModal') ); modal.show(); } // 获取账号列表 function fetchAccounts() { showLoading('加载账号数据中...'); // 构建查询参数 const queryParams = new URLSearchParams({ page: currentPage, per_page: itemsPerPage, sort_field: currentSortField, sort_order: currentSortOrder, }); // 如果有搜索词,添加到查询参数 const searchTerm = $('#search-input').val().trim(); if (searchTerm) { queryParams.append('search', searchTerm); } // 发起请求 fetch(`/accounts?${queryParams.toString()}`) .then((response) => { if (!response.ok) { throw new Error(`服务器返回状态码: ${response.status}`); } return response.json(); }) .then((data) => { if (data.success) { // 保存账号数据 accounts = data.data || []; // 关闭"等待账号注册完成..."的提示 $('.alert-info:contains("等待账号注册完成")').alert('close'); // 直接更新账号表格,不需要额外的过滤和排序 updateAccountsTable(accounts); // 更新分页信息 updatePagination(data.pagination.page, data.pagination.total_pages); $('#total-accounts').text(data.pagination.total_count); } else { showAlert( '加载账号失败: ' + (data.message || '服务器返回错误'), 'danger' ); console.error('加载账号数据失败:', data); } }) .catch((error) => { console.error('获取账号列表时发生错误:', error); showAlert('加载账号失败: ' + error.message, 'danger'); // 如果是网络错误,显示更详细的提示 if ( error.name === 'TypeError' && error.message.includes('Failed to fetch') ) { showAlert('网络连接错误,请检查服务是否正常运行', 'danger', true); } }) .finally(() => { hideLoading(); }); }