feat: 我的界面功能完善
用户编辑: - 用户头像选择(15种预设头像) - 用户名修改(唯一性检查) - 个性签名 - 个人资料(手机号只读、邮箱、性别、年龄、地区) 消息通知: - 通知开关、声音、震动设置 - 免打扰时段设置 隐私安全: - 修改密码 - 账号信息查看 - 清除聊天记录 - 清除缓存 - 数据安全状态显示 帮助与反馈: - 使用帮助指南 - 问题反馈表单 - 联系方式 关于: - 版本信息 - 检查更新 - 隐私政策/用户协议入口
This commit is contained in:
919
www/app.js
919
www/app.js
@@ -1331,6 +1331,8 @@ function showAgentFavoritePage() {
|
||||
|
||||
function renderProfilePage() {
|
||||
const quota = getRemainingQuota();
|
||||
const userAvatar = currentUser?.avatar || '👤';
|
||||
const userSignature = currentUser?.signature || '这个人很懒,什么都没写~';
|
||||
|
||||
return `
|
||||
<div class="profile-page">
|
||||
@@ -1339,22 +1341,24 @@ function renderProfilePage() {
|
||||
</header>
|
||||
|
||||
<div class="profile-content">
|
||||
<div class="profile-card">
|
||||
<div class="profile-avatar">${currentUser ? '👤' : '👤'}</div>
|
||||
<!-- 用户卡片 -->
|
||||
<div class="profile-user-card" id="userCard">
|
||||
<div class="profile-avatar-large" id="profileAvatar">${userAvatar}</div>
|
||||
<div class="profile-user-info">
|
||||
<div class="profile-name">${currentUser ? currentUser.username : '游客'}</div>
|
||||
<div class="profile-signature">${currentUser ? userSignature : '登录后解锁更多功能'}</div>
|
||||
</div>
|
||||
${currentUser
|
||||
? `<div class="profile-status">已登录</div>
|
||||
<button class="logout-btn" id="logoutBtn">退出登录</button>`
|
||||
: `<div class="profile-status guest">未登录</div>
|
||||
<div class="profile-login-buttons">
|
||||
<button class="login-btn" id="loginBtn">登录</button>
|
||||
<button class="register-btn" id="registerBtn">注册</button>
|
||||
<button class="skip-btn" id="skipBtn">跳过,继续使用</button>
|
||||
? `<div class="profile-arrow">›</div>`
|
||||
: `<div class="profile-login-buttons-inline">
|
||||
<button class="login-btn-small" id="loginBtnSmall">登录</button>
|
||||
<button class="register-btn-small" id="registerBtnSmall">注册</button>
|
||||
</div>`
|
||||
}
|
||||
</div>
|
||||
|
||||
${!currentUser && quota ? `
|
||||
<!-- 游客配额 -->
|
||||
<div class="quota-card">
|
||||
<div class="quota-title">今日使用配额(游客)</div>
|
||||
<div class="quota-items">
|
||||
@@ -1370,28 +1374,73 @@ function renderProfilePage() {
|
||||
<span class="quota-label">智能体消息</span>
|
||||
<span class="quota-value">${quota.agentMsgRemain}/${GUEST_LIMITS.maxAgentMessagesPerDay}</span>
|
||||
</div>
|
||||
<div class="quota-item">
|
||||
<span class="quota-label">可用智能体</span>
|
||||
<span class="quota-value">仅通用助手 🤖</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="quota-tip">登录后解锁全部功能</div>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<div class="profile-section">
|
||||
<div class="profile-item" id="aboutBtn">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20"><path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>
|
||||
<span>关于</span>
|
||||
<!-- 功能菜单 -->
|
||||
<div class="profile-menu-section">
|
||||
${currentUser ? `
|
||||
<div class="profile-menu-item" id="editProfileBtn">
|
||||
<div class="menu-icon">👤</div>
|
||||
<span class="menu-text">用户编辑</span>
|
||||
<span class="menu-desc">头像、用户名、签名、资料</span>
|
||||
<span class="menu-arrow">›</span>
|
||||
</div>
|
||||
<div class="profile-item" id="clearDataBtn">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20"><path fill="currentColor" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
|
||||
<span>清除数据</span>
|
||||
` : ''}
|
||||
|
||||
<div class="profile-menu-item" id="notificationBtn">
|
||||
<div class="menu-icon">🔔</div>
|
||||
<span class="menu-text">消息通知</span>
|
||||
<span class="menu-desc">通知设置、消息提醒</span>
|
||||
<span class="menu-arrow">›</span>
|
||||
</div>
|
||||
|
||||
<div class="profile-menu-item" id="privacyBtn">
|
||||
<div class="menu-icon">🔒</div>
|
||||
<span class="menu-text">隐私安全</span>
|
||||
<span class="menu-desc">账号安全、隐私设置</span>
|
||||
<span class="menu-arrow">›</span>
|
||||
</div>
|
||||
|
||||
<div class="profile-menu-item" id="helpBtn">
|
||||
<div class="menu-icon">❓</div>
|
||||
<span class="menu-text">帮助与反馈</span>
|
||||
<span class="menu-desc">使用帮助、问题反馈</span>
|
||||
<span class="menu-arrow">›</span>
|
||||
</div>
|
||||
|
||||
<div class="profile-menu-item" id="aboutBtn">
|
||||
<div class="menu-icon">📋</div>
|
||||
<span class="menu-text">关于</span>
|
||||
<span class="menu-desc">版本信息</span>
|
||||
<span class="menu-arrow">›</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 其他操作 -->
|
||||
${currentUser ? `
|
||||
<div class="profile-menu-section">
|
||||
<div class="profile-menu-item danger" id="logoutBtn">
|
||||
<div class="menu-icon">🚪</div>
|
||||
<span class="menu-text">退出登录</span>
|
||||
<span class="menu-arrow">›</span>
|
||||
</div>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<div class="profile-menu-section">
|
||||
<div class="profile-menu-item danger" id="clearDataBtn">
|
||||
<div class="menu-icon">🗑️</div>
|
||||
<span class="menu-text">清除数据</span>
|
||||
<span class="menu-desc">删除所有本地数据</span>
|
||||
<span class="menu-arrow">›</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="profile-footer">
|
||||
<p>AI助手 v3.4.1</p>
|
||||
<p>AI助手 v3.5.0</p>
|
||||
<p>基于智谱 GLM-4.5-Air</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1400,28 +1449,67 @@ function renderProfilePage() {
|
||||
}
|
||||
|
||||
function bindProfilePageEvents() {
|
||||
// 登录按钮
|
||||
const loginBtn = document.getElementById('loginBtn');
|
||||
if (loginBtn) {
|
||||
loginBtn.addEventListener('click', () => {
|
||||
// 用户卡片点击(已登录用户进入编辑页面)
|
||||
const userCard = document.getElementById('userCard');
|
||||
if (userCard && currentUser) {
|
||||
userCard.addEventListener('click', () => {
|
||||
showEditProfilePage();
|
||||
});
|
||||
}
|
||||
|
||||
// 小登录按钮
|
||||
const loginBtnSmall = document.getElementById('loginBtnSmall');
|
||||
if (loginBtnSmall) {
|
||||
loginBtnSmall.addEventListener('click', () => {
|
||||
showLoginPage();
|
||||
});
|
||||
}
|
||||
|
||||
// 注册按钮
|
||||
const registerBtn = document.getElementById('registerBtn');
|
||||
if (registerBtn) {
|
||||
registerBtn.addEventListener('click', () => {
|
||||
// 小注册按钮
|
||||
const registerBtnSmall = document.getElementById('registerBtnSmall');
|
||||
if (registerBtnSmall) {
|
||||
registerBtnSmall.addEventListener('click', () => {
|
||||
showRegisterPage();
|
||||
});
|
||||
}
|
||||
|
||||
// 跳过按钮(游客模式)
|
||||
const skipBtn = document.getElementById('skipBtn');
|
||||
if (skipBtn) {
|
||||
skipBtn.addEventListener('click', () => {
|
||||
showToast('以游客模式继续使用');
|
||||
switchPage('chats');
|
||||
// 用户编辑按钮
|
||||
const editProfileBtn = document.getElementById('editProfileBtn');
|
||||
if (editProfileBtn) {
|
||||
editProfileBtn.addEventListener('click', () => {
|
||||
showEditProfilePage();
|
||||
});
|
||||
}
|
||||
|
||||
// 消息通知按钮
|
||||
const notificationBtn = document.getElementById('notificationBtn');
|
||||
if (notificationBtn) {
|
||||
notificationBtn.addEventListener('click', () => {
|
||||
showNotificationPage();
|
||||
});
|
||||
}
|
||||
|
||||
// 隐私安全按钮
|
||||
const privacyBtn = document.getElementById('privacyBtn');
|
||||
if (privacyBtn) {
|
||||
privacyBtn.addEventListener('click', () => {
|
||||
showPrivacyPage();
|
||||
});
|
||||
}
|
||||
|
||||
// 帮助与反馈按钮
|
||||
const helpBtn = document.getElementById('helpBtn');
|
||||
if (helpBtn) {
|
||||
helpBtn.addEventListener('click', () => {
|
||||
showHelpPage();
|
||||
});
|
||||
}
|
||||
|
||||
// 关于按钮
|
||||
const aboutBtn = document.getElementById('aboutBtn');
|
||||
if (aboutBtn) {
|
||||
aboutBtn.addEventListener('click', () => {
|
||||
showAboutPage();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1454,6 +1542,767 @@ function bindProfilePageEvents() {
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 用户编辑页面 ====================
|
||||
|
||||
function showEditProfilePage() {
|
||||
if (!currentUser) {
|
||||
showToast('请先登录');
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取完整用户信息
|
||||
const users = JSON.parse(localStorage.getItem('registeredUsers') || '[]');
|
||||
const fullUser = users.find(u => u.username === currentUser.username);
|
||||
|
||||
const avatar = currentUser.avatar || '👤';
|
||||
const signature = currentUser.signature || '';
|
||||
const phone = fullUser?.phone || '';
|
||||
const email = fullUser?.email || '';
|
||||
const gender = currentUser.gender || '';
|
||||
const age = currentUser.age || '';
|
||||
const region = currentUser.region || '';
|
||||
|
||||
const editHtml = `
|
||||
<div class="edit-profile-page">
|
||||
<header class="edit-header">
|
||||
<button class="back-btn-white" id="editBackBtn">
|
||||
<svg viewBox="0 0 24 24" width="24" height="24"><path fill="currentColor" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
|
||||
</button>
|
||||
<h1>用户编辑</h1>
|
||||
</header>
|
||||
|
||||
<div class="edit-content">
|
||||
<!-- 头像选择 -->
|
||||
<div class="edit-section">
|
||||
<div class="edit-avatar-section">
|
||||
<div class="edit-avatar-preview" id="editAvatarPreview">${avatar}</div>
|
||||
<div class="edit-avatar-label">点击更换头像</div>
|
||||
</div>
|
||||
<div class="avatar-selector" id="avatarSelector">
|
||||
${['👤', '😊', '😎', '🤓', '🧑', '👨', '👩', '🧒', '👴', '👵', '🦸', '🧙', '🤖', '👽', '🥷'].map(a => `
|
||||
<div class="avatar-option ${a === avatar ? 'selected' : ''}" data-avatar="${a}">${a}</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<div class="edit-section">
|
||||
<div class="edit-section-title">基本信息</div>
|
||||
|
||||
<div class="edit-input-group">
|
||||
<label>用户名</label>
|
||||
<input type="text" id="editUsername" value="${currentUser.username}" maxlength="20">
|
||||
</div>
|
||||
|
||||
<div class="edit-input-group">
|
||||
<label>个性签名</label>
|
||||
<textarea id="editSignature" placeholder="写点什么介绍自己..." maxlength="50">${signature}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 个人资料 -->
|
||||
<div class="edit-section">
|
||||
<div class="edit-section-title">个人资料</div>
|
||||
|
||||
<div class="edit-input-group readonly">
|
||||
<label>手机号</label>
|
||||
<input type="tel" id="editPhone" value="${phone}" readonly>
|
||||
<span class="readonly-tip">不可修改</span>
|
||||
</div>
|
||||
|
||||
<div class="edit-input-group">
|
||||
<label>邮箱</label>
|
||||
<input type="email" id="editEmail" value="${email}" placeholder="请输入邮箱">
|
||||
</div>
|
||||
|
||||
<div class="edit-input-group">
|
||||
<label>性别</label>
|
||||
<div class="gender-selector" id="genderSelector">
|
||||
<div class="gender-option ${gender === 'male' ? 'selected' : ''}" data-gender="male">男 👨</div>
|
||||
<div class="gender-option ${gender === 'female' ? 'selected' : ''}" data-gender="female">女 👩</div>
|
||||
<div class="gender-option ${gender === 'other' ? 'selected' : ''}" data-gender="other">其他 🧑</div>
|
||||
<div class="gender-option ${gender === '' ? 'selected' : ''}" data-gender="">保密 🤐</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="edit-input-group">
|
||||
<label>年龄</label>
|
||||
<input type="number" id="editAge" value="${age}" placeholder="请输入年龄" min="1" max="120">
|
||||
</div>
|
||||
|
||||
<div class="edit-input-group">
|
||||
<label>地区</label>
|
||||
<input type="text" id="editRegion" value="${region}" placeholder="请输入地区" maxlength="20">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="edit-save-btn" id="saveProfileBtn">保存修改</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
appContainer.innerHTML = editHtml;
|
||||
|
||||
// 绑定返回按钮
|
||||
const backBtn = document.getElementById('editBackBtn');
|
||||
if (backBtn) {
|
||||
backBtn.addEventListener('click', () => {
|
||||
switchPage('profile');
|
||||
});
|
||||
}
|
||||
|
||||
// 绑定头像选择
|
||||
document.querySelectorAll('.avatar-option').forEach(option => {
|
||||
option.addEventListener('click', () => {
|
||||
const newAvatar = option.getAttribute('data-avatar');
|
||||
document.getElementById('editAvatarPreview').textContent = newAvatar;
|
||||
document.querySelectorAll('.avatar-option').forEach(o => o.classList.remove('selected'));
|
||||
option.classList.add('selected');
|
||||
});
|
||||
});
|
||||
|
||||
// 绑定性别选择
|
||||
document.querySelectorAll('.gender-option').forEach(option => {
|
||||
option.addEventListener('click', () => {
|
||||
document.querySelectorAll('.gender-option').forEach(o => o.classList.remove('selected'));
|
||||
option.classList.add('selected');
|
||||
});
|
||||
});
|
||||
|
||||
// 绑定保存按钮
|
||||
const saveBtn = document.getElementById('saveProfileBtn');
|
||||
if (saveBtn) {
|
||||
saveBtn.addEventListener('click', handleSaveProfile);
|
||||
}
|
||||
}
|
||||
|
||||
function handleSaveProfile() {
|
||||
const newAvatar = document.querySelector('.avatar-option.selected')?.getAttribute('data-avatar') || '👤';
|
||||
const newUsername = document.getElementById('editUsername')?.value.trim();
|
||||
const newSignature = document.getElementById('editSignature')?.value.trim();
|
||||
const newEmail = document.getElementById('editEmail')?.value.trim();
|
||||
const newGender = document.querySelector('.gender-option.selected')?.getAttribute('data-gender') || '';
|
||||
const newAge = document.getElementById('editAge')?.value;
|
||||
const newRegion = document.getElementById('editRegion')?.value.trim();
|
||||
|
||||
// 验证用户名
|
||||
if (!newUsername || newUsername.length < 3 || newUsername.length > 20) {
|
||||
showToast('用户名需要3-20个字符');
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证邮箱
|
||||
if (newEmail && !validateEmail(newEmail)) {
|
||||
showToast('请输入正确的邮箱格式');
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证年龄
|
||||
if (newAge && (parseInt(newAge) < 1 || parseInt(newAge) > 120)) {
|
||||
showToast('年龄需要在1-120之间');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查用户名是否被其他人占用
|
||||
const users = JSON.parse(localStorage.getItem('registeredUsers') || '[]');
|
||||
if (newUsername !== currentUser.username) {
|
||||
if (users.find(u => u.username === newUsername)) {
|
||||
showToast('用户名已被占用');
|
||||
return;
|
||||
}
|
||||
// 更新用户名
|
||||
const userIndex = users.findIndex(u => u.username === currentUser.username);
|
||||
if (userIndex >= 0) {
|
||||
users[userIndex].username = newUsername;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新邮箱
|
||||
const userIndex = users.findIndex(u => u.username === currentUser.username);
|
||||
if (userIndex >= 0) {
|
||||
users[userIndex].email = newEmail;
|
||||
}
|
||||
|
||||
// 更新 currentUser
|
||||
currentUser.avatar = newAvatar;
|
||||
currentUser.username = newUsername;
|
||||
currentUser.signature = newSignature;
|
||||
currentUser.gender = newGender;
|
||||
currentUser.age = newAge ? parseInt(newAge) : '';
|
||||
currentUser.region = newRegion;
|
||||
|
||||
saveCurrentUser();
|
||||
localStorage.setItem('registeredUsers', JSON.stringify(users));
|
||||
|
||||
showToast('保存成功');
|
||||
switchPage('profile');
|
||||
}
|
||||
|
||||
// ==================== 消息通知页面 ====================
|
||||
|
||||
function showNotificationPage() {
|
||||
// 加载通知设置
|
||||
const settings = JSON.parse(localStorage.getItem('notificationSettings') || '{}');
|
||||
const enableNotification = settings.enableNotification !== false;
|
||||
const enableSound = settings.enableSound !== false;
|
||||
const enableVibration = settings.enableVibration !== false;
|
||||
const quietHoursStart = settings.quietHoursStart || '23:00';
|
||||
const quietHoursEnd = settings.quietHoursEnd || '08:00';
|
||||
|
||||
const notificationHtml = `
|
||||
<div class="settings-page">
|
||||
<header class="settings-header">
|
||||
<button class="back-btn-white" id="notificationBackBtn">
|
||||
<svg viewBox="0 0 24 24" width="24" height="24"><path fill="currentColor" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
|
||||
</button>
|
||||
<h1>消息通知</h1>
|
||||
</header>
|
||||
|
||||
<div class="settings-content">
|
||||
<div class="settings-section">
|
||||
<div class="settings-section-title">通知设置</div>
|
||||
|
||||
<div class="settings-item">
|
||||
<div class="settings-info">
|
||||
<span class="settings-label">开启通知</span>
|
||||
<span class="settings-desc">接收消息推送通知</span>
|
||||
</div>
|
||||
<div class="settings-toggle ${enableNotification ? 'active' : ''}" id="toggleNotification">
|
||||
<div class="toggle-switch"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-item">
|
||||
<div class="settings-info">
|
||||
<span class="settings-label">通知声音</span>
|
||||
<span class="settings-desc">收到通知时播放声音</span>
|
||||
</div>
|
||||
<div class="settings-toggle ${enableSound ? 'active' : ''}" id="toggleSound">
|
||||
<div class="toggle-switch"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-item">
|
||||
<div class="settings-info">
|
||||
<span class="settings-label">震动提醒</span>
|
||||
<span class="settings-desc">收到通知时震动</span>
|
||||
</div>
|
||||
<div class="settings-toggle ${enableVibration ? 'active' : ''}" id="toggleVibration">
|
||||
<div class="toggle-switch"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-section">
|
||||
<div class="settings-section-title">免打扰时段</div>
|
||||
|
||||
<div class="settings-item">
|
||||
<div class="settings-info">
|
||||
<span class="settings-label">开始时间</span>
|
||||
</div>
|
||||
<input type="time" id="quietHoursStart" value="${quietHoursStart}" class="settings-time-input">
|
||||
</div>
|
||||
|
||||
<div class="settings-item">
|
||||
<div class="settings-info">
|
||||
<span class="settings-label">结束时间</span>
|
||||
</div>
|
||||
<input type="time" id="quietHoursEnd" value="${quietHoursEnd}" class="settings-time-input">
|
||||
</div>
|
||||
|
||||
<div class="settings-tip">免打扰时段内不会收到通知</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
appContainer.innerHTML = notificationHtml;
|
||||
|
||||
// 绑定返回按钮
|
||||
const backBtn = document.getElementById('notificationBackBtn');
|
||||
if (backBtn) {
|
||||
backBtn.addEventListener('click', () => {
|
||||
saveNotificationSettings();
|
||||
switchPage('profile');
|
||||
});
|
||||
}
|
||||
|
||||
// 绑定开关切换
|
||||
document.querySelectorAll('.settings-toggle').forEach(toggle => {
|
||||
toggle.addEventListener('click', () => {
|
||||
toggle.classList.toggle('active');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function saveNotificationSettings() {
|
||||
const settings = {
|
||||
enableNotification: document.getElementById('toggleNotification')?.classList.contains('active'),
|
||||
enableSound: document.getElementById('toggleSound')?.classList.contains('active'),
|
||||
enableVibration: document.getElementById('toggleVibration')?.classList.contains('active'),
|
||||
quietHoursStart: document.getElementById('quietHoursStart')?.value || '23:00',
|
||||
quietHoursEnd: document.getElementById('quietHoursEnd')?.value || '08:00'
|
||||
};
|
||||
localStorage.setItem('notificationSettings', JSON.stringify(settings));
|
||||
}
|
||||
|
||||
// ==================== 隐私安全页面 ====================
|
||||
|
||||
function showPrivacyPage() {
|
||||
const privacyHtml = `
|
||||
<div class="settings-page">
|
||||
<header class="settings-header">
|
||||
<button class="back-btn-white" id="privacyBackBtn">
|
||||
<svg viewBox="0 0 24 24" width="24" height="24"><path fill="currentColor" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
|
||||
</button>
|
||||
<h1>隐私安全</h1>
|
||||
</header>
|
||||
|
||||
<div class="settings-content">
|
||||
<div class="settings-section">
|
||||
<div class="settings-section-title">账号安全</div>
|
||||
|
||||
${currentUser ? `
|
||||
<div class="settings-item" id="changePasswordBtn">
|
||||
<div class="settings-info">
|
||||
<span class="settings-label">修改密码</span>
|
||||
<span class="settings-desc">定期修改密码保障账号安全</span>
|
||||
</div>
|
||||
<span class="settings-arrow">›</span>
|
||||
</div>
|
||||
|
||||
<div class="settings-item" id="showAccountInfoBtn">
|
||||
<div class="settings-info">
|
||||
<span class="settings-label">账号信息</span>
|
||||
<span class="settings-desc">查看注册时间等信息</span>
|
||||
</div>
|
||||
<span class="settings-arrow">›</span>
|
||||
</div>
|
||||
` : `
|
||||
<div class="settings-tip">登录后可管理账号安全</div>
|
||||
`}
|
||||
</div>
|
||||
|
||||
<div class="settings-section">
|
||||
<div class="settings-section-title">隐私设置</div>
|
||||
|
||||
<div class="settings-item">
|
||||
<div class="settings-info">
|
||||
<span class="settings-label">清除聊天记录</span>
|
||||
<span class="settings-desc">删除所有对话历史</span>
|
||||
</div>
|
||||
<button class="settings-action-btn danger" id="clearChatsBtn">清除</button>
|
||||
</div>
|
||||
|
||||
<div class="settings-item">
|
||||
<div class="settings-info">
|
||||
<span class="settings-label">清除缓存</span>
|
||||
<span class="settings-desc">清除应用缓存数据</span>
|
||||
</div>
|
||||
<button class="settings-action-btn" id="clearCacheBtn">清除</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-section">
|
||||
<div class="settings-section-title">数据安全</div>
|
||||
|
||||
<div class="settings-item">
|
||||
<div class="settings-info">
|
||||
<span class="settings-label">数据本地存储</span>
|
||||
<span class="settings-desc">所有数据存储在本地,不上传服务器</span>
|
||||
</div>
|
||||
<span class="settings-badge safe">安全</span>
|
||||
</div>
|
||||
|
||||
<div class="settings-item">
|
||||
<div class="settings-info">
|
||||
<span class="settings-label">对话加密</span>
|
||||
<span class="settings-desc">对话内容采用本地加密存储</span>
|
||||
</div>
|
||||
<span class="settings-badge safe">已开启</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
appContainer.innerHTML = privacyHtml;
|
||||
|
||||
// 绑定返回按钮
|
||||
const backBtn = document.getElementById('privacyBackBtn');
|
||||
if (backBtn) {
|
||||
backBtn.addEventListener('click', () => {
|
||||
switchPage('profile');
|
||||
});
|
||||
}
|
||||
|
||||
// 修改密码
|
||||
const changePasswordBtn = document.getElementById('changePasswordBtn');
|
||||
if (changePasswordBtn) {
|
||||
changePasswordBtn.addEventListener('click', () => {
|
||||
showChangePasswordPage();
|
||||
});
|
||||
}
|
||||
|
||||
// 账号信息
|
||||
const showAccountInfoBtn = document.getElementById('showAccountInfoBtn');
|
||||
if (showAccountInfoBtn) {
|
||||
showAccountInfoBtn.addEventListener('click', () => {
|
||||
showAccountInfoDialog();
|
||||
});
|
||||
}
|
||||
|
||||
// 清除聊天记录
|
||||
const clearChatsBtn = document.getElementById('clearChatsBtn');
|
||||
if (clearChatsBtn) {
|
||||
clearChatsBtn.addEventListener('click', () => {
|
||||
if (confirm('确定要清除所有聊天记录吗?')) {
|
||||
conversations = [];
|
||||
saveConversations();
|
||||
showToast('聊天记录已清除');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 清除缓存
|
||||
const clearCacheBtn = document.getElementById('clearCacheBtn');
|
||||
if (clearCacheBtn) {
|
||||
clearCacheBtn.addEventListener('click', () => {
|
||||
// 清除临时数据,保留用户数据
|
||||
localStorage.removeItem('currentPage');
|
||||
localStorage.removeItem('dailyUsage');
|
||||
showToast('缓存已清除');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function showChangePasswordPage() {
|
||||
const changePwdHtml = `
|
||||
<div class="settings-page">
|
||||
<header class="settings-header">
|
||||
<button class="back-btn-white" id="changePwdBackBtn">
|
||||
<svg viewBox="0 0 24 24" width="24" height="24"><path fill="currentColor" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
|
||||
</button>
|
||||
<h1>修改密码</h1>
|
||||
</header>
|
||||
|
||||
<div class="settings-content">
|
||||
<div class="auth-form">
|
||||
<div class="auth-input-group">
|
||||
<label>当前密码</label>
|
||||
<input type="password" id="currentPassword" placeholder="请输入当前密码">
|
||||
</div>
|
||||
<div class="auth-input-group">
|
||||
<label>新密码</label>
|
||||
<input type="password" id="newPassword" placeholder="请输入新密码(6-20字符)">
|
||||
</div>
|
||||
<div class="auth-input-group">
|
||||
<label>确认新密码</label>
|
||||
<input type="password" id="confirmNewPassword" placeholder="请再次输入新密码">
|
||||
</div>
|
||||
<button class="auth-submit-btn" id="changePwdSubmitBtn">确认修改</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
appContainer.innerHTML = changePwdHtml;
|
||||
|
||||
// 绑定返回按钮
|
||||
const backBtn = document.getElementById('changePwdBackBtn');
|
||||
if (backBtn) {
|
||||
backBtn.addEventListener('click', () => {
|
||||
showPrivacyPage();
|
||||
});
|
||||
}
|
||||
|
||||
// 绑定提交按钮
|
||||
const submitBtn = document.getElementById('changePwdSubmitBtn');
|
||||
if (submitBtn) {
|
||||
submitBtn.addEventListener('click', handleChangePassword);
|
||||
}
|
||||
}
|
||||
|
||||
function handleChangePassword() {
|
||||
const currentPwd = document.getElementById('currentPassword')?.value;
|
||||
const newPwd = document.getElementById('newPassword')?.value;
|
||||
const confirmNewPwd = document.getElementById('confirmNewPassword')?.value;
|
||||
|
||||
// 验证
|
||||
if (!currentPwd) {
|
||||
showToast('请输入当前密码');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newPwd || newPwd.length < 6 || newPwd.length > 20) {
|
||||
showToast('新密码需要6-20个字符');
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPwd !== confirmNewPwd) {
|
||||
showToast('两次密码不一致');
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证当前密码
|
||||
const users = JSON.parse(localStorage.getItem('registeredUsers') || '[]');
|
||||
const user = users.find(u => u.username === currentUser.username);
|
||||
|
||||
if (!user || user.password !== currentPwd) {
|
||||
showToast('当前密码错误');
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新密码
|
||||
user.password = newPwd;
|
||||
localStorage.setItem('registeredUsers', JSON.stringify(users));
|
||||
|
||||
showToast('密码已修改');
|
||||
showPrivacyPage();
|
||||
}
|
||||
|
||||
function showAccountInfoDialog() {
|
||||
const users = JSON.parse(localStorage.getItem('registeredUsers') || '[]');
|
||||
const user = users.find(u => u.username === currentUser.username);
|
||||
|
||||
const registeredAt = user?.registeredAt ? new Date(user.registeredAt).toLocaleString('zh-CN') : '未知';
|
||||
const phone = user?.phone || '未设置';
|
||||
const email = user?.email || '未设置';
|
||||
|
||||
const dialogHtml = `
|
||||
<div class="info-dialog" id="accountInfoDialog">
|
||||
<div class="info-dialog-content">
|
||||
<div class="info-dialog-title">账号信息</div>
|
||||
<div class="info-dialog-items">
|
||||
<div class="info-item">
|
||||
<span class="info-label">用户名</span>
|
||||
<span class="info-value">${currentUser.username}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">手机号</span>
|
||||
<span class="info-value">${phone}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">邮箱</span>
|
||||
<span class="info-value">${email}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">注册时间</span>
|
||||
<span class="info-value">${registeredAt}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="info-dialog-close" id="closeAccountInfo">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.insertAdjacentHTML('beforeend', dialogHtml);
|
||||
|
||||
document.getElementById('closeAccountInfo')?.addEventListener('click', () => {
|
||||
document.getElementById('accountInfoDialog')?.remove();
|
||||
});
|
||||
|
||||
document.getElementById('accountInfoDialog')?.addEventListener('click', (e) => {
|
||||
if (e.target.id === 'accountInfoDialog') {
|
||||
document.getElementById('accountInfoDialog')?.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 帮助与反馈页面 ====================
|
||||
|
||||
function showHelpPage() {
|
||||
const helpHtml = `
|
||||
<div class="settings-page">
|
||||
<header class="settings-header">
|
||||
<button class="back-btn-white" id="helpBackBtn">
|
||||
<svg viewBox="0 0 24 24" width="24" height="24"><path fill="currentColor" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
|
||||
</button>
|
||||
<h1>帮助与反馈</h1>
|
||||
</header>
|
||||
|
||||
<div class="settings-content">
|
||||
<div class="settings-section">
|
||||
<div class="settings-section-title">使用帮助</div>
|
||||
|
||||
<div class="help-item" id="helpGuide1">
|
||||
<div class="help-icon">💬</div>
|
||||
<div class="help-info">
|
||||
<span class="help-title">如何开始对话</span>
|
||||
<span class="help-desc">点击对话页面的 + 按钮创建新对话</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item" id="helpGuide2">
|
||||
<div class="help-icon">🤖</div>
|
||||
<div class="help-info">
|
||||
<span class="help-title">如何使用智能体</span>
|
||||
<span class="help-desc">切换到智能体页面,选择想要使用的智能体</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item" id="helpGuide3">
|
||||
<div class="help-icon">🔍</div>
|
||||
<div class="help-info">
|
||||
<span class="help-title">如何搜索对话</span>
|
||||
<span class="help-desc">点击对话页面的搜索按钮输入关键词</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item" id="helpGuide4">
|
||||
<div class="help-icon">⭐</div>
|
||||
<div class="help-info">
|
||||
<span class="help-title">如何收藏智能体</span>
|
||||
<span class="help-desc">长按智能体卡片选择收藏,或去发现页面添加</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-section">
|
||||
<div class="settings-section-title">问题反馈</div>
|
||||
|
||||
<div class="feedback-form">
|
||||
<textarea id="feedbackContent" placeholder="请描述您遇到的问题或建议..." rows="4"></textarea>
|
||||
<button class="feedback-submit-btn" id="submitFeedbackBtn">提交反馈</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-section">
|
||||
<div class="settings-section-title">联系我们</div>
|
||||
|
||||
<div class="contact-item">
|
||||
<div class="contact-icon">📧</div>
|
||||
<div class="contact-info">
|
||||
<span class="contact-label">邮箱</span>
|
||||
<span class="contact-value">support@example.com</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
appContainer.innerHTML = helpHtml;
|
||||
|
||||
// 绑定返回按钮
|
||||
const backBtn = document.getElementById('helpBackBtn');
|
||||
if (backBtn) {
|
||||
backBtn.addEventListener('click', () => {
|
||||
switchPage('profile');
|
||||
});
|
||||
}
|
||||
|
||||
// 提交反馈
|
||||
const submitBtn = document.getElementById('submitFeedbackBtn');
|
||||
if (submitBtn) {
|
||||
submitBtn.addEventListener('click', () => {
|
||||
const content = document.getElementById('feedbackContent')?.value.trim();
|
||||
if (!content) {
|
||||
showToast('请输入反馈内容');
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存反馈(模拟)
|
||||
const feedbacks = JSON.parse(localStorage.getItem('feedbacks') || '[]');
|
||||
feedbacks.push({
|
||||
content,
|
||||
userId: currentUser?.username || 'guest',
|
||||
createdAt: Date.now()
|
||||
});
|
||||
localStorage.setItem('feedbacks', JSON.stringify(feedbacks));
|
||||
|
||||
showToast('感谢您的反馈');
|
||||
document.getElementById('feedbackContent').value = '';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 关于页面 ====================
|
||||
|
||||
function showAboutPage() {
|
||||
const aboutHtml = `
|
||||
<div class="settings-page">
|
||||
<header class="settings-header">
|
||||
<button class="back-btn-white" id="aboutBackBtn">
|
||||
<svg viewBox="0 0 24 24" width="24" height="24"><path fill="currentColor" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
|
||||
</button>
|
||||
<h1>关于</h1>
|
||||
</header>
|
||||
|
||||
<div class="settings-content about-content">
|
||||
<div class="about-logo">🤖</div>
|
||||
<div class="about-name">AI助手</div>
|
||||
<div class="about-version">v3.5.0</div>
|
||||
|
||||
<div class="about-info">
|
||||
<p>基于智谱 GLM-4.5-Air 大模型</p>
|
||||
<p>提供智能对话、多种智能体服务</p>
|
||||
</div>
|
||||
|
||||
<div class="about-section">
|
||||
<div class="about-item">
|
||||
<span class="about-label">开发者</span>
|
||||
<span class="about-value">OpenClaw Team</span>
|
||||
</div>
|
||||
<div class="about-item">
|
||||
<span class="about-label">更新日期</span>
|
||||
<span class="about-value">2026-04-27</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="about-links">
|
||||
<div class="about-link-item" id="checkUpdateBtn">
|
||||
<span>检查更新</span>
|
||||
<span class="about-arrow">›</span>
|
||||
</div>
|
||||
<div class="about-link-item" id="privacyPolicyBtn">
|
||||
<span>隐私政策</span>
|
||||
<span class="about-arrow">›</span>
|
||||
</div>
|
||||
<div class="about-link-item" id="userAgreementBtn">
|
||||
<span>用户协议</span>
|
||||
<span class="about-arrow">›</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
appContainer.innerHTML = aboutHtml;
|
||||
|
||||
// 绑定返回按钮
|
||||
const backBtn = document.getElementById('aboutBackBtn');
|
||||
if (backBtn) {
|
||||
backBtn.addEventListener('click', () => {
|
||||
switchPage('profile');
|
||||
});
|
||||
}
|
||||
|
||||
// 检查更新
|
||||
const checkUpdateBtn = document.getElementById('checkUpdateBtn');
|
||||
if (checkUpdateBtn) {
|
||||
checkUpdateBtn.addEventListener('click', () => {
|
||||
showToast('当前已是最新版本');
|
||||
});
|
||||
}
|
||||
|
||||
// 隐私政策
|
||||
const privacyPolicyBtn = document.getElementById('privacyPolicyBtn');
|
||||
if (privacyPolicyBtn) {
|
||||
privacyPolicyBtn.addEventListener('click', () => {
|
||||
showToast('隐私政策页面待完善');
|
||||
});
|
||||
}
|
||||
|
||||
// 用户协议
|
||||
const userAgreementBtn = document.getElementById('userAgreementBtn');
|
||||
if (userAgreementBtn) {
|
||||
userAgreementBtn.addEventListener('click', () => {
|
||||
showToast('用户协议页面待完善');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 登录页面 ====================
|
||||
|
||||
function showLoginPage() {
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<meta http-equiv="Expires" content="0">
|
||||
<title>AI助手</title>
|
||||
<link rel="stylesheet" href="style.css?v=3.4.1">
|
||||
<link rel="stylesheet" href="style.css?v=3.5.0">
|
||||
<link rel="manifest" href="manifest.json">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="marked.min.js?v=3.4.1"></script>
|
||||
<script src="app.js?v=3.4.1"></script>
|
||||
<script src="marked.min.js?v=3.5.0"></script>
|
||||
<script src="app.js?v=3.5.0"></script>
|
||||
</body>
|
||||
</html>
|
||||
688
www/style.css
688
www/style.css
@@ -833,88 +833,696 @@ body {
|
||||
|
||||
/* ==================== 我的页面 ==================== */
|
||||
|
||||
.profile-card {
|
||||
.profile-user-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 16px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.profile-user-card:hover {
|
||||
background: rgba(102, 126, 234, 0.05);
|
||||
}
|
||||
|
||||
.profile-avatar-large {
|
||||
font-size: 48px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.profile-user-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.profile-user-info .profile-name {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.profile-signature {
|
||||
font-size: 14px;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.profile-arrow {
|
||||
font-size: 20px;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.profile-login-buttons-inline {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.login-btn-small, .register-btn-small {
|
||||
padding: 8px 16px;
|
||||
background: linear-gradient(135deg, var(--primary) 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.register-btn-small {
|
||||
background: white;
|
||||
border: 1px solid var(--primary);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* ==================== 功能菜单 ==================== */
|
||||
|
||||
.profile-menu-section {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.profile-menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.profile-menu-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.profile-menu-item:hover {
|
||||
background: rgba(102, 126, 234, 0.05);
|
||||
}
|
||||
|
||||
.profile-menu-item .menu-icon {
|
||||
font-size: 20px;
|
||||
margin-right: 12px;
|
||||
width: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.profile-menu-item .menu-text {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.profile-menu-item .menu-desc {
|
||||
font-size: 12px;
|
||||
color: var(--text-light);
|
||||
margin-left: 8px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.profile-menu-item .menu-arrow {
|
||||
font-size: 18px;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.profile-menu-item.danger .menu-text {
|
||||
color: #e53e3e;
|
||||
}
|
||||
|
||||
.profile-menu-item.danger:hover {
|
||||
background: rgba(229, 62, 62, 0.05);
|
||||
}
|
||||
|
||||
/* ==================== 用户编辑页面 ==================== */
|
||||
|
||||
.edit-profile-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.edit-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24px;
|
||||
padding: 12px 16px;
|
||||
background: linear-gradient(135deg, var(--primary) 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.edit-header h1 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.edit-content {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.edit-section {
|
||||
background: white;
|
||||
padding: 16px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.profile-avatar {
|
||||
font-size: 48px;
|
||||
.edit-section-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.edit-avatar-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-bottom: 16px;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.edit-avatar-preview {
|
||||
font-size: 64px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.profile-name {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
.edit-avatar-label {
|
||||
font-size: 12px;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.avatar-selector {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.avatar-option {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28px;
|
||||
background: white;
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.avatar-option:hover {
|
||||
border-color: var(--primary);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.avatar-option.selected {
|
||||
border-color: var(--primary);
|
||||
background: rgba(102, 126, 234, 0.1);
|
||||
box-shadow: 0 0 8px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.edit-input-group {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.edit-input-group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.edit-input-group label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--text-color);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.profile-status {
|
||||
.edit-input-group input,
|
||||
.edit-input-group textarea {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
color: #22c55e;
|
||||
margin-bottom: 16px;
|
||||
outline: none;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.profile-status.guest {
|
||||
.edit-input-group input:focus,
|
||||
.edit-input-group textarea:focus {
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.edit-input-group textarea {
|
||||
resize: none;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.edit-input-group.readonly input {
|
||||
background: var(--bg-color);
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.profile-login-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
.readonly-tip {
|
||||
font-size: 12px;
|
||||
color: var(--text-light);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.login-btn, .register-btn, .logout-btn {
|
||||
.gender-selector {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.gender-option {
|
||||
padding: 10px 16px;
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.gender-option:hover {
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.gender-option.selected {
|
||||
border-color: var(--primary);
|
||||
background: rgba(102, 126, 234, 0.1);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.edit-save-btn {
|
||||
width: 100%;
|
||||
padding: 14px;
|
||||
background: linear-gradient(135deg, var(--primary) 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.edit-save-btn:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* ==================== 设置页面 ==================== */
|
||||
|
||||
.settings-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.settings-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
background: linear-gradient(135deg, var(--primary) 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.settings-header h1 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.settings-content {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
background: white;
|
||||
padding: 16px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.settings-section-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.settings-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.settings-item:last-child {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.settings-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.settings-label {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.settings-desc {
|
||||
font-size: 12px;
|
||||
color: var(--text-light);
|
||||
display: block;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.settings-arrow {
|
||||
font-size: 18px;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.settings-toggle {
|
||||
width: 50px;
|
||||
height: 28px;
|
||||
background: var(--border-color);
|
||||
border-radius: 14px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.settings-toggle.active {
|
||||
background: var(--primary);
|
||||
}
|
||||
|
||||
.toggle-switch {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
position: absolute;
|
||||
left: 2px;
|
||||
top: 2px;
|
||||
transition: left 0.2s;
|
||||
}
|
||||
|
||||
.settings-toggle.active .toggle-switch {
|
||||
left: 24px;
|
||||
}
|
||||
|
||||
.settings-time-input {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.settings-tip {
|
||||
font-size: 12px;
|
||||
color: var(--text-light);
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.settings-action-btn {
|
||||
padding: 8px 16px;
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.settings-action-btn.danger {
|
||||
background: #e53e3e;
|
||||
}
|
||||
|
||||
.settings-badge {
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.settings-badge.safe {
|
||||
background: rgba(34, 197, 94, 0.1);
|
||||
color: #22c55e;
|
||||
}
|
||||
|
||||
/* ==================== 帮助页面 ==================== */
|
||||
|
||||
.help-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.help-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.help-icon {
|
||||
font-size: 24px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.help-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.help-title {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.help-desc {
|
||||
font-size: 12px;
|
||||
color: var(--text-light);
|
||||
display: block;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.feedback-form {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.feedback-form textarea {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
resize: none;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.feedback-submit-btn {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background: linear-gradient(135deg, var(--primary) 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
margin-top: 12px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.login-btn:hover, .register-btn:hover, .logout-btn:hover {
|
||||
transform: scale(1.02);
|
||||
.contact-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.register-btn {
|
||||
background: white;
|
||||
border: 2px solid var(--primary);
|
||||
color: var(--primary);
|
||||
.contact-icon {
|
||||
font-size: 20px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
background: #fee2e2;
|
||||
color: #e53e3e;
|
||||
}
|
||||
|
||||
.skip-btn {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background: transparent;
|
||||
border: 1px solid var(--border-color);
|
||||
.contact-info .contact-label {
|
||||
font-size: 14px;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.contact-info .contact-value {
|
||||
font-size: 15px;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* ==================== 关于页面 ==================== */
|
||||
|
||||
.about-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.about-logo {
|
||||
font-size: 72px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.about-name {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.about-version {
|
||||
font-size: 14px;
|
||||
color: var(--text-light);
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.about-info {
|
||||
padding: 16px;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.about-info p {
|
||||
font-size: 14px;
|
||||
color: var(--text-light);
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.about-section {
|
||||
background: white;
|
||||
padding: 16px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.about-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.about-label {
|
||||
font-size: 14px;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.about-value {
|
||||
font-size: 14px;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.about-links {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.about-link-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.about-link-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.about-link-item:hover {
|
||||
background: rgba(102, 126, 234, 0.05);
|
||||
}
|
||||
|
||||
.about-link-item span:first-child {
|
||||
font-size: 15px;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.about-link-item .about-arrow {
|
||||
font-size: 18px;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
/* ==================== 信息对话框 ==================== */
|
||||
|
||||
.info-dialog {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0,0,0,0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
.info-dialog-content {
|
||||
background: white;
|
||||
padding: 24px;
|
||||
border-radius: 16px;
|
||||
max-width: 300px;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.info-dialog-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
margin-bottom: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.info-dialog-items {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.info-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 14px;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 14px;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.info-dialog-close {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.skip-btn:hover {
|
||||
background: rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
/* ==================== 配额显示 ==================== */
|
||||
|
||||
.quota-card {
|
||||
|
||||
Reference in New Issue
Block a user