加载中
`; el.innerHTML = `

公网穿透

${active ? '已激活' : '未激活'}

设置别名后,公网地址将变为 ${alias ? escapeHtml(alias) : '别名'}.lezhuanyou.cn。别名全局唯一,需未被其他用户占用。

⚠️ 公网穿透需要客户端在线运行(WSS 连接到中转服务器)。客户端离线时访客无法访问。
`; } // 保存客服别名:调用 PUT /api/v1/users/me/tunnel/alias // 空字符串清除别名回退到默认子域名;服务端校验全局唯一性 async function saveTunnelAlias() { const alias = document.getElementById('tunnelAlias').value.trim(); const msgEl = document.getElementById('aliasMsg'); const btn = document.getElementById('saveAliasBtn'); // 前端校验:空字符串允许(清除别名);非空需 4-10 字符 if (alias && !/^[a-zA-Z0-9_-]{4,10}$/.test(alias)) { msgEl.innerHTML = '
别名需为 4-10 字符,仅限字母、数字、下划线或连字符
'; return; } btn.disabled = true; btn.textContent = '保存中...'; const resp = await AuthAPI.setTunnelAlias(alias); btn.disabled = false; btn.textContent = '保存别名'; if (resp.ok && resp.data && resp.data.success !== false) { const newAlias = resp.data.alias || ''; const newUrl = resp.data.publicUrl || ''; msgEl.innerHTML = '
✓ 别名保存成功
'; // 更新公网地址显示 const urlInput = document.querySelector('.edit-section .form-input[readonly]'); if (urlInput && newUrl) urlInput.value = newUrl; document.getElementById('tunnelAlias').value = newAlias; } else { const errMsg = (resp.data && (resp.data.message || (resp.data.error && resp.data.error.message))) || '保存失败'; msgEl.innerHTML = `
${escapeHtml(errMsg)}
`; } } function renderEditForm(el) { const u = currentUser || {}; const sub = currentSubscription || {}; const mb = memberBadge(sub.active, sub.expiresAt); el.innerHTML = `

编辑个人资料

修改手机号

MEMBERSHIP

${tierMap[sub.tier] || '基础版'}
订阅等级 Lv.${sub.tier || 0} · ${mb.text} · 到期:${sub.active ? formatExpiry(sub.expiresAt) : '—'} · ${u.status === 'active' ? '账户正常' : '账户状态: ' + (u.status || '未知')}

KNOWLEDGE BASE

${(sub.knowledgeBase && sub.knowledgeBase.active) ? '已订阅' : '未订阅'}
${(sub.knowledgeBase && sub.knowledgeBase.active) ? '有效' : '未订阅'} · 到期:${(sub.knowledgeBase && sub.knowledgeBase.active) ? formatExpiry(sub.knowledgeBase.expiresAt) : '—'}
`; } async function saveProfile() { const displayName = document.getElementById('editDisplayName').value.trim(); const email = document.getElementById('editEmail').value.trim(); const bio = document.getElementById('editBio').value.trim(); const website = document.getElementById('editWebsite').value.trim(); const msgEl = document.getElementById('editMsg'); // 昵称前端校验(与后端一致) if (displayName) { const displayNameLen = [...displayName].length; if (displayNameLen < 2 || displayNameLen > 32) { msgEl.innerHTML = '
昵称需为2-32字符
'; return; } // tier < 7 的用户禁止使用"官方/管理/客服/系统"等敏感词 const userTier = (currentUser && currentUser.subscriptionTier) || 0; if (userTier < 7 && /官方|管理|客服|系统/.test(displayName)) { msgEl.innerHTML = '
昵称禁止包含「官方」「管理」「客服」「系统」
'; return; } } const data = {}; if (displayName) data.displayName = displayName; if (email) data.email = email; if (bio) data.bio = bio; if (website) data.website = website; const resp = await AuthAPI.updateProfile(data); if (resp.ok) { msgEl.innerHTML = '
✓ 资料更新成功
'; await loadProfile(); setTimeout(() => renderEditForm(document.getElementById('tabContent')), 800); } else { msgEl.innerHTML = `
${(resp.data && resp.data.message) || '更新失败'}
`; } } // ===== 修改手机号:发送验证码 / 倒计时 / 确认修改 ===== let phoneCountdown = 0; let phoneTimer = null; async function sendChangePhoneCode() { const newPhone = document.getElementById('newPhone').value.trim(); const msgEl = document.getElementById('phoneMsg'); if (!/^1[3-9]\d{9}$/.test(newPhone)) { msgEl.innerHTML = '
请输入有效的11位手机号
'; return; } if (currentUser && newPhone === currentUser.phone) { msgEl.innerHTML = '
新手机号不能与当前手机号相同
'; return; } const btn = document.getElementById('changePhoneCodeBtn'); btn.disabled = true; const resp = await AuthAPI.sendChangePhoneCode(newPhone); if (resp.ok) { msgEl.innerHTML = '
✓ 验证码已发送到新手机号
'; startPhoneCountdown(); } else { msgEl.innerHTML = `
${(resp.data && resp.data.message) || '发送失败'}
`; btn.disabled = false; btn.textContent = '发送验证码'; } } function startPhoneCountdown() { phoneCountdown = 60; const btn = document.getElementById('changePhoneCodeBtn'); btn.disabled = true; btn.textContent = `${phoneCountdown}s 后重试`; phoneTimer = setInterval(() => { phoneCountdown--; if (phoneCountdown <= 0) { clearInterval(phoneTimer); btn.disabled = false; btn.textContent = '发送验证码'; } else { btn.textContent = `${phoneCountdown}s 后重试`; } }, 1000); } async function confirmChangePhone() { const newPhone = document.getElementById('newPhone').value.trim(); const smsCode = document.getElementById('changePhoneSmsCode').value.trim(); const msgEl = document.getElementById('phoneMsg'); if (!/^1[3-9]\d{9}$/.test(newPhone)) { msgEl.innerHTML = '
请输入有效的11位新手机号
'; return; } if (!/^\d{6}$/.test(smsCode)) { msgEl.innerHTML = '
请输入6位短信验证码
'; return; } const resp = await AuthAPI.updatePhone(newPhone, smsCode); if (resp.ok) { msgEl.innerHTML = '
✓ 手机号修改成功,请使用新手机号登录
'; // 手机号已变更,当前 token 仍有效但建议重新登录 setTimeout(() => { Auth.logout(); }, 2000); } else { msgEl.innerHTML = `
${(resp.data && resp.data.message) || '修改失败'}
`; } } // 初始化加载个人资料和第一个标签 (async () => { await loadProfile(); const firstTab = document.querySelector('.tab'); if (firstTab) { switchTab('purchased', { target: firstTab }); } })();