会员 R币

会员权益对比

购买须知

R币充值

当前余额
0 R币
💰 充值汇率:1元 = 100 R币

选择充值金额

¥10
1,000 R币
¥50
5,000 R币
¥200
20,000 R币
¥500
50,000 R币
自定义
输入金额

支付方式

充值说明

账号登录

daily_reward: 500, concurrent_threads: 8, image_save_days: 0, video_save_days: 30, features: { watermark: true, priority_queue: true, exclusive_models: true, api_access: true } } ] }; // 页面加载 // 页面加载前立即同步登录状态 (function syncLoginState() { const userId = localStorage.getItem('user_id'); const userToken = localStorage.getItem('user_token'); const userData = localStorage.getItem('user_data'); if (userId && userId !== 'null' && userId !== 'undefined') { localStorage.setItem('user_logged_in', '1'); localStorage.setItem('user_id', userId); } else if (userData && userData !== 'null' && userData !== '{}') { try { const parsed = JSON.parse(userData); if (parsed.user_id) { localStorage.setItem('user_logged_in', '1'); localStorage.setItem('user_id', parsed.user_id); localStorage.setItem('user_id', parsed.user_id); } } catch(e) {} } else if (userToken && userToken !== 'null' && userToken !== 'undefined') { localStorage.setItem('user_logged_in', '1'); } })(); document.addEventListener('DOMContentLoaded', async function() { // 清除可能残留的错误提示 const existingToasts = document.querySelectorAll('.toast'); existingToasts.forEach(t => t.remove()); // 检查用户登录状态并更新头像 await checkLoginState(); await loadMemberConfig(); renderBenefitsTable(); }); // ========================================== // 登录/注册系统(与首页一致) // ========================================== const AUTH_API_URL = '/api/user'; // 检查是否已登录(多重检测,确保健壮性) function isLoggedIn() { // 检查 localStorage 中的登录标志 if (localStorage.getItem('user_logged_in') === '1') { return true; } // 检查 localStorage 中的 token(排除无效值) const token = localStorage.getItem('user_token'); if (token && token !== 'null' && token !== 'undefined' && token !== '') { return true; } // 检查 user_id 是否存在(排除无效值) const userId = localStorage.getItem('user_id') || localStorage.getItem('user_id'); if (userId && userId !== 'null' && userId !== 'undefined' && userId !== '') { localStorage.setItem('user_logged_in', '1'); return true; } // 检查 user_data 是否存在且有效 const userData = localStorage.getItem('user_data'); if (userData && userData !== 'null' && userData !== '{}') { try { const parsed = JSON.parse(userData); if (parsed.user_id) { localStorage.setItem('user_logged_in', '1'); localStorage.setItem('user_id', parsed.user_id); localStorage.setItem('user_id', parsed.user_id); return true; } } catch(e) {} } return false; } // 检查登录状态并更新UI async function checkLoginState() { const authModal = document.getElementById('authModal'); const authModalOverlay = document.getElementById('authModalOverlay'); const authCloseBtn = document.getElementById('authCloseBtn'); const avatarBtn = document.getElementById('userAvatarBtn'); const avatarImg = document.getElementById('userAvatarImg'); if (!authModal || !avatarBtn) return; // 点击头像打开弹窗(与首页一致) avatarBtn.onclick = function() { authModal.classList.add('active'); if (isLoggedIn()) { showUserInfoForm(); } else { showLoginForm(); } }; // 关闭弹窗 authCloseBtn.addEventListener('click', () => { authModal.classList.remove('active'); }); authModalOverlay.addEventListener('click', () => { authModal.classList.remove('active'); }); // 登录 document.getElementById('loginBtn')?.addEventListener('click', handleLogin); // 注册 document.getElementById('registerBtn')?.addEventListener('click', handleRegister); // 发送验证码 document.getElementById('sendCodeBtn')?.addEventListener('click', () => sendCode('register')); document.getElementById('forgotCodeBtn')?.addEventListener('click', () => sendCode('reset')); // 重置密码 document.getElementById('resetPasswordBtn')?.addEventListener('click', handleResetPassword); // 退出登录 document.getElementById('logoutBtn')?.addEventListener('click', handleLogout); // 回车登录 document.getElementById('loginPassword')?.addEventListener('keydown', (e) => { if (e.key === 'Enter') handleLogin(); }); // 如果已登录,更新头像 if (isLoggedIn()) { await updateUIForLoggedIn(); } } // 登录后更新UI async function updateUIForLoggedIn() { const userData = await fetchUserInfo(); const avatarImg = document.getElementById('userAvatarImg'); const avatarBtn = document.getElementById('userAvatarBtn'); if (avatarImg && userData.avatar) { avatarImg.src = userData.avatar; } if (avatarBtn) { avatarBtn.classList.add('logged-in'); } } // 登出后更新UI function updateUIForLoggedOut() { const avatarImg = document.getElementById('userAvatarImg'); const avatarBtn = document.getElementById('userAvatarBtn'); if (avatarImg) { avatarImg.src = 'https://api.dicebear.com/7.x/avataaars/svg?seed=neo'; } if (avatarBtn) { avatarBtn.classList.remove('logged-in'); } } // 从服务器获取最新用户信息 async function fetchUserInfo() { try { const userId = localStorage.getItem('user_id') || localStorage.getItem('user_id'); if (!userId) { return JSON.parse(localStorage.getItem('user_data') || '{}'); } const res = await fetch(`${AUTH_API_URL}/info`, { method: 'GET', headers: { 'Authorization': 'Bearer ' + localStorage.getItem('user_token') }, credentials: 'include' }); if (!res.ok) { throw new Error(`HTTP ${res.status}`); } const data = await res.json(); if (data.code === 0 && data.data) { localStorage.setItem('user_data', JSON.stringify(data.data)); return data.data; } return JSON.parse(localStorage.getItem('user_data') || '{}'); } catch (err) { console.error('获取用户信息失败:', err); return JSON.parse(localStorage.getItem('user_data') || '{}'); } } // 处理登录 async function handleLogin() { const username = document.getElementById('loginUsername').value.trim(); const password = document.getElementById('loginPassword').value.trim(); const agree = document.getElementById('loginAgree').checked; if (!username || !password) { showToast('请输入用户名和密码', 'error'); return; } if (!agree) { showToast('请同意用户协议', 'error'); return; } const btn = document.getElementById('loginBtn'); btn.disabled = true; btn.textContent = '登录中...'; try { const res = await fetch(`${AUTH_API_URL}/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); if (!res.ok) { throw new Error(`HTTP ${res.status}`); } const data = await res.json(); if (data.code === 0) { // 保存登录状态 localStorage.setItem('user_logged_in', '1'); localStorage.setItem('user_id', data.data.user_id); localStorage.setItem('user_id', data.data.user_id); localStorage.setItem('user_token', data.data.token); localStorage.setItem('user_data', JSON.stringify(data.data)); showToast('登录成功', 'success'); updateUIForLoggedIn(); // 关闭弹窗 document.getElementById('authModal').classList.remove('active'); // 刷新RH币余额显示 await loadUserBalance(); } else { showToast(data.msg || '登录失败', 'error'); } } catch (err) { showToast('网络错误,请稍后重试', 'error'); } btn.disabled = false; btn.textContent = '登录'; } // 处理注册 async function handleRegister() { const username = document.getElementById('regUsername').value.trim(); const nickname = document.getElementById('regNickname').value.trim(); const phone = document.getElementById('regPhone').value.trim(); const code = document.getElementById('regCode').value.trim(); const password = document.getElementById('regPassword').value.trim(); const passwordConfirm = document.getElementById('regPasswordConfirm').value.trim(); const agree = document.getElementById('regAgree').checked; if (!username || !phone || !code || !password || !passwordConfirm) { showToast('请填写完整信息', 'error'); return; } if (!agree) { showToast('请同意用户协议', 'error'); return; } const btn = document.getElementById('registerBtn'); btn.disabled = true; btn.textContent = '注册中...'; try { const res = await fetch(`${AUTH_API_URL}/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, nickname, phone, code, password, password_confirm: passwordConfirm }) }); if (!res.ok) { throw new Error(`HTTP ${res.status}`); } const data = await res.json(); if (data.code === 0) { localStorage.setItem('user_logged_in', '1'); localStorage.setItem('user_id', data.data.user_id); localStorage.setItem('user_id', data.data.user_id); localStorage.setItem('user_token', data.data.token); localStorage.setItem('user_data', JSON.stringify(data.data)); showToast('注册成功', 'success'); updateUIForLoggedIn(); document.getElementById('authModal').classList.remove('active'); } else { showToast(data.msg || '注册失败', 'error'); } } catch (err) { showToast('网络错误,请稍后重试', 'error'); } btn.disabled = false; btn.textContent = '注册'; } // 发送验证码 async function sendCode(type) { const phoneInput = type === 'register' ? document.getElementById('regPhone') : document.getElementById('forgotPhone'); const btn = type === 'register' ? document.getElementById('sendCodeBtn') : document.getElementById('forgotCodeBtn'); const phone = phoneInput.value.trim(); if (!phone) { showToast('请输入手机号', 'error'); return; } if (!/^1[3-9]\d{9}$/.test(phone)) { showToast('手机号格式不正确', 'error'); return; } btn.disabled = true; try { const res = await fetch(`${AUTH_API_URL}/send_code`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ phone, type }) }); if (!res.ok) { throw new Error(`HTTP ${res.status}`); } const data = await res.json(); if (data.code === 0) { showToast('验证码已发送', 'success'); let countdown = 60; btn.textContent = `${countdown}秒后重发`; const timer = setInterval(() => { countdown--; if (countdown <= 0) { clearInterval(timer); btn.disabled = false; btn.textContent = '获取验证码'; } else { btn.textContent = `${countdown}秒后重发`; } }, 1000); } else { showToast(data.msg || '发送失败', 'error'); btn.disabled = false; } } catch (err) { showToast('网络错误,请稍后重试', 'error'); btn.disabled = false; } } // 重置密码 async function handleResetPassword() { const phone = document.getElementById('forgotPhone').value.trim(); const code = document.getElementById('forgotCode').value.trim(); const password = document.getElementById('forgotPassword').value.trim(); const passwordConfirm = document.getElementById('forgotPasswordConfirm').value.trim(); if (!phone || !code || !password || !passwordConfirm) { showToast('请填写完整信息', 'error'); return; } const btn = document.getElementById('resetPasswordBtn'); btn.disabled = true; btn.textContent = '重置中...'; try { const res = await fetch(`${AUTH_API_URL}/reset_password`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ phone, code, password, password_confirm: passwordConfirm }) }); if (!res.ok) { throw new Error(`HTTP ${res.status}`); } const data = await res.json(); if (data.code === 0) { showToast('密码重置成功,请登录', 'success'); showLoginForm(); } else { showToast(data.msg || '重置失败', 'error'); } } catch (err) { showToast('网络错误,请稍后重试', 'error'); } btn.disabled = false; btn.textContent = '重置密码'; } // 退出登录 async function handleLogout() { try { await fetch(`${AUTH_API_URL}/logout`, { method: 'POST' }); } catch (e) { // 忽略 } // 清除本地存储 localStorage.removeItem('user_logged_in'); localStorage.removeItem('user_id'); localStorage.removeItem('user_id'); localStorage.removeItem('user_token'); localStorage.removeItem('user_data'); showToast('已退出登录', 'success'); updateUIForLoggedOut(); document.getElementById('authModal').classList.remove('active'); // 重置余额显示 document.getElementById('currentRhb').textContent = '0'; } // 显示用户信息面板 async function showUserInfoForm() { document.getElementById('loginForm').style.display = 'none'; document.getElementById('registerForm').style.display = 'none'; document.getElementById('forgotForm').style.display = 'none'; document.getElementById('userInfoForm').style.display = 'block'; // 先显示缓存的数据 const userData = JSON.parse(localStorage.getItem('user_data') || '{}'); document.getElementById('userInfoAvatar').src = userData.avatar || 'https://api.dicebear.com/7.x/avataaars/svg?seed=neo'; document.getElementById('userInfoNickname').textContent = userData.nickname || userData.username || '用户'; document.getElementById('userInfoMember').textContent = userData.group_name || '注册会员'; document.getElementById('userInfoRhb').textContent = '加载中...'; document.getElementById('userInfoTokens').textContent = userData.tokens || '0'; // 从服务器获取最新数据 const latestUserData = await fetchUserInfo(); document.getElementById('userInfoAvatar').src = latestUserData.avatar || 'https://api.dicebear.com/7.x/avataaars/svg?seed=neo'; document.getElementById('userInfoNickname').textContent = latestUserData.nickname || latestUserData.username || '用户'; document.getElementById('userInfoMember').textContent = latestUserData.group_name || '注册会员'; document.getElementById('userInfoRhb').textContent = latestUserData.rhb || '0'; document.getElementById('userInfoTokens').textContent = latestUserData.tokens || '0'; } // 显示登录表单 function showLoginForm() { document.getElementById('loginForm').style.display = 'block'; document.getElementById('registerForm').style.display = 'none'; document.getElementById('forgotForm').style.display = 'none'; document.getElementById('userInfoForm').style.display = 'none'; } // 显示注册表单 function showRegisterForm() { document.getElementById('loginForm').style.display = 'none'; document.getElementById('registerForm').style.display = 'block'; document.getElementById('forgotForm').style.display = 'none'; document.getElementById('userInfoForm').style.display = 'none'; } // 显示忘记密码表单 function showForgotForm() { document.getElementById('loginForm').style.display = 'none'; document.getElementById('registerForm').style.display = 'none'; document.getElementById('forgotForm').style.display = 'block'; document.getElementById('userInfoForm').style.display = 'none'; } // 加载会员配置(禁用缓存,确保获取最新数据) async function loadMemberConfig() { try { const response = await fetch('/api/member-group/list?t=' + Date.now(), { cache: 'no-store', headers: { 'Cache-Control': 'no-cache' } }); const result = await response.json(); if (result.code === 0 && result.data) { // API 返回的是数组,包装成对象格式 memberConfig = { groups: result.data }; } else { // API返回错误,静默使用默认配置 console.warn('会员配置API返回错误:', result.msg || '未知错误'); memberConfig = defaultConfig; } } catch (error) { // 网络错误或解析错误,静默使用默认配置 console.error('加载会员配置失败:', error); memberConfig = defaultConfig; } } // 渲染权益对比(科技感卡片 + 详情表格) function renderBenefitsTable() { const groups = memberConfig.groups || defaultConfig.groups; const container = document.getElementById('benefitsTable'); const levelClasses = ['free', 'gold', 'platinum', 'diamond']; // 图标SVG const iconSvg = ``; const checkSvg = ``; const crossSvg = ``; // 生成会员卡片 const cardsHtml = groups.map((g, i) => { const cls = levelClasses[i] || 'free'; const priceText = g.price > 0 ? `¥${g.price}` : '免费'; const btnText = g.price > 0 ? '立即开通' : '当前等级'; const btnOnclick = g.price > 0 ? `onclick="buyMember(${g.id}, '${g.name}', ${g.price})"` : ''; const btnClass = g.price > 0 ? '' : 'disabled'; return `
${iconSvg}
${g.name}
${priceText} ${g.price > 0 ? '/月' : ''}
每日奖励 ${g.daily_reward || 50} 算力
赠送币 ${g.rhb_reward > 0 ? g.rhb_reward.toLocaleString() + ' ' + tokenName + '' : '-'}
并发数 ${g.concurrent_threads || 1} 个
图片保存 ${g.image_save_days === 0 ? '永久' : g.image_save_days + ' 天'}
`; }).join(''); // 生成详情表格 const benefits = [ { label: '每日登录奖励', sub: '领取后24h有效', getValue: g => `${g.daily_reward || 50} 算力` }, { label: '购买赠送', sub: '一次性', getValue: g => g.rhb_reward > 0 ? `${g.rhb_reward.toLocaleString()} ${tokenName}` : crossSvg }, { label: '并发线程', sub: '同时任务数', getValue: g => `${g.concurrent_threads || 1} 个` }, { label: '图片保存', sub: '', getValue: g => g.image_save_days === 0 ? '永久' : `${g.image_save_days} 天` }, { label: '视频保存', sub: '', getValue: g => g.video_save_days === 0 ? '永久' : `${g.video_save_days} 天` }, { label: '去水印', sub: '', getValue: g => g.features?.watermark ? checkSvg : crossSvg }, { label: '优先队列', sub: '', getValue: g => g.features?.priority_queue ? checkSvg : crossSvg }, { label: '专属模型', sub: '', getValue: g => g.features?.exclusive_models ? checkSvg : crossSvg }, { label: 'API访问', sub: '', getValue: g => g.features?.api_access ? checkSvg : crossSvg } ]; const headerCells = groups.map((g, i) => `
${g.name}
`).join(''); const rowsHtml = benefits.map(b => `
${b.label}
${b.sub ? `
${b.sub}
` : ''}
${groups.map(g => { const val = b.getValue(g); const isIcon = val.includes('${val}
`; }).join('')} `).join(''); container.innerHTML = `
${cardsHtml}
权益详情
${headerCells}
${rowsHtml}
`; } // 购买会员 async function buyMember(groupId, groupName, price) { console.log('=== buyMember 开始 ==='); // 获取用户ID - 直接从各个来源获取 let userId = null; // 1. 从 localStorage 获取 const sessionUserId = localStorage.getItem('user_id'); console.log('1. localStorage.user_id:', sessionUserId, typeof sessionUserId); // 2. 再从 localStorage 获取 const localUserId = localStorage.getItem('user_id'); console.log('2. localStorage.user_id:', localUserId, typeof localUserId); // 3. 从 user_data 获取 const userData = localStorage.getItem('user_data'); let userDataId = null; if (userData) { try { const parsed = JSON.parse(userData); userDataId = parsed.user_id; console.log('3. user_data.user_id:', userDataId, typeof userDataId); } catch(e) { console.log('3. user_data 解析失败'); } } // 确定最终的 userId userId = sessionUserId || localUserId || userDataId; console.log('4. 最终 userId:', userId, typeof userId); // 如果从 user_data 获取到了 userId,同步到 storage if (userId && !sessionUserId) { localStorage.setItem('user_id', String(userId)); localStorage.setItem('user_logged_in', '1'); } if (userId && !localUserId) { localStorage.setItem('user_id', String(userId)); } // 简化的登录检查:只要 userId 存在且不是假值 const isValid = userId && userId !== 'null' && userId !== 'undefined' && userId !== ''; console.log('5. userId 是否有效:', isValid); if (!isValid) { console.log('6. 检测失败,弹出登录提示'); showToast('请先登录后再购买', 'error'); setTimeout(() => { document.getElementById('authModal').classList.add('active'); showLoginForm(); }, 1000); return; } console.log('7. 检测通过,准备跳转支付'); showToast('正在跳转支付...', 'info'); try { const response = await fetch('/api/payment/pay', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + localStorage.getItem('user_token') }, body: JSON.stringify({ payment_type: 'alipay', pay_type: 'alipay', amount: price, product_name: `${groupName}会员(月卡)`, product_desc: `开通${groupName}会员,有效期30天`, product_type: 'member', member_group_id: groupId }) }); const result = await response.json(); if (result.code === 0 && result.data) { // 跳转支付宝支付 const payUrl = result.data.pay_url || result.data.redirect_url; if (payUrl) { showToast('正在跳转支付宝...', 'success'); // 直接跳转到支付宝 window.location.href = payUrl; } else { showToast('订单创建成功,但支付链接获取失败', 'error'); } } else { showToast(result.msg || '订单创建失败', 'error'); } } catch (error) { console.error('购买失败:', error); showToast('网络错误,请重试', 'error'); } } // 页面加载完成标志 let pageFullyLoaded = false; window.addEventListener('load', () => { pageFullyLoaded = true; }); // 显示提示 function showToast(message, type = 'info') { // 只阻止显示数据库连接相关的技术性错误 if (message && message.includes('数据库连接失败')) { console.warn('抑制技术错误提示:', message); return; } const existing = document.querySelector('.toast'); if (existing) existing.remove(); const toast = document.createElement('div'); toast.className = `toast ${type}`; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => { toast.style.animation = 'toastIn 0.4s cubic-bezier(0.4, 0, 0.2, 1) reverse'; setTimeout(() => toast.remove(), 400); }, 3000); } // ========== 代币充值相关 ========== let exchangeRate = 100; // 1元 = 100 代币 let selectedAmount = 0; let tokenName = 'R币'; // 代币名称,从配置加载 // 页面加载时初始化代币面板 document.addEventListener('DOMContentLoaded', async function() { await loadExchangeRate(); await loadUserBalance(); updateAmountOptions(); updateTokenNameDisplay(); // 监听自定义金额输入 document.getElementById('customAmount').addEventListener('input', function() { const amount = parseFloat(this.value) || 0; document.getElementById('customRhb').textContent = (amount * exchangeRate).toLocaleString(); selectedAmount = amount; updatePayButton(); }); // URL参数切换tab const hash = window.location.hash; if (hash === '#rhb') { switchTab('rhb', document.querySelector('.page-tab[data-tab="rhb"]')); } }); // 加载汇率配置和代币名称(禁用缓存) async function loadExchangeRate() { try { const response = await fetch('/api/config?t=' + Date.now(), { cache: 'no-store' }); const result = await response.json(); if (result.code === 0 && result.data) { if (result.data.rhb_exchange_rate) { exchangeRate = parseInt(result.data.rhb_exchange_rate) || 100; document.getElementById('exchangeRate').textContent = exchangeRate; } if (result.data.token_name) { tokenName = result.data.token_name; updateTokenNameDisplay(); } } } catch (error) { console.error('加载配置失败:', error); } } // 更新页面上所有代币名称显示 function updateTokenNameDisplay() { document.querySelectorAll('.token-name').forEach(el => { el.textContent = tokenName; }); } // 加载用户余额(禁用缓存) async function loadUserBalance() { const userId = localStorage.getItem('user_id') || localStorage.getItem('user_id'); if (!userId) return; try { const response = await fetch('/api/user/info?t=' + Date.now(), { cache: 'no-store', headers: { 'Authorization': 'Bearer ' + localStorage.getItem('user_token') } }); const result = await response.json(); if (result.code === 0 && result.data) { document.getElementById('currentRhb').textContent = parseFloat(result.data.rhb || 0).toLocaleString(); } } catch (error) { console.error('加载余额失败:', error); } } // 更新金额选项显示 function updateAmountOptions() { const options = document.querySelectorAll('.amount-option:not(.custom)'); options.forEach(opt => { const amount = parseInt(opt.dataset.amount); const rhb = amount * exchangeRate; const rhbEl = opt.querySelector('.amount-rhb'); if (rhbEl) { // 保留 span.token-name 元素 const tokenSpan = rhbEl.querySelector('.token-name'); if (tokenSpan) { rhbEl.innerHTML = rhb.toLocaleString() + ' ' + tokenName + ''; } else { rhbEl.textContent = rhb.toLocaleString() + ' ' + tokenName; } } }); } // 切换Tab function switchTab(tab, element) { // 更新tab样式 document.querySelectorAll('.page-tab').forEach(t => t.classList.remove('active')); element.classList.add('active'); // 切换面板 document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active')); document.getElementById(tab + 'Panel').classList.add('active'); // 更新URL window.location.hash = tab; } // 选择金额 function selectAmount(amount, element) { document.querySelectorAll('.amount-option').forEach(opt => opt.classList.remove('selected')); element.classList.add('selected'); document.getElementById('customInputRow').style.display = 'none'; selectedAmount = amount; updatePayButton(); } // 显示自定义输入 function showCustomInput() { document.querySelectorAll('.amount-option').forEach(opt => opt.classList.remove('selected')); document.querySelector('.amount-option.custom').classList.add('selected'); document.getElementById('customInputRow').style.display = 'flex'; document.getElementById('customAmount').focus(); selectedAmount = 0; updatePayButton(); } // 更新支付按钮 function updatePayButton() { document.getElementById('payAmountText').textContent = '¥' + selectedAmount; } // 支付RH币 async function payRhb() { if (selectedAmount <= 0) { showToast('请选择充值金额', 'error'); return; } // 直接获取用户ID(多重检测) let userId = localStorage.getItem('user_id') || localStorage.getItem('user_id'); // 如果直接获取不到,尝试从 user_data 解析 if (!userId || userId === 'null' || userId === 'undefined') { const userData = localStorage.getItem('user_data'); if (userData && userData !== 'null' && userData !== '{}') { try { const parsed = JSON.parse(userData); userId = parsed.user_id; if (userId) { localStorage.setItem('user_id', String(userId)); localStorage.setItem('user_id', String(userId)); localStorage.setItem('user_logged_in', '1'); } } catch(e) {} } } // 检查是否有有效的 userId if (!userId || userId === 'null' || userId === 'undefined') { showToast('请先登录后再充值', 'error'); setTimeout(() => { document.getElementById('authModal').classList.add('active'); showLoginForm(); }, 1000); return; } const rhbAmount = selectedAmount * exchangeRate; showToast('正在创建订单...', 'info'); try { const response = await fetch('/api/payment/pay', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + localStorage.getItem('user_token') }, body: JSON.stringify({ payment_type: 'alipay', pay_type: 'alipay', amount: selectedAmount, product_name: `${tokenName}充值 ${rhbAmount.toLocaleString()} 币`, product_desc: `充值 ${rhbAmount.toLocaleString()} ${tokenName}`, product_type: 'rhb', rhb_amount: rhbAmount }) }); const result = await response.json(); if (result.code === 0 && result.data) { const payUrl = result.data.pay_url || result.data.redirect_url; if (payUrl) { showToast('正在跳转支付宝...', 'success'); // 直接跳转到支付宝 window.location.href = payUrl; } else { showToast('订单创建成功,但支付链接获取失败', 'error'); } } else { showToast(result.msg || '订单创建失败', 'error'); } } catch (error) { console.error('充值失败:', error); showToast('网络错误,请重试', 'error'); } } document.getElementById('userInfoMember').textContent = latestUserData.group_name || '注册会员'; document.getElementById('userInfoRhb').textContent = latestUserData.rhb || '0'; document.getElementById('userInfoTokens').textContent = latestUserData.tokens || '0'; } // 显示登录表单 function showLoginForm() { document.getElementById('loginForm').style.display = 'block'; document.getElementById('registerForm').style.display = 'none'; document.getElementById('forgotForm').style.display = 'none'; document.getElementById('userInfoForm').style.display = 'none'; } // 显示注册表单 function showRegisterForm() { document.getElementById('loginForm').style.display = 'none'; document.getElementById('registerForm').style.display = 'block'; document.getElementById('forgotForm').style.display = 'none'; document.getElementById('userInfoForm').style.display = 'none'; } // 显示忘记密码表单 function showForgotForm() { document.getElementById('loginForm').style.display = 'none'; document.getElementById('registerForm').style.display = 'none'; document.getElementById('forgotForm').style.display = 'block'; document.getElementById('userInfoForm').style.display = 'none'; } // 加载会员配置(禁用缓存,确保获取最新数据) async function loadMemberConfig() { try { const response = await fetch('/api/member-group/list?t=' + Date.now(), { cache: 'no-store', headers: { 'Cache-Control': 'no-cache' } }); const result = await response.json(); if (result.code === 0 && result.data) { // API 返回的是数组,包装成对象格式 memberConfig = { groups: result.data }; } else { // API返回错误,静默使用默认配置 console.warn('会员配置API返回错误:', result.msg || '未知错误'); memberConfig = defaultConfig; } } catch (error) { // 网络错误或解析错误,静默使用默认配置 console.error('加载会员配置失败:', error); memberConfig = defaultConfig; } } // 渲染权益对比(科技感卡片 + 详情表格) function renderBenefitsTable() { const groups = memberConfig.groups || defaultConfig.groups; const container = document.getElementById('benefitsTable'); const levelClasses = ['free', 'gold', 'platinum', 'diamond']; // 图标SVG const iconSvg = ``; const checkSvg = ``; const crossSvg = ``; // 生成会员卡片 const cardsHtml = groups.map((g, i) => { const cls = levelClasses[i] || 'free'; const priceText = g.price > 0 ? `¥${g.price}` : '免费'; const btnText = g.price > 0 ? '立即开通' : '当前等级'; const btnOnclick = g.price > 0 ? `onclick="buyMember(${g.id}, '${g.name}', ${g.price})"` : ''; const btnClass = g.price > 0 ? '' : 'disabled'; return `
${iconSvg}
${g.name}
${priceText} ${g.price > 0 ? '/月' : ''}
每日奖励 ${g.daily_reward || 50} 算力
赠送币 ${g.rhb_reward > 0 ? g.rhb_reward.toLocaleString() + ' ' + tokenName + '' : '-'}
并发数 ${g.concurrent_threads || 1} 个
图片保存 ${g.image_save_days === 0 ? '永久' : g.image_save_days + ' 天'}
`; }).join(''); // 生成详情表格 const benefits = [ { label: '每日登录奖励', sub: '领取后24h有效', getValue: g => `${g.daily_reward || 50} 算力` }, { label: '购买赠送', sub: '一次性', getValue: g => g.rhb_reward > 0 ? `${g.rhb_reward.toLocaleString()} ${tokenName}` : crossSvg }, { label: '并发线程', sub: '同时任务数', getValue: g => `${g.concurrent_threads || 1} 个` }, { label: '图片保存', sub: '', getValue: g => g.image_save_days === 0 ? '永久' : `${g.image_save_days} 天` }, { label: '视频保存', sub: '', getValue: g => g.video_save_days === 0 ? '永久' : `${g.video_save_days} 天` }, { label: '去水印', sub: '', getValue: g => g.features?.watermark ? checkSvg : crossSvg }, { label: '优先队列', sub: '', getValue: g => g.features?.priority_queue ? checkSvg : crossSvg }, { label: '专属模型', sub: '', getValue: g => g.features?.exclusive_models ? checkSvg : crossSvg }, { label: 'API访问', sub: '', getValue: g => g.features?.api_access ? checkSvg : crossSvg } ]; const headerCells = groups.map((g, i) => `
${g.name}
`).join(''); const rowsHtml = benefits.map(b => `
${b.label}
${b.sub ? `
${b.sub}
` : ''}
${groups.map(g => { const val = b.getValue(g); const isIcon = val.includes('${val}
`; }).join('')} `).join(''); container.innerHTML = `
${cardsHtml}
权益详情
${headerCells}
${rowsHtml}
`; } // 购买会员 async function buyMember(groupId, groupName, price) { console.log('=== buyMember 开始 ==='); // 获取用户ID - 直接从各个来源获取 let userId = null; // 1. 从 localStorage 获取 const sessionUserId = localStorage.getItem('user_id'); console.log('1. localStorage.user_id:', sessionUserId, typeof sessionUserId); // 2. 再从 localStorage 获取 const localUserId = localStorage.getItem('user_id'); console.log('2. localStorage.user_id:', localUserId, typeof localUserId); // 3. 从 user_data 获取 const userData = localStorage.getItem('user_data'); let userDataId = null; if (userData) { try { const parsed = JSON.parse(userData); userDataId = parsed.user_id; console.log('3. user_data.user_id:', userDataId, typeof userDataId); } catch(e) { console.log('3. user_data 解析失败'); } } // 确定最终的 userId userId = sessionUserId || localUserId || userDataId; console.log('4. 最终 userId:', userId, typeof userId); // 如果从 user_data 获取到了 userId,同步到 storage if (userId && !sessionUserId) { localStorage.setItem('user_id', String(userId)); localStorage.setItem('user_logged_in', '1'); } if (userId && !localUserId) { localStorage.setItem('user_id', String(userId)); } // 简化的登录检查:只要 userId 存在且不是假值 const isValid = userId && userId !== 'null' && userId !== 'undefined' && userId !== ''; console.log('5. userId 是否有效:', isValid); if (!isValid) { console.log('6. 检测失败,弹出登录提示'); showToast('请先登录后再购买', 'error'); setTimeout(() => { document.getElementById('authModal').classList.add('active'); showLoginForm(); }, 1000); return; } console.log('7. 检测通过,准备跳转支付'); showToast('正在跳转支付...', 'info'); try { const response = await fetch('/api/payment/pay', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + localStorage.getItem('user_token') }, body: JSON.stringify({ payment_type: 'alipay', pay_type: 'alipay', amount: price, product_name: `${groupName}会员(月卡)`, product_desc: `开通${groupName}会员,有效期30天`, product_type: 'member', member_group_id: groupId }) }); const result = await response.json(); if (result.code === 0 && result.data) { // 跳转支付宝支付 const payUrl = result.data.pay_url || result.data.redirect_url; if (payUrl) { showToast('正在跳转支付宝...', 'success'); // 直接跳转到支付宝 window.location.href = payUrl; } else { showToast('订单创建成功,但支付链接获取失败', 'error'); } } else { showToast(result.msg || '订单创建失败', 'error'); } } catch (error) { console.error('购买失败:', error); showToast('网络错误,请重试', 'error'); } } // 页面加载完成标志 let pageFullyLoaded = false; window.addEventListener('load', () => { pageFullyLoaded = true; }); // 显示提示 function showToast(message, type = 'info') { // 只阻止显示数据库连接相关的技术性错误 if (message && message.includes('数据库连接失败')) { console.warn('抑制技术错误提示:', message); return; } const existing = document.querySelector('.toast'); if (existing) existing.remove(); const toast = document.createElement('div'); toast.className = `toast ${type}`; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => { toast.style.animation = 'toastIn 0.4s cubic-bezier(0.4, 0, 0.2, 1) reverse'; setTimeout(() => toast.remove(), 400); }, 3000); } // ========== 代币充值相关 ========== let exchangeRate = 100; // 1元 = 100 代币 let selectedAmount = 0; let tokenName = 'R币'; // 代币名称,从配置加载 // 页面加载时初始化代币面板 document.addEventListener('DOMContentLoaded', async function() { await loadExchangeRate(); await loadUserBalance(); updateAmountOptions(); updateTokenNameDisplay(); // 监听自定义金额输入 document.getElementById('customAmount').addEventListener('input', function() { const amount = parseFloat(this.value) || 0; document.getElementById('customRhb').textContent = (amount * exchangeRate).toLocaleString(); selectedAmount = amount; updatePayButton(); }); // URL参数切换tab const hash = window.location.hash; if (hash === '#rhb') { switchTab('rhb', document.querySelector('.page-tab[data-tab="rhb"]')); } }); // 加载汇率配置和代币名称(禁用缓存) async function loadExchangeRate() { try { const response = await fetch('/api/config?t=' + Date.now(), { cache: 'no-store' }); const result = await response.json(); if (result.code === 0 && result.data) { if (result.data.rhb_exchange_rate) { exchangeRate = parseInt(result.data.rhb_exchange_rate) || 100; document.getElementById('exchangeRate').textContent = exchangeRate; } if (result.data.token_name) { tokenName = result.data.token_name; updateTokenNameDisplay(); } } } catch (error) { console.error('加载配置失败:', error); } } // 更新页面上所有代币名称显示 function updateTokenNameDisplay() { document.querySelectorAll('.token-name').forEach(el => { el.textContent = tokenName; }); } // 加载用户余额(禁用缓存) async function loadUserBalance() { const userId = localStorage.getItem('user_id') || localStorage.getItem('user_id'); if (!userId) return; try { const response = await fetch('/api/user/info?t=' + Date.now(), { cache: 'no-store', headers: { 'Authorization': 'Bearer ' + localStorage.getItem('user_token') } }); const result = await response.json(); if (result.code === 0 && result.data) { document.getElementById('currentRhb').textContent = parseFloat(result.data.rhb || 0).toLocaleString(); } } catch (error) { console.error('加载余额失败:', error); } } // 更新金额选项显示 function updateAmountOptions() { const options = document.querySelectorAll('.amount-option:not(.custom)'); options.forEach(opt => { const amount = parseInt(opt.dataset.amount); const rhb = amount * exchangeRate; const rhbEl = opt.querySelector('.amount-rhb'); if (rhbEl) { // 保留 span.token-name 元素 const tokenSpan = rhbEl.querySelector('.token-name'); if (tokenSpan) { rhbEl.innerHTML = rhb.toLocaleString() + ' ' + tokenName + ''; } else { rhbEl.textContent = rhb.toLocaleString() + ' ' + tokenName; } } }); } // 切换Tab function switchTab(tab, element) { // 更新tab样式 document.querySelectorAll('.page-tab').forEach(t => t.classList.remove('active')); element.classList.add('active'); // 切换面板 document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active')); document.getElementById(tab + 'Panel').classList.add('active'); // 更新URL window.location.hash = tab; } // 选择金额 function selectAmount(amount, element) { document.querySelectorAll('.amount-option').forEach(opt => opt.classList.remove('selected')); element.classList.add('selected'); document.getElementById('customInputRow').style.display = 'none'; selectedAmount = amount; updatePayButton(); } // 显示自定义输入 function showCustomInput() { document.querySelectorAll('.amount-option').forEach(opt => opt.classList.remove('selected')); document.querySelector('.amount-option.custom').classList.add('selected'); document.getElementById('customInputRow').style.display = 'flex'; document.getElementById('customAmount').focus(); selectedAmount = 0; updatePayButton(); } // 更新支付按钮 function updatePayButton() { document.getElementById('payAmountText').textContent = '¥' + selectedAmount; } // 支付RH币 async function payRhb() { if (selectedAmount <= 0) { showToast('请选择充值金额', 'error'); return; } // 直接获取用户ID(多重检测) let userId = localStorage.getItem('user_id') || localStorage.getItem('user_id'); // 如果直接获取不到,尝试从 user_data 解析 if (!userId || userId === 'null' || userId === 'undefined') { const userData = localStorage.getItem('user_data'); if (userData && userData !== 'null' && userData !== '{}') { try { const parsed = JSON.parse(userData); userId = parsed.user_id; if (userId) { localStorage.setItem('user_id', String(userId)); localStorage.setItem('user_id', String(userId)); localStorage.setItem('user_logged_in', '1'); } } catch(e) {} } } // 检查是否有有效的 userId if (!userId || userId === 'null' || userId === 'undefined') { showToast('请先登录后再充值', 'error'); setTimeout(() => { document.getElementById('authModal').classList.add('active'); showLoginForm(); }, 1000); return; } const rhbAmount = selectedAmount * exchangeRate; showToast('正在创建订单...', 'info'); try { const response = await fetch('/api/payment/pay', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + localStorage.getItem('user_token') }, body: JSON.stringify({ payment_type: 'alipay', pay_type: 'alipay', amount: selectedAmount, product_name: `${tokenName}充值 ${rhbAmount.toLocaleString()} 币`, product_desc: `充值 ${rhbAmount.toLocaleString()} ${tokenName}`, product_type: 'rhb', rhb_amount: rhbAmount }) }); const result = await response.json(); if (result.code === 0 && result.data) { const payUrl = result.data.pay_url || result.data.redirect_url; if (payUrl) { showToast('正在跳转支付宝...', 'success'); // 直接跳转到支付宝 window.location.href = payUrl; } else { showToast('订单创建成功,但支付链接获取失败', 'error'); } } else { showToast(result.msg || '订单创建失败', 'error'); } } catch (error) { console.error('充值失败:', error); showToast('网络错误,请重试', 'error'); } } daily_reward: 500, concurrent_threads: 8, image_save_days: 0, video_save_days: 30, features: { watermark: true, priority_queue: true, exclusive_models: true, api_access: true } } ] }; // 页面加载 // 页面加载前立即同步登录状态 (function syncLoginState() { const userId = localStorage.getItem('user_id'); const userToken = localStorage.getItem('user_token'); const userData = localStorage.getItem('user_data'); if (userId && userId !== 'null' && userId !== 'undefined') { localStorage.setItem('user_logged_in', '1'); localStorage.setItem('user_id', userId); } else if (userData && userData !== 'null' && userData !== '{}') { try { const parsed = JSON.parse(userData); if (parsed.user_id) { localStorage.setItem('user_logged_in', '1'); localStorage.setItem('user_id', parsed.user_id); localStorage.setItem('user_id', parsed.user_id); } } catch(e) {} } else if (userToken && userToken !== 'null' && userToken !== 'undefined') { localStorage.setItem('user_logged_in', '1'); } })(); document.addEventListener('DOMContentLoaded', async function() { // 清除可能残留的错误提示 const existingToasts = document.querySelectorAll('.toast'); existingToasts.forEach(t => t.remove()); // 检查用户登录状态并更新头像 await checkLoginState(); await loadMemberConfig(); renderBenefitsTable(); }); // ========================================== // 登录/注册系统(与首页一致) // ========================================== const AUTH_API_URL = '/api/user'; // 检查是否已登录(多重检测,确保健壮性) function isLoggedIn() { // 检查 localStorage 中的登录标志 if (localStorage.getItem('user_logged_in') === '1') { return true; } // 检查 localStorage 中的 token(排除无效值) const token = localStorage.getItem('user_token'); if (token && token !== 'null' && token !== 'undefined' && token !== '') { return true; } // 检查 user_id 是否存在(排除无效值) const userId = localStorage.getItem('user_id') || localStorage.getItem('user_id'); if (userId && userId !== 'null' && userId !== 'undefined' && userId !== '') { localStorage.setItem('user_logged_in', '1'); return true; } // 检查 user_data 是否存在且有效 const userData = localStorage.getItem('user_data'); if (userData && userData !== 'null' && userData !== '{}') { try { const parsed = JSON.parse(userData); if (parsed.user_id) { localStorage.setItem('user_logged_in', '1'); localStorage.setItem('user_id', parsed.user_id); localStorage.setItem('user_id', parsed.user_id); return true; } } catch(e) {} } return false; } // 检查登录状态并更新UI async function checkLoginState() { const authModal = document.getElementById('authModal'); const authModalOverlay = document.getElementById('authModalOverlay'); const authCloseBtn = document.getElementById('authCloseBtn'); const avatarBtn = document.getElementById('userAvatarBtn'); const avatarImg = document.getElementById('userAvatarImg'); if (!authModal || !avatarBtn) return; // 点击头像打开弹窗(与首页一致) avatarBtn.onclick = function() { authModal.classList.add('active'); if (isLoggedIn()) { showUserInfoForm(); } else { showLoginForm(); } }; // 关闭弹窗 authCloseBtn.addEventListener('click', () => { authModal.classList.remove('active'); }); authModalOverlay.addEventListener('click', () => { authModal.classList.remove('active'); }); // 登录 document.getElementById('loginBtn')?.addEventListener('click', handleLogin); // 注册 document.getElementById('registerBtn')?.addEventListener('click', handleRegister); // 发送验证码 document.getElementById('sendCodeBtn')?.addEventListener('click', () => sendCode('register')); document.getElementById('forgotCodeBtn')?.addEventListener('click', () => sendCode('reset')); // 重置密码 document.getElementById('resetPasswordBtn')?.addEventListener('click', handleResetPassword); // 退出登录 document.getElementById('logoutBtn')?.addEventListener('click', handleLogout); // 回车登录 document.getElementById('loginPassword')?.addEventListener('keydown', (e) => { if (e.key === 'Enter') handleLogin(); }); // 如果已登录,更新头像 if (isLoggedIn()) { await updateUIForLoggedIn(); } } // 登录后更新UI async function updateUIForLoggedIn() { const userData = await fetchUserInfo(); const avatarImg = document.getElementById('userAvatarImg'); const avatarBtn = document.getElementById('userAvatarBtn'); if (avatarImg && userData.avatar) { avatarImg.src = userData.avatar; } if (avatarBtn) { avatarBtn.classList.add('logged-in'); } } // 登出后更新UI function updateUIForLoggedOut() { const avatarImg = document.getElementById('userAvatarImg'); const avatarBtn = document.getElementById('userAvatarBtn'); if (avatarImg) { avatarImg.src = 'https://api.dicebear.com/7.x/avataaars/svg?seed=neo'; } if (avatarBtn) { avatarBtn.classList.remove('logged-in'); } } // 从服务器获取最新用户信息 async function fetchUserInfo() { try { const userId = localStorage.getItem('user_id') || localStorage.getItem('user_id'); if (!userId) { return JSON.parse(localStorage.getItem('user_data') || '{}'); } const res = await fetch(`${AUTH_API_URL}/info`, { method: 'GET', headers: { 'Authorization': 'Bearer ' + localStorage.getItem('user_token') }, credentials: 'include' }); if (!res.ok) { throw new Error(`HTTP ${res.status}`); } const data = await res.json(); if (data.code === 0 && data.data) { localStorage.setItem('user_data', JSON.stringify(data.data)); return data.data; } return JSON.parse(localStorage.getItem('user_data') || '{}'); } catch (err) { console.error('获取用户信息失败:', err); return JSON.parse(localStorage.getItem('user_data') || '{}'); } } // 处理登录 async function handleLogin() { const username = document.getElementById('loginUsername').value.trim(); const password = document.getElementById('loginPassword').value.trim(); const agree = document.getElementById('loginAgree').checked; if (!username || !password) { showToast('请输入用户名和密码', 'error'); return; } if (!agree) { showToast('请同意用户协议', 'error'); return; } const btn = document.getElementById('loginBtn'); btn.disabled = true; btn.textContent = '登录中...'; try { const res = await fetch(`${AUTH_API_URL}/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); if (!res.ok) { throw new Error(`HTTP ${res.status}`); } const data = await res.json(); if (data.code === 0) { // 保存登录状态 localStorage.setItem('user_logged_in', '1'); localStorage.setItem('user_id', data.data.user_id); localStorage.setItem('user_id', data.data.user_id); localStorage.setItem('user_token', data.data.token); localStorage.setItem('user_data', JSON.stringify(data.data)); showToast('登录成功', 'success'); updateUIForLoggedIn(); // 关闭弹窗 document.getElementById('authModal').classList.remove('active'); // 刷新RH币余额显示 await loadUserBalance(); } else { showToast(data.msg || '登录失败', 'error'); } } catch (err) { showToast('网络错误,请稍后重试', 'error'); } btn.disabled = false; btn.textContent = '登录'; } // 处理注册 async function handleRegister() { const username = document.getElementById('regUsername').value.trim(); const nickname = document.getElementById('regNickname').value.trim(); const phone = document.getElementById('regPhone').value.trim(); const code = document.getElementById('regCode').value.trim(); const password = document.getElementById('regPassword').value.trim(); const passwordConfirm = document.getElementById('regPasswordConfirm').value.trim(); const agree = document.getElementById('regAgree').checked; if (!username || !phone || !code || !password || !passwordConfirm) { showToast('请填写完整信息', 'error'); return; } if (!agree) { showToast('请同意用户协议', 'error'); return; } const btn = document.getElementById('registerBtn'); btn.disabled = true; btn.textContent = '注册中...'; try { const res = await fetch(`${AUTH_API_URL}/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, nickname, phone, code, password, password_confirm: passwordConfirm }) }); if (!res.ok) { throw new Error(`HTTP ${res.status}`); } const data = await res.json(); if (data.code === 0) { localStorage.setItem('user_logged_in', '1'); localStorage.setItem('user_id', data.data.user_id); localStorage.setItem('user_id', data.data.user_id); localStorage.setItem('user_token', data.data.token); localStorage.setItem('user_data', JSON.stringify(data.data)); showToast('注册成功', 'success'); updateUIForLoggedIn(); document.getElementById('authModal').classList.remove('active'); } else { showToast(data.msg || '注册失败', 'error'); } } catch (err) { showToast('网络错误,请稍后重试', 'error'); } btn.disabled = false; btn.textContent = '注册'; } // 发送验证码 async function sendCode(type) { const phoneInput = type === 'register' ? document.getElementById('regPhone') : document.getElementById('forgotPhone'); const btn = type === 'register' ? document.getElementById('sendCodeBtn') : document.getElementById('forgotCodeBtn'); const phone = phoneInput.value.trim(); if (!phone) { showToast('请输入手机号', 'error'); return; } if (!/^1[3-9]\d{9}$/.test(phone)) { showToast('手机号格式不正确', 'error'); return; } btn.disabled = true; try { const res = await fetch(`${AUTH_API_URL}/send_code`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ phone, type }) }); if (!res.ok) { throw new Error(`HTTP ${res.status}`); } const data = await res.json(); if (data.code === 0) { showToast('验证码已发送', 'success'); let countdown = 60; btn.textContent = `${countdown}秒后重发`; const timer = setInterval(() => { countdown--; if (countdown <= 0) { clearInterval(timer); btn.disabled = false; btn.textContent = '获取验证码'; } else { btn.textContent = `${countdown}秒后重发`; } }, 1000); } else { showToast(data.msg || '发送失败', 'error'); btn.disabled = false; } } catch (err) { showToast('网络错误,请稍后重试', 'error'); btn.disabled = false; } } // 重置密码 async function handleResetPassword() { const phone = document.getElementById('forgotPhone').value.trim(); const code = document.getElementById('forgotCode').value.trim(); const password = document.getElementById('forgotPassword').value.trim(); const passwordConfirm = document.getElementById('forgotPasswordConfirm').value.trim(); if (!phone || !code || !password || !passwordConfirm) { showToast('请填写完整信息', 'error'); return; } const btn = document.getElementById('resetPasswordBtn'); btn.disabled = true; btn.textContent = '重置中...'; try { const res = await fetch(`${AUTH_API_URL}/reset_password`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ phone, code, password, password_confirm: passwordConfirm }) }); if (!res.ok) { throw new Error(`HTTP ${res.status}`); } const data = await res.json(); if (data.code === 0) { showToast('密码重置成功,请登录', 'success'); showLoginForm(); } else { showToast(data.msg || '重置失败', 'error'); } } catch (err) { showToast('网络错误,请稍后重试', 'error'); } btn.disabled = false; btn.textContent = '重置密码'; } // 退出登录 async function handleLogout() { try { await fetch(`${AUTH_API_URL}/logout`, { method: 'POST' }); } catch (e) { // 忽略 } // 清除本地存储 localStorage.removeItem('user_logged_in'); localStorage.removeItem('user_id'); localStorage.removeItem('user_id'); localStorage.removeItem('user_token'); localStorage.removeItem('user_data'); showToast('已退出登录', 'success'); updateUIForLoggedOut(); document.getElementById('authModal').classList.remove('active'); // 重置余额显示 document.getElementById('currentRhb').textContent = '0'; } // 显示用户信息面板 async function showUserInfoForm() { document.getElementById('loginForm').style.display = 'none'; document.getElementById('registerForm').style.display = 'none'; document.getElementById('forgotForm').style.display = 'none'; document.getElementById('userInfoForm').style.display = 'block'; // 先显示缓存的数据 const userData = JSON.parse(localStorage.getItem('user_data') || '{}'); document.getElementById('userInfoAvatar').src = userData.avatar || 'https://api.dicebear.com/7.x/avataaars/svg?seed=neo'; document.getElementById('userInfoNickname').textContent = userData.nickname || userData.username || '用户'; document.getElementById('userInfoMember').textContent = userData.group_name || '注册会员'; document.getElementById('userInfoRhb').textContent = '加载中...'; document.getElementById('userInfoTokens').textContent = userData.tokens || '0'; // 从服务器获取最新数据 const latestUserData = await fetchUserInfo(); document.getElementById('userInfoAvatar').src = latestUserData.avatar || 'https://api.dicebear.com/7.x/avataaars/svg?seed=neo'; document.getElementById('userInfoNickname').textContent = latestUserData.nickname || latestUserData.username || '用户'; document.getElementById('userInfoMember').textContent = latestUserData.group_name || '注册会员'; document.getElementById('userInfoRhb').textContent = latestUserData.rhb || '0'; document.getElementById('userInfoTokens').textContent = latestUserData.tokens || '0'; } // 显示登录表单 function showLoginForm() { document.getElementById('loginForm').style.display = 'block'; document.getElementById('registerForm').style.display = 'none'; document.getElementById('forgotForm').style.display = 'none'; document.getElementById('userInfoForm').style.display = 'none'; } // 显示注册表单 function showRegisterForm() { document.getElementById('loginForm').style.display = 'none'; document.getElementById('registerForm').style.display = 'block'; document.getElementById('forgotForm').style.display = 'none'; document.getElementById('userInfoForm').style.display = 'none'; } // 显示忘记密码表单 function showForgotForm() { document.getElementById('loginForm').style.display = 'none'; document.getElementById('registerForm').style.display = 'none'; document.getElementById('forgotForm').style.display = 'block'; document.getElementById('userInfoForm').style.display = 'none'; } // 加载会员配置(禁用缓存,确保获取最新数据) async function loadMemberConfig() { try { const response = await fetch('/api/member-group/list?t=' + Date.now(), { cache: 'no-store', headers: { 'Cache-Control': 'no-cache' } }); const result = await response.json(); if (result.code === 0 && result.data) { // API 返回的是数组,包装成对象格式 memberConfig = { groups: result.data }; } else { // API返回错误,静默使用默认配置 console.warn('会员配置API返回错误:', result.msg || '未知错误'); memberConfig = defaultConfig; } } catch (error) { // 网络错误或解析错误,静默使用默认配置 console.error('加载会员配置失败:', error); memberConfig = defaultConfig; } } // 渲染权益对比(科技感卡片 + 详情表格) function renderBenefitsTable() { const groups = memberConfig.groups || defaultConfig.groups; const container = document.getElementById('benefitsTable'); const levelClasses = ['free', 'gold', 'platinum', 'diamond']; // 图标SVG const iconSvg = ``; const checkSvg = ``; const crossSvg = ``; // 生成会员卡片 const cardsHtml = groups.map((g, i) => { const cls = levelClasses[i] || 'free'; const priceText = g.price > 0 ? `¥${g.price}` : '免费'; const btnText = g.price > 0 ? '立即开通' : '当前等级'; const btnOnclick = g.price > 0 ? `onclick="buyMember(${g.id}, '${g.name}', ${g.price})"` : ''; const btnClass = g.price > 0 ? '' : 'disabled'; return `
${iconSvg}
${g.name}
${priceText} ${g.price > 0 ? '/月' : ''}
每日奖励 ${g.daily_reward || 50} 算力
赠送币 ${g.rhb_reward > 0 ? g.rhb_reward.toLocaleString() + ' ' + tokenName + '' : '-'}
并发数 ${g.concurrent_threads || 1} 个
图片保存 ${g.image_save_days === 0 ? '永久' : g.image_save_days + ' 天'}
`; }).join(''); // 生成详情表格 const benefits = [ { label: '每日登录奖励', sub: '领取后24h有效', getValue: g => `${g.daily_reward || 50} 算力` }, { label: '购买赠送', sub: '一次性', getValue: g => g.rhb_reward > 0 ? `${g.rhb_reward.toLocaleString()} ${tokenName}` : crossSvg }, { label: '并发线程', sub: '同时任务数', getValue: g => `${g.concurrent_threads || 1} 个` }, { label: '图片保存', sub: '', getValue: g => g.image_save_days === 0 ? '永久' : `${g.image_save_days} 天` }, { label: '视频保存', sub: '', getValue: g => g.video_save_days === 0 ? '永久' : `${g.video_save_days} 天` }, { label: '去水印', sub: '', getValue: g => g.features?.watermark ? checkSvg : crossSvg }, { label: '优先队列', sub: '', getValue: g => g.features?.priority_queue ? checkSvg : crossSvg }, { label: '专属模型', sub: '', getValue: g => g.features?.exclusive_models ? checkSvg : crossSvg }, { label: 'API访问', sub: '', getValue: g => g.features?.api_access ? checkSvg : crossSvg } ]; const headerCells = groups.map((g, i) => `
${g.name}
`).join(''); const rowsHtml = benefits.map(b => `
${b.label}
${b.sub ? `
${b.sub}
` : ''}
${groups.map(g => { const val = b.getValue(g); const isIcon = val.includes('${val}
`; }).join('')} `).join(''); container.innerHTML = `
${cardsHtml}
权益详情
${headerCells}
${rowsHtml}
`; } // 购买会员 async function buyMember(groupId, groupName, price) { console.log('=== buyMember 开始 ==='); // 获取用户ID - 直接从各个来源获取 let userId = null; // 1. 从 localStorage 获取 const sessionUserId = localStorage.getItem('user_id'); console.log('1. localStorage.user_id:', sessionUserId, typeof sessionUserId); // 2. 再从 localStorage 获取 const localUserId = localStorage.getItem('user_id'); console.log('2. localStorage.user_id:', localUserId, typeof localUserId); // 3. 从 user_data 获取 const userData = localStorage.getItem('user_data'); let userDataId = null; if (userData) { try { const parsed = JSON.parse(userData); userDataId = parsed.user_id; console.log('3. user_data.user_id:', userDataId, typeof userDataId); } catch(e) { console.log('3. user_data 解析失败'); } } // 确定最终的 userId userId = sessionUserId || localUserId || userDataId; console.log('4. 最终 userId:', userId, typeof userId); // 如果从 user_data 获取到了 userId,同步到 storage if (userId && !sessionUserId) { localStorage.setItem('user_id', String(userId)); localStorage.setItem('user_logged_in', '1'); } if (userId && !localUserId) { localStorage.setItem('user_id', String(userId)); } // 简化的登录检查:只要 userId 存在且不是假值 const isValid = userId && userId !== 'null' && userId !== 'undefined' && userId !== ''; console.log('5. userId 是否有效:', isValid); if (!isValid) { console.log('6. 检测失败,弹出登录提示'); showToast('请先登录后再购买', 'error'); setTimeout(() => { document.getElementById('authModal').classList.add('active'); showLoginForm(); }, 1000); return; } console.log('7. 检测通过,准备跳转支付'); showToast('正在跳转支付...', 'info'); try { const response = await fetch('/api/payment/pay', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + localStorage.getItem('user_token') }, body: JSON.stringify({ payment_type: 'alipay', pay_type: 'alipay', amount: price, product_name: `${groupName}会员(月卡)`, product_desc: `开通${groupName}会员,有效期30天`, product_type: 'member', member_group_id: groupId }) }); const result = await response.json(); if (result.code === 0 && result.data) { // 跳转支付宝支付 const payUrl = result.data.pay_url || result.data.redirect_url; if (payUrl) { showToast('正在跳转支付宝...', 'success'); // 直接跳转到支付宝 window.location.href = payUrl; } else { showToast('订单创建成功,但支付链接获取失败', 'error'); } } else { showToast(result.msg || '订单创建失败', 'error'); } } catch (error) { console.error('购买失败:', error); showToast('网络错误,请重试', 'error'); } } // 页面加载完成标志 let pageFullyLoaded = false; window.addEventListener('load', () => { pageFullyLoaded = true; }); // 显示提示 function showToast(message, type = 'info') { // 只阻止显示数据库连接相关的技术性错误 if (message && message.includes('数据库连接失败')) { console.warn('抑制技术错误提示:', message); return; } const existing = document.querySelector('.toast'); if (existing) existing.remove(); const toast = document.createElement('div'); toast.className = `toast ${type}`; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => { toast.style.animation = 'toastIn 0.4s cubic-bezier(0.4, 0, 0.2, 1) reverse'; setTimeout(() => toast.remove(), 400); }, 3000); } // ========== 代币充值相关 ========== let exchangeRate = 100; // 1元 = 100 代币 let selectedAmount = 0; let tokenName = 'R币'; // 代币名称,从配置加载 // 页面加载时初始化代币面板 document.addEventListener('DOMContentLoaded', async function() { await loadExchangeRate(); await loadUserBalance(); updateAmountOptions(); updateTokenNameDisplay(); // 监听自定义金额输入 document.getElementById('customAmount').addEventListener('input', function() { const amount = parseFloat(this.value) || 0; document.getElementById('customRhb').textContent = (amount * exchangeRate).toLocaleString(); selectedAmount = amount; updatePayButton(); }); // URL参数切换tab const hash = window.location.hash; if (hash === '#rhb') { switchTab('rhb', document.querySelector('.page-tab[data-tab="rhb"]')); } }); // 加载汇率配置和代币名称(禁用缓存) async function loadExchangeRate() { try { const response = await fetch('/api/config?t=' + Date.now(), { cache: 'no-store' }); const result = await response.json(); if (result.code === 0 && result.data) { if (result.data.rhb_exchange_rate) { exchangeRate = parseInt(result.data.rhb_exchange_rate) || 100; document.getElementById('exchangeRate').textContent = exchangeRate; } if (result.data.token_name) { tokenName = result.data.token_name; updateTokenNameDisplay(); } } } catch (error) { console.error('加载配置失败:', error); } } // 更新页面上所有代币名称显示 function updateTokenNameDisplay() { document.querySelectorAll('.token-name').forEach(el => { el.textContent = tokenName; }); } // 加载用户余额(禁用缓存) async function loadUserBalance() { const userId = localStorage.getItem('user_id') || localStorage.getItem('user_id'); if (!userId) return; try { const response = await fetch('/api/user/info?t=' + Date.now(), { cache: 'no-store', headers: { 'Authorization': 'Bearer ' + localStorage.getItem('user_token') } }); const result = await response.json(); if (result.code === 0 && result.data) { document.getElementById('currentRhb').textContent = parseFloat(result.data.rhb || 0).toLocaleString(); } } catch (error) { console.error('加载余额失败:', error); } } // 更新金额选项显示 function updateAmountOptions() { const options = document.querySelectorAll('.amount-option:not(.custom)'); options.forEach(opt => { const amount = parseInt(opt.dataset.amount); const rhb = amount * exchangeRate; const rhbEl = opt.querySelector('.amount-rhb'); if (rhbEl) { // 保留 span.token-name 元素 const tokenSpan = rhbEl.querySelector('.token-name'); if (tokenSpan) { rhbEl.innerHTML = rhb.toLocaleString() + ' ' + tokenName + ''; } else { rhbEl.textContent = rhb.toLocaleString() + ' ' + tokenName; } } }); } // 切换Tab function switchTab(tab, element) { // 更新tab样式 document.querySelectorAll('.page-tab').forEach(t => t.classList.remove('active')); element.classList.add('active'); // 切换面板 document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active')); document.getElementById(tab + 'Panel').classList.add('active'); // 更新URL window.location.hash = tab; } // 选择金额 function selectAmount(amount, element) { document.querySelectorAll('.amount-option').forEach(opt => opt.classList.remove('selected')); element.classList.add('selected'); document.getElementById('customInputRow').style.display = 'none'; selectedAmount = amount; updatePayButton(); } // 显示自定义输入 function showCustomInput() { document.querySelectorAll('.amount-option').forEach(opt => opt.classList.remove('selected')); document.querySelector('.amount-option.custom').classList.add('selected'); document.getElementById('customInputRow').style.display = 'flex'; document.getElementById('customAmount').focus(); selectedAmount = 0; updatePayButton(); } // 更新支付按钮 function updatePayButton() { document.getElementById('payAmountText').textContent = '¥' + selectedAmount; } // 支付RH币 async function payRhb() { if (selectedAmount <= 0) { showToast('请选择充值金额', 'error'); return; } // 直接获取用户ID(多重检测) let userId = localStorage.getItem('user_id') || localStorage.getItem('user_id'); // 如果直接获取不到,尝试从 user_data 解析 if (!userId || userId === 'null' || userId === 'undefined') { const userData = localStorage.getItem('user_data'); if (userData && userData !== 'null' && userData !== '{}') { try { const parsed = JSON.parse(userData); userId = parsed.user_id; if (userId) { localStorage.setItem('user_id', String(userId)); localStorage.setItem('user_id', String(userId)); localStorage.setItem('user_logged_in', '1'); } } catch(e) {} } } // 检查是否有有效的 userId if (!userId || userId === 'null' || userId === 'undefined') { showToast('请先登录后再充值', 'error'); setTimeout(() => { document.getElementById('authModal').classList.add('active'); showLoginForm(); }, 1000); return; } const rhbAmount = selectedAmount * exchangeRate; showToast('正在创建订单...', 'info'); try { const response = await fetch('/api/payment/pay', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + localStorage.getItem('user_token') }, body: JSON.stringify({ payment_type: 'alipay', pay_type: 'alipay', amount: selectedAmount, product_name: `${tokenName}充值 ${rhbAmount.toLocaleString()} 币`, product_desc: `充值 ${rhbAmount.toLocaleString()} ${tokenName}`, product_type: 'rhb', rhb_amount: rhbAmount }) }); const result = await response.json(); if (result.code === 0 && result.data) { const payUrl = result.data.pay_url || result.data.redirect_url; if (payUrl) { showToast('正在跳转支付宝...', 'success'); // 直接跳转到支付宝 window.location.href = payUrl; } else { showToast('订单创建成功,但支付链接获取失败', 'error'); } } else { showToast(result.msg || '订单创建失败', 'error'); } } catch (error) { console.error('充值失败:', error); showToast('网络错误,请重试', 'error'); } } document.getElementById('userInfoMember').textContent = latestUserData.group_name || '注册会员'; document.getElementById('userInfoRhb').textContent = latestUserData.rhb || '0'; document.getElementById('userInfoTokens').textContent = latestUserData.tokens || '0'; } // 显示登录表单 function showLoginForm() { document.getElementById('loginForm').style.display = 'block'; document.getElementById('registerForm').style.display = 'none'; document.getElementById('forgotForm').style.display = 'none'; document.getElementById('userInfoForm').style.display = 'none'; } // 显示注册表单 function showRegisterForm() { document.getElementById('loginForm').style.display = 'none'; document.getElementById('registerForm').style.display = 'block'; document.getElementById('forgotForm').style.display = 'none'; document.getElementById('userInfoForm').style.display = 'none'; } // 显示忘记密码表单 function showForgotForm() { document.getElementById('loginForm').style.display = 'none'; document.getElementById('registerForm').style.display = 'none'; document.getElementById('forgotForm').style.display = 'block'; document.getElementById('userInfoForm').style.display = 'none'; } // 加载会员配置(禁用缓存,确保获取最新数据) async function loadMemberConfig() { try { const response = await fetch('/api/member-group/list?t=' + Date.now(), { cache: 'no-store', headers: { 'Cache-Control': 'no-cache' } }); const result = await response.json(); if (result.code === 0 && result.data) { // API 返回的是数组,包装成对象格式 memberConfig = { groups: result.data }; } else { // API返回错误,静默使用默认配置 console.warn('会员配置API返回错误:', result.msg || '未知错误'); memberConfig = defaultConfig; } } catch (error) { // 网络错误或解析错误,静默使用默认配置 console.error('加载会员配置失败:', error); memberConfig = defaultConfig; } } // 渲染权益对比(科技感卡片 + 详情表格) function renderBenefitsTable() { const groups = memberConfig.groups || defaultConfig.groups; const container = document.getElementById('benefitsTable'); const levelClasses = ['free', 'gold', 'platinum', 'diamond']; // 图标SVG const iconSvg = ``; const checkSvg = ``; const crossSvg = ``; // 生成会员卡片 const cardsHtml = groups.map((g, i) => { const cls = levelClasses[i] || 'free'; const priceText = g.price > 0 ? `¥${g.price}` : '免费'; const btnText = g.price > 0 ? '立即开通' : '当前等级'; const btnOnclick = g.price > 0 ? `onclick="buyMember(${g.id}, '${g.name}', ${g.price})"` : ''; const btnClass = g.price > 0 ? '' : 'disabled'; return `
${iconSvg}
${g.name}
${priceText} ${g.price > 0 ? '/月' : ''}
每日奖励 ${g.daily_reward || 50} 算力
赠送币 ${g.rhb_reward > 0 ? g.rhb_reward.toLocaleString() + ' ' + tokenName + '' : '-'}
并发数 ${g.concurrent_threads || 1} 个
图片保存 ${g.image_save_days === 0 ? '永久' : g.image_save_days + ' 天'}
`; }).join(''); // 生成详情表格 const benefits = [ { label: '每日登录奖励', sub: '领取后24h有效', getValue: g => `${g.daily_reward || 50} 算力` }, { label: '购买赠送', sub: '一次性', getValue: g => g.rhb_reward > 0 ? `${g.rhb_reward.toLocaleString()} ${tokenName}` : crossSvg }, { label: '并发线程', sub: '同时任务数', getValue: g => `${g.concurrent_threads || 1} 个` }, { label: '图片保存', sub: '', getValue: g => g.image_save_days === 0 ? '永久' : `${g.image_save_days} 天` }, { label: '视频保存', sub: '', getValue: g => g.video_save_days === 0 ? '永久' : `${g.video_save_days} 天` }, { label: '去水印', sub: '', getValue: g => g.features?.watermark ? checkSvg : crossSvg }, { label: '优先队列', sub: '', getValue: g => g.features?.priority_queue ? checkSvg : crossSvg }, { label: '专属模型', sub: '', getValue: g => g.features?.exclusive_models ? checkSvg : crossSvg }, { label: 'API访问', sub: '', getValue: g => g.features?.api_access ? checkSvg : crossSvg } ]; const headerCells = groups.map((g, i) => `
${g.name}
`).join(''); const rowsHtml = benefits.map(b => `
${b.label}
${b.sub ? `
${b.sub}
` : ''}
${groups.map(g => { const val = b.getValue(g); const isIcon = val.includes('${val}
`; }).join('')} `).join(''); container.innerHTML = `
${cardsHtml}
权益详情
${headerCells}
${rowsHtml}
`; } // 购买会员 async function buyMember(groupId, groupName, price) { console.log('=== buyMember 开始 ==='); // 获取用户ID - 直接从各个来源获取 let userId = null; // 1. 从 localStorage 获取 const sessionUserId = localStorage.getItem('user_id'); console.log('1. localStorage.user_id:', sessionUserId, typeof sessionUserId); // 2. 再从 localStorage 获取 const localUserId = localStorage.getItem('user_id'); console.log('2. localStorage.user_id:', localUserId, typeof localUserId); // 3. 从 user_data 获取 const userData = localStorage.getItem('user_data'); let userDataId = null; if (userData) { try { const parsed = JSON.parse(userData); userDataId = parsed.user_id; console.log('3. user_data.user_id:', userDataId, typeof userDataId); } catch(e) { console.log('3. user_data 解析失败'); } } // 确定最终的 userId userId = sessionUserId || localUserId || userDataId; console.log('4. 最终 userId:', userId, typeof userId); // 如果从 user_data 获取到了 userId,同步到 storage if (userId && !sessionUserId) { localStorage.setItem('user_id', String(userId)); localStorage.setItem('user_logged_in', '1'); } if (userId && !localUserId) { localStorage.setItem('user_id', String(userId)); } // 简化的登录检查:只要 userId 存在且不是假值 const isValid = userId && userId !== 'null' && userId !== 'undefined' && userId !== ''; console.log('5. userId 是否有效:', isValid); if (!isValid) { console.log('6. 检测失败,弹出登录提示'); showToast('请先登录后再购买', 'error'); setTimeout(() => { document.getElementById('authModal').classList.add('active'); showLoginForm(); }, 1000); return; } console.log('7. 检测通过,准备跳转支付'); showToast('正在跳转支付...', 'info'); try { const response = await fetch('/api/payment/pay', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + localStorage.getItem('user_token') }, body: JSON.stringify({ payment_type: 'alipay', pay_type: 'alipay', amount: price, product_name: `${groupName}会员(月卡)`, product_desc: `开通${groupName}会员,有效期30天`, product_type: 'member', member_group_id: groupId }) }); const result = await response.json(); if (result.code === 0 && result.data) { // 跳转支付宝支付 const payUrl = result.data.pay_url || result.data.redirect_url; if (payUrl) { showToast('正在跳转支付宝...', 'success'); // 直接跳转到支付宝 window.location.href = payUrl; } else { showToast('订单创建成功,但支付链接获取失败', 'error'); } } else { showToast(result.msg || '订单创建失败', 'error'); } } catch (error) { console.error('购买失败:', error); showToast('网络错误,请重试', 'error'); } } // 页面加载完成标志 let pageFullyLoaded = false; window.addEventListener('load', () => { pageFullyLoaded = true; }); // 显示提示 function showToast(message, type = 'info') { // 只阻止显示数据库连接相关的技术性错误 if (message && message.includes('数据库连接失败')) { console.warn('抑制技术错误提示:', message); return; } const existing = document.querySelector('.toast'); if (existing) existing.remove(); const toast = document.createElement('div'); toast.className = `toast ${type}`; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => { toast.style.animation = 'toastIn 0.4s cubic-bezier(0.4, 0, 0.2, 1) reverse'; setTimeout(() => toast.remove(), 400); }, 3000); } // ========== 代币充值相关 ========== let exchangeRate = 100; // 1元 = 100 代币 let selectedAmount = 0; let tokenName = 'R币'; // 代币名称,从配置加载 // 页面加载时初始化代币面板 document.addEventListener('DOMContentLoaded', async function() { await loadExchangeRate(); await loadUserBalance(); updateAmountOptions(); updateTokenNameDisplay(); // 监听自定义金额输入 document.getElementById('customAmount').addEventListener('input', function() { const amount = parseFloat(this.value) || 0; document.getElementById('customRhb').textContent = (amount * exchangeRate).toLocaleString(); selectedAmount = amount; updatePayButton(); }); // URL参数切换tab const hash = window.location.hash; if (hash === '#rhb') { switchTab('rhb', document.querySelector('.page-tab[data-tab="rhb"]')); } }); // 加载汇率配置和代币名称(禁用缓存) async function loadExchangeRate() { try { const response = await fetch('/api/config?t=' + Date.now(), { cache: 'no-store' }); const result = await response.json(); if (result.code === 0 && result.data) { if (result.data.rhb_exchange_rate) { exchangeRate = parseInt(result.data.rhb_exchange_rate) || 100; document.getElementById('exchangeRate').textContent = exchangeRate; } if (result.data.token_name) { tokenName = result.data.token_name; updateTokenNameDisplay(); } } } catch (error) { console.error('加载配置失败:', error); } } // 更新页面上所有代币名称显示 function updateTokenNameDisplay() { document.querySelectorAll('.token-name').forEach(el => { el.textContent = tokenName; }); } // 加载用户余额(禁用缓存) async function loadUserBalance() { const userId = localStorage.getItem('user_id') || localStorage.getItem('user_id'); if (!userId) return; try { const response = await fetch('/api/user/info?t=' + Date.now(), { cache: 'no-store', headers: { 'Authorization': 'Bearer ' + localStorage.getItem('user_token') } }); const result = await response.json(); if (result.code === 0 && result.data) { document.getElementById('currentRhb').textContent = parseFloat(result.data.rhb || 0).toLocaleString(); } } catch (error) { console.error('加载余额失败:', error); } } // 更新金额选项显示 function updateAmountOptions() { const options = document.querySelectorAll('.amount-option:not(.custom)'); options.forEach(opt => { const amount = parseInt(opt.dataset.amount); const rhb = amount * exchangeRate; const rhbEl = opt.querySelector('.amount-rhb'); if (rhbEl) { // 保留 span.token-name 元素 const tokenSpan = rhbEl.querySelector('.token-name'); if (tokenSpan) { rhbEl.innerHTML = rhb.toLocaleString() + ' ' + tokenName + ''; } else { rhbEl.textContent = rhb.toLocaleString() + ' ' + tokenName; } } }); } // 切换Tab function switchTab(tab, element) { // 更新tab样式 document.querySelectorAll('.page-tab').forEach(t => t.classList.remove('active')); element.classList.add('active'); // 切换面板 document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active')); document.getElementById(tab + 'Panel').classList.add('active'); // 更新URL window.location.hash = tab; } // 选择金额 function selectAmount(amount, element) { document.querySelectorAll('.amount-option').forEach(opt => opt.classList.remove('selected')); element.classList.add('selected'); document.getElementById('customInputRow').style.display = 'none'; selectedAmount = amount; updatePayButton(); } // 显示自定义输入 function showCustomInput() { document.querySelectorAll('.amount-option').forEach(opt => opt.classList.remove('selected')); document.querySelector('.amount-option.custom').classList.add('selected'); document.getElementById('customInputRow').style.display = 'flex'; document.getElementById('customAmount').focus(); selectedAmount = 0; updatePayButton(); } // 更新支付按钮 function updatePayButton() { document.getElementById('payAmountText').textContent = '¥' + selectedAmount; } // 支付RH币 async function payRhb() { if (selectedAmount <= 0) { showToast('请选择充值金额', 'error'); return; } // 直接获取用户ID(多重检测) let userId = localStorage.getItem('user_id') || localStorage.getItem('user_id'); // 如果直接获取不到,尝试从 user_data 解析 if (!userId || userId === 'null' || userId === 'undefined') { const userData = localStorage.getItem('user_data'); if (userData && userData !== 'null' && userData !== '{}') { try { const parsed = JSON.parse(userData); userId = parsed.user_id; if (userId) { localStorage.setItem('user_id', String(userId)); localStorage.setItem('user_id', String(userId)); localStorage.setItem('user_logged_in', '1'); } } catch(e) {} } } // 检查是否有有效的 userId if (!userId || userId === 'null' || userId === 'undefined') { showToast('请先登录后再充值', 'error'); setTimeout(() => { document.getElementById('authModal').classList.add('active'); showLoginForm(); }, 1000); return; } const rhbAmount = selectedAmount * exchangeRate; showToast('正在创建订单...', 'info'); try { const response = await fetch('/api/payment/pay', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + localStorage.getItem('user_token') }, body: JSON.stringify({ payment_type: 'alipay', pay_type: 'alipay', amount: selectedAmount, product_name: `${tokenName}充值 ${rhbAmount.toLocaleString()} 币`, product_desc: `充值 ${rhbAmount.toLocaleString()} ${tokenName}`, product_type: 'rhb', rhb_amount: rhbAmount }) }); const result = await response.json(); if (result.code === 0 && result.data) { const payUrl = result.data.pay_url || result.data.redirect_url; if (payUrl) { showToast('正在跳转支付宝...', 'success'); // 直接跳转到支付宝 window.location.href = payUrl; } else { showToast('订单创建成功,但支付链接获取失败', 'error'); } } else { showToast(result.msg || '订单创建失败', 'error'); } } catch (error) { console.error('充值失败:', error); showToast('网络错误,请重试', 'error'); } } daily_reward: 500, concurrent_threads: 8, image_save_days: 0, video_save_days: 30, features: { watermark: true, priority_queue: true, exclusive_models: true, api_access: true } } ] }; // 页面加载 // 页面加载前立即同步登录状态 (function syncLoginState() { const userId = localStorage.getItem('user_id'); const userToken = localStorage.getItem('user_token'); const userData = localStorage.getItem('user_data'); if (userId && userId !== 'null' && userId !== 'undefined') { localStorage.setItem('user_logged_in', '1'); localStorage.setItem('user_id', userId); } else if (userData && userData !== 'null' && userData !== '{}') { try { const parsed = JSON.parse(userData); if (parsed.user_id) { localStorage.setItem('user_logged_in', '1'); localStorage.setItem('user_id', parsed.user_id); localStorage.setItem('user_id', parsed.user_id); } } catch(e) {} } else if (userToken && userToken !== 'null' && userToken !== 'undefined') { localStorage.setItem('user_logged_in', '1'); } })(); document.addEventListener('DOMContentLoaded', async function() { // 清除可能残留的错误提示 const existingToasts = document.querySelectorAll('.toast'); existingToasts.forEach(t => t.remove()); // 检查用户登录状态并更新头像 await checkLoginState(); await loadMemberConfig(); renderBenefitsTable(); }); // ========================================== // 登录/注册系统(与首页一致) // ========================================== const AUTH_API_URL = '/api/user'; // 检查是否已登录(多重检测,确保健壮性) function isLoggedIn() { // 检查 localStorage 中的登录标志 if (localStorage.getItem('user_logged_in') === '1') { return true; } // 检查 localStorage 中的 token(排除无效值) const token = localStorage.getItem('user_token'); if (token && token !== 'null' && token !== 'undefined' && token !== '') { return true; } // 检查 user_id 是否存在(排除无效值) const userId = localStorage.getItem('user_id') || localStorage.getItem('user_id'); if (userId && userId !== 'null' && userId !== 'undefined' && userId !== '') { localStorage.setItem('user_logged_in', '1'); return true; } // 检查 user_data 是否存在且有效 const userData = localStorage.getItem('user_data'); if (userData && userData !== 'null' && userData !== '{}') { try { const parsed = JSON.parse(userData); if (parsed.user_id) { localStorage.setItem('user_logged_in', '1'); localStorage.setItem('user_id', parsed.user_id); localStorage.setItem('user_id', parsed.user_id); return true; } } catch(e) {} } return false; } // 检查登录状态并更新UI async function checkLoginState() { const authModal = document.getElementById('authModal'); const authModalOverlay = document.getElementById('authModalOverlay'); const authCloseBtn = document.getElementById('authCloseBtn'); const avatarBtn = document.getElementById('userAvatarBtn'); const avatarImg = document.getElementById('userAvatarImg'); if (!authModal || !avatarBtn) return; // 点击头像打开弹窗(与首页一致) avatarBtn.onclick = function() { authModal.classList.add('active'); if (isLoggedIn()) { showUserInfoForm(); } else { showLoginForm(); } }; // 关闭弹窗 authCloseBtn.addEventListener('click', () => { authModal.classList.remove('active'); }); authModalOverlay.addEventListener('click', () => { authModal.classList.remove('active'); }); // 登录 document.getElementById('loginBtn')?.addEventListener('click', handleLogin); // 注册 document.getElementById('registerBtn')?.addEventListener('click', handleRegister); // 发送验证码 document.getElementById('sendCodeBtn')?.addEventListener('click', () => sendCode('register')); document.getElementById('forgotCodeBtn')?.addEventListener('click', () => sendCode('reset')); // 重置密码 document.getElementById('resetPasswordBtn')?.addEventListener('click', handleResetPassword); // 退出登录 document.getElementById('logoutBtn')?.addEventListener('click', handleLogout); // 回车登录 document.getElementById('loginPassword')?.addEventListener('keydown', (e) => { if (e.key === 'Enter') handleLogin(); }); // 如果已登录,更新头像 if (isLoggedIn()) { await updateUIForLoggedIn(); } } // 登录后更新UI async function updateUIForLoggedIn() { const userData = await fetchUserInfo(); const avatarImg = document.getElementById('userAvatarImg'); const avatarBtn = document.getElementById('userAvatarBtn'); if (avatarImg && userData.avatar) { avatarImg.src = userData.avatar; } if (avatarBtn) { avatarBtn.classList.add('logged-in'); } } // 登出后更新UI function updateUIForLoggedOut() { const avatarImg = document.getElementById('userAvatarImg'); const avatarBtn = document.getElementById('userAvatarBtn'); if (avatarImg) { avatarImg.src = 'https://api.dicebear.com/7.x/avataaars/svg?seed=neo'; } if (avatarBtn) { avatarBtn.classList.remove('logged-in'); } } // 从服务器获取最新用户信息 async function fetchUserInfo() { try { const userId = localStorage.getItem('user_id') || localStorage.getItem('user_id'); if (!userId) { return JSON.parse(localStorage.getItem('user_data') || '{}'); } const res = await fetch(`${AUTH_API_URL}/info`, { method: 'GET', headers: { 'Authorization': 'Bearer ' + localStorage.getItem('user_token') }, credentials: 'include' }); if (!res.ok) { throw new Error(`HTTP ${res.status}`); } const data = await res.json(); if (data.code === 0 && data.data) { localStorage.setItem('user_data', JSON.stringify(data.data)); return data.data; } return JSON.parse(localStorage.getItem('user_data') || '{}'); } catch (err) { console.error('获取用户信息失败:', err); return JSON.parse(localStorage.getItem('user_data') || '{}'); } } // 处理登录 async function handleLogin() { const username = document.getElementById('loginUsername').value.trim(); const password = document.getElementById('loginPassword').value.trim(); const agree = document.getElementById('loginAgree').checked; if (!username || !password) { showToast('请输入用户名和密码', 'error'); return; } if (!agree) { showToast('请同意用户协议', 'error'); return; } const btn = document.getElementById('loginBtn'); btn.disabled = true; btn.textContent = '登录中...'; try { const res = await fetch(`${AUTH_API_URL}/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); if (!res.ok) { throw new Error(`HTTP ${res.status}`); } const data = await res.json(); if (data.code === 0) { // 保存登录状态 localStorage.setItem('user_logged_in', '1'); localStorage.setItem('user_id', data.data.user_id); localStorage.setItem('user_id', data.data.user_id); localStorage.setItem('user_token', data.data.token); localStorage.setItem('user_data', JSON.stringify(data.data)); showToast('登录成功', 'success'); updateUIForLoggedIn(); // 关闭弹窗 document.getElementById('authModal').classList.remove('active'); // 刷新RH币余额显示 await loadUserBalance(); } else { showToast(data.msg || '登录失败', 'error'); } } catch (err) { showToast('网络错误,请稍后重试', 'error'); } btn.disabled = false; btn.textContent = '登录'; } // 处理注册 async function handleRegister() { const username = document.getElementById('regUsername').value.trim(); const nickname = document.getElementById('regNickname').value.trim(); const phone = document.getElementById('regPhone').value.trim(); const code = document.getElementById('regCode').value.trim(); const password = document.getElementById('regPassword').value.trim(); const passwordConfirm = document.getElementById('regPasswordConfirm').value.trim(); const agree = document.getElementById('regAgree').checked; if (!username || !phone || !code || !password || !passwordConfirm) { showToast('请填写完整信息', 'error'); return; } if (!agree) { showToast('请同意用户协议', 'error'); return; } const btn = document.getElementById('registerBtn'); btn.disabled = true; btn.textContent = '注册中...'; try { const res = await fetch(`${AUTH_API_URL}/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, nickname, phone, code, password, password_confirm: passwordConfirm }) }); if (!res.ok) { throw new Error(`HTTP ${res.status}`); } const data = await res.json(); if (data.code === 0) { localStorage.setItem('user_logged_in', '1'); localStorage.setItem('user_id', data.data.user_id); localStorage.setItem('user_id', data.data.user_id); localStorage.setItem('user_token', data.data.token); localStorage.setItem('user_data', JSON.stringify(data.data)); showToast('注册成功', 'success'); updateUIForLoggedIn(); document.getElementById('authModal').classList.remove('active'); } else { showToast(data.msg || '注册失败', 'error'); } } catch (err) { showToast('网络错误,请稍后重试', 'error'); } btn.disabled = false; btn.textContent = '注册'; } // 发送验证码 async function sendCode(type) { const phoneInput = type === 'register' ? document.getElementById('regPhone') : document.getElementById('forgotPhone'); const btn = type === 'register' ? document.getElementById('sendCodeBtn') : document.getElementById('forgotCodeBtn'); const phone = phoneInput.value.trim(); if (!phone) { showToast('请输入手机号', 'error'); return; } if (!/^1[3-9]\d{9}$/.test(phone)) { showToast('手机号格式不正确', 'error'); return; } btn.disabled = true; try { const res = await fetch(`${AUTH_API_URL}/send_code`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ phone, type }) }); if (!res.ok) { throw new Error(`HTTP ${res.status}`); } const data = await res.json(); if (data.code === 0) { showToast('验证码已发送', 'success'); let countdown = 60; btn.textContent = `${countdown}秒后重发`; const timer = setInterval(() => { countdown--; if (countdown <= 0) { clearInterval(timer); btn.disabled = false; btn.textContent = '获取验证码'; } else { btn.textContent = `${countdown}秒后重发`; } }, 1000); } else { showToast(data.msg || '发送失败', 'error'); btn.disabled = false; } } catch (err) { showToast('网络错误,请稍后重试', 'error'); btn.disabled = false; } } // 重置密码 async function handleResetPassword() { const phone = document.getElementById('forgotPhone').value.trim(); const code = document.getElementById('forgotCode').value.trim(); const password = document.getElementById('forgotPassword').value.trim(); const passwordConfirm = document.getElementById('forgotPasswordConfirm').value.trim(); if (!phone || !code || !password || !passwordConfirm) { showToast('请填写完整信息', 'error'); return; } const btn = document.getElementById('resetPasswordBtn'); btn.disabled = true; btn.textContent = '重置中...'; try { const res = await fetch(`${AUTH_API_URL}/reset_password`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ phone, code, password, password_confirm: passwordConfirm }) }); if (!res.ok) { throw new Error(`HTTP ${res.status}`); } const data = await res.json(); if (data.code === 0) { showToast('密码重置成功,请登录', 'success'); showLoginForm(); } else { showToast(data.msg || '重置失败', 'error'); } } catch (err) { showToast('网络错误,请稍后重试', 'error'); } btn.disabled = false; btn.textContent = '重置密码'; } // 退出登录 async function handleLogout() { try { await fetch(`${AUTH_API_URL}/logout`, { method: 'POST' }); } catch (e) { // 忽略 } // 清除本地存储 localStorage.removeItem('user_logged_in'); localStorage.removeItem('user_id'); localStorage.removeItem('user_id'); localStorage.removeItem('user_token'); localStorage.removeItem('user_data'); showToast('已退出登录', 'success'); updateUIForLoggedOut(); document.getElementById('authModal').classList.remove('active'); // 重置余额显示 document.getElementById('currentRhb').textContent = '0'; } // 显示用户信息面板 async function showUserInfoForm() { document.getElementById('loginForm').style.display = 'none'; document.getElementById('registerForm').style.display = 'none'; document.getElementById('forgotForm').style.display = 'none'; document.getElementById('userInfoForm').style.display = 'block'; // 先显示缓存的数据 const userData = JSON.parse(localStorage.getItem('user_data') || '{}'); document.getElementById('userInfoAvatar').src = userData.avatar || 'https://api.dicebear.com/7.x/avataaars/svg?seed=neo'; document.getElementById('userInfoNickname').textContent = userData.nickname || userData.username || '用户'; document.getElementById('userInfoMember').textContent = userData.group_name || '注册会员'; document.getElementById('userInfoRhb').textContent = '加载中...'; document.getElementById('userInfoTokens').textContent = userData.tokens || '0'; // 从服务器获取最新数据 const latestUserData = await fetchUserInfo(); document.getElementById('userInfoAvatar').src = latestUserData.avatar || 'https://api.dicebear.com/7.x/avataaars/svg?seed=neo'; document.getElementById('userInfoNickname').textContent = latestUserData.nickname || latestUserData.username || '用户'; document.getElementById('userInfoMember').textContent = latestUserData.group_name || '注册会员'; document.getElementById('userInfoRhb').textContent = latestUserData.rhb || '0'; document.getElementById('userInfoTokens').textContent = latestUserData.tokens || '0'; } // 显示登录表单 function showLoginForm() { document.getElementById('loginForm').style.display = 'block'; document.getElementById('registerForm').style.display = 'none'; document.getElementById('forgotForm').style.display = 'none'; document.getElementById('userInfoForm').style.display = 'none'; } // 显示注册表单 function showRegisterForm() { document.getElementById('loginForm').style.display = 'none'; document.getElementById('registerForm').style.display = 'block'; document.getElementById('forgotForm').style.display = 'none'; document.getElementById('userInfoForm').style.display = 'none'; } // 显示忘记密码表单 function showForgotForm() { document.getElementById('loginForm').style.display = 'none'; document.getElementById('registerForm').style.display = 'none'; document.getElementById('forgotForm').style.display = 'block'; document.getElementById('userInfoForm').style.display = 'none'; } // 加载会员配置(禁用缓存,确保获取最新数据) async function loadMemberConfig() { try { const response = await fetch('/api/member-group/list?t=' + Date.now(), { cache: 'no-store', headers: { 'Cache-Control': 'no-cache' } }); const result = await response.json(); if (result.code === 0 && result.data) { // API 返回的是数组,包装成对象格式 memberConfig = { groups: result.data }; } else { // API返回错误,静默使用默认配置 console.warn('会员配置API返回错误:', result.msg || '未知错误'); memberConfig = defaultConfig; } } catch (error) { // 网络错误或解析错误,静默使用默认配置 console.error('加载会员配置失败:', error); memberConfig = defaultConfig; } } // 渲染权益对比(科技感卡片 + 详情表格) function renderBenefitsTable() { const groups = memberConfig.groups || defaultConfig.groups; const container = document.getElementById('benefitsTable'); const levelClasses = ['free', 'gold', 'platinum', 'diamond']; // 图标SVG const iconSvg = ``; const checkSvg = ``; const crossSvg = ``; // 生成会员卡片 const cardsHtml = groups.map((g, i) => { const cls = levelClasses[i] || 'free'; const priceText = g.price > 0 ? `¥${g.price}` : '免费'; const btnText = g.price > 0 ? '立即开通' : '当前等级'; const btnOnclick = g.price > 0 ? `onclick="buyMember(${g.id}, '${g.name}', ${g.price})"` : ''; const btnClass = g.price > 0 ? '' : 'disabled'; return `
${iconSvg}
${g.name}
${priceText} ${g.price > 0 ? '/月' : ''}
每日奖励 ${g.daily_reward || 50} 算力
赠送币 ${g.rhb_reward > 0 ? g.rhb_reward.toLocaleString() + ' ' + tokenName + '' : '-'}
并发数 ${g.concurrent_threads || 1} 个
图片保存 ${g.image_save_days === 0 ? '永久' : g.image_save_days + ' 天'}
`; }).join(''); // 生成详情表格 const benefits = [ { label: '每日登录奖励', sub: '领取后24h有效', getValue: g => `${g.daily_reward || 50} 算力` }, { label: '购买赠送', sub: '一次性', getValue: g => g.rhb_reward > 0 ? `${g.rhb_reward.toLocaleString()} ${tokenName}` : crossSvg }, { label: '并发线程', sub: '同时任务数', getValue: g => `${g.concurrent_threads || 1} 个` }, { label: '图片保存', sub: '', getValue: g => g.image_save_days === 0 ? '永久' : `${g.image_save_days} 天` }, { label: '视频保存', sub: '', getValue: g => g.video_save_days === 0 ? '永久' : `${g.video_save_days} 天` }, { label: '去水印', sub: '', getValue: g => g.features?.watermark ? checkSvg : crossSvg }, { label: '优先队列', sub: '', getValue: g => g.features?.priority_queue ? checkSvg : crossSvg }, { label: '专属模型', sub: '', getValue: g => g.features?.exclusive_models ? checkSvg : crossSvg }, { label: 'API访问', sub: '', getValue: g => g.features?.api_access ? checkSvg : crossSvg } ]; const headerCells = groups.map((g, i) => `
${g.name}
`).join(''); const rowsHtml = benefits.map(b => `
${b.label}
${b.sub ? `
${b.sub}
` : ''}
${groups.map(g => { const val = b.getValue(g); const isIcon = val.includes('${val}
`; }).join('')} `).join(''); container.innerHTML = `
${cardsHtml}
权益详情
${headerCells}
${rowsHtml}
`; } // 购买会员 async function buyMember(groupId, groupName, price) { console.log('=== buyMember 开始 ==='); // 获取用户ID - 直接从各个来源获取 let userId = null; // 1. 从 localStorage 获取 const sessionUserId = localStorage.getItem('user_id'); console.log('1. localStorage.user_id:', sessionUserId, typeof sessionUserId); // 2. 再从 localStorage 获取 const localUserId = localStorage.getItem('user_id'); console.log('2. localStorage.user_id:', localUserId, typeof localUserId); // 3. 从 user_data 获取 const userData = localStorage.getItem('user_data'); let userDataId = null; if (userData) { try { const parsed = JSON.parse(userData); userDataId = parsed.user_id; console.log('3. user_data.user_id:', userDataId, typeof userDataId); } catch(e) { console.log('3. user_data 解析失败'); } } // 确定最终的 userId userId = sessionUserId || localUserId || userDataId; console.log('4. 最终 userId:', userId, typeof userId); // 如果从 user_data 获取到了 userId,同步到 storage if (userId && !sessionUserId) { localStorage.setItem('user_id', String(userId)); localStorage.setItem('user_logged_in', '1'); } if (userId && !localUserId) { localStorage.setItem('user_id', String(userId)); } // 简化的登录检查:只要 userId 存在且不是假值 const isValid = userId && userId !== 'null' && userId !== 'undefined' && userId !== ''; console.log('5. userId 是否有效:', isValid); if (!isValid) { console.log('6. 检测失败,弹出登录提示'); showToast('请先登录后再购买', 'error'); setTimeout(() => { document.getElementById('authModal').classList.add('active'); showLoginForm(); }, 1000); return; } console.log('7. 检测通过,准备跳转支付'); showToast('正在跳转支付...', 'info'); try { const response = await fetch('/api/payment/pay', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + localStorage.getItem('user_token') }, body: JSON.stringify({ payment_type: 'alipay', pay_type: 'alipay', amount: price, product_name: `${groupName}会员(月卡)`, product_desc: `开通${groupName}会员,有效期30天`, product_type: 'member', member_group_id: groupId }) }); const result = await response.json(); if (result.code === 0 && result.data) { // 跳转支付宝支付 const payUrl = result.data.pay_url || result.data.redirect_url; if (payUrl) { showToast('正在跳转支付宝...', 'success'); // 直接跳转到支付宝 window.location.href = payUrl; } else { showToast('订单创建成功,但支付链接获取失败', 'error'); } } else { showToast(result.msg || '订单创建失败', 'error'); } } catch (error) { console.error('购买失败:', error); showToast('网络错误,请重试', 'error'); } } // 页面加载完成标志 let pageFullyLoaded = false; window.addEventListener('load', () => { pageFullyLoaded = true; }); // 显示提示 function showToast(message, type = 'info') { // 只阻止显示数据库连接相关的技术性错误 if (message && message.includes('数据库连接失败')) { console.warn('抑制技术错误提示:', message); return; } const existing = document.querySelector('.toast'); if (existing) existing.remove(); const toast = document.createElement('div'); toast.className = `toast ${type}`; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => { toast.style.animation = 'toastIn 0.4s cubic-bezier(0.4, 0, 0.2, 1) reverse'; setTimeout(() => toast.remove(), 400); }, 3000); } // ========== 代币充值相关 ========== let exchangeRate = 100; // 1元 = 100 代币 let selectedAmount = 0; let tokenName = 'R币'; // 代币名称,从配置加载 // 页面加载时初始化代币面板 document.addEventListener('DOMContentLoaded', async function() { await loadExchangeRate(); await loadUserBalance(); updateAmountOptions(); updateTokenNameDisplay(); // 监听自定义金额输入 document.getElementById('customAmount').addEventListener('input', function() { const amount = parseFloat(this.value) || 0; document.getElementById('customRhb').textContent = (amount * exchangeRate).toLocaleString(); selectedAmount = amount; updatePayButton(); }); // URL参数切换tab const hash = window.location.hash; if (hash === '#rhb') { switchTab('rhb', document.querySelector('.page-tab[data-tab="rhb"]')); } }); // 加载汇率配置和代币名称(禁用缓存) async function loadExchangeRate() { try { const response = await fetch('/api/config?t=' + Date.now(), { cache: 'no-store' }); const result = await response.json(); if (result.code === 0 && result.data) { if (result.data.rhb_exchange_rate) { exchangeRate = parseInt(result.data.rhb_exchange_rate) || 100; document.getElementById('exchangeRate').textContent = exchangeRate; } if (result.data.token_name) { tokenName = result.data.token_name; updateTokenNameDisplay(); } } } catch (error) { console.error('加载配置失败:', error); } } // 更新页面上所有代币名称显示 function updateTokenNameDisplay() { document.querySelectorAll('.token-name').forEach(el => { el.textContent = tokenName; }); } // 加载用户余额(禁用缓存) async function loadUserBalance() { const userId = localStorage.getItem('user_id') || localStorage.getItem('user_id'); if (!userId) return; try { const response = await fetch('/api/user/info?t=' + Date.now(), { cache: 'no-store', headers: { 'Authorization': 'Bearer ' + localStorage.getItem('user_token') } }); const result = await response.json(); if (result.code === 0 && result.data) { document.getElementById('currentRhb').textContent = parseFloat(result.data.rhb || 0).toLocaleString(); } } catch (error) { console.error('加载余额失败:', error); } } // 更新金额选项显示 function updateAmountOptions() { const options = document.querySelectorAll('.amount-option:not(.custom)'); options.forEach(opt => { const amount = parseInt(opt.dataset.amount); const rhb = amount * exchangeRate; const rhbEl = opt.querySelector('.amount-rhb'); if (rhbEl) { // 保留 span.token-name 元素 const tokenSpan = rhbEl.querySelector('.token-name'); if (tokenSpan) { rhbEl.innerHTML = rhb.toLocaleString() + ' ' + tokenName + ''; } else { rhbEl.textContent = rhb.toLocaleString() + ' ' + tokenName; } } }); } // 切换Tab function switchTab(tab, element) { // 更新tab样式 document.querySelectorAll('.page-tab').forEach(t => t.classList.remove('active')); element.classList.add('active'); // 切换面板 document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active')); document.getElementById(tab + 'Panel').classList.add('active'); // 更新URL window.location.hash = tab; } // 选择金额 function selectAmount(amount, element) { document.querySelectorAll('.amount-option').forEach(opt => opt.classList.remove('selected')); element.classList.add('selected'); document.getElementById('customInputRow').style.display = 'none'; selectedAmount = amount; updatePayButton(); } // 显示自定义输入 function showCustomInput() { document.querySelectorAll('.amount-option').forEach(opt => opt.classList.remove('selected')); document.querySelector('.amount-option.custom').classList.add('selected'); document.getElementById('customInputRow').style.display = 'flex'; document.getElementById('customAmount').focus(); selectedAmount = 0; updatePayButton(); } // 更新支付按钮 function updatePayButton() { document.getElementById('payAmountText').textContent = '¥' + selectedAmount; } // 支付RH币 async function payRhb() { if (selectedAmount <= 0) { showToast('请选择充值金额', 'error'); return; } // 直接获取用户ID(多重检测) let userId = localStorage.getItem('user_id') || localStorage.getItem('user_id'); // 如果直接获取不到,尝试从 user_data 解析 if (!userId || userId === 'null' || userId === 'undefined') { const userData = localStorage.getItem('user_data'); if (userData && userData !== 'null' && userData !== '{}') { try { const parsed = JSON.parse(userData); userId = parsed.user_id; if (userId) { localStorage.setItem('user_id', String(userId)); localStorage.setItem('user_id', String(userId)); localStorage.setItem('user_logged_in', '1'); } } catch(e) {} } } // 检查是否有有效的 userId if (!userId || userId === 'null' || userId === 'undefined') { showToast('请先登录后再充值', 'error'); setTimeout(() => { document.getElementById('authModal').classList.add('active'); showLoginForm(); }, 1000); return; } const rhbAmount = selectedAmount * exchangeRate; showToast('正在创建订单...', 'info'); try { const response = await fetch('/api/payment/pay', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + localStorage.getItem('user_token') }, body: JSON.stringify({ payment_type: 'alipay', pay_type: 'alipay', amount: selectedAmount, product_name: `${tokenName}充值 ${rhbAmount.toLocaleString()} 币`, product_desc: `充值 ${rhbAmount.toLocaleString()} ${tokenName}`, product_type: 'rhb', rhb_amount: rhbAmount }) }); const result = await response.json(); if (result.code === 0 && result.data) { const payUrl = result.data.pay_url || result.data.redirect_url; if (payUrl) { showToast('正在跳转支付宝...', 'success'); // 直接跳转到支付宝 window.location.href = payUrl; } else { showToast('订单创建成功,但支付链接获取失败', 'error'); } } else { showToast(result.msg || '订单创建失败', 'error'); } } catch (error) { console.error('充值失败:', error); showToast('网络错误,请重试', 'error'); } }