`;
el.innerHTML = `
公网穿透
⚠️ 公网穿透需要客户端在线运行(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 });
}
})();