feat: 登录注册系统 + 游客使用限制

功能:
- 登录/注册界面(本地存储用户数据)
- 我的页面显示用户状态、登录/退出按钮
- 游客配额显示(对话会话/消息/智能体消息)

游客限制:
- 每天最多1个对话会话
- 每天最多20条对话消息
- 只能使用通用助手智能体
- 每天最多20条智能体消息
- 登录用户无限制

限制提示:
- 达到限制弹出提示对话框
- 提示登录解锁全部功能
This commit is contained in:
2026-04-27 11:06:20 +08:00
parent 439743c051
commit c8cea4c1a1
3 changed files with 913 additions and 14 deletions

View File

@@ -67,6 +67,166 @@ let pinnedAgents = {
let currentAgent = null; // 当前选中的智能体
// 用户状态
let currentUser = null; // 当前登录用户 { username, password, registeredAt }
// 每日使用统计(未登录用户)
let dailyUsage = {
date: null, // 日期 YYYY-MM-DD
chatSessions: 0, // 对话会话数
chatMessages: 0, // 对话消息数
agentUsed: null, // 使用的智能体ID只能用一个
agentMessages: 0 // 智能体消息数
};
// 未登录用户限制
const GUEST_LIMITS = {
maxChatSessionsPerDay: 1, // 每天最多1个对话会话
maxChatMessagesPerDay: 20, // 每天最多20条对话消息
maxAgentPerDay: 1, // 每天只能用1个智能体通用助手
maxAgentMessagesPerDay: 20 // 每天最多20条智能体消息
};
// 获取今日日期字符串
function getTodayDate() {
const today = new Date();
return `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
}
// 检查并重置每日使用统计
function checkDailyUsage() {
const today = getTodayDate();
if (dailyUsage.date !== today) {
// 新的一天,重置统计
dailyUsage = {
date: today,
chatSessions: 0,
chatMessages: 0,
agentUsed: null,
agentMessages: 0
};
saveDailyUsage();
}
}
// 保存用户状态
function saveCurrentUser() {
if (currentUser) {
localStorage.setItem('currentUser', JSON.stringify(currentUser));
} else {
localStorage.removeItem('currentUser');
}
}
// 保存每日使用统计
function saveDailyUsage() {
localStorage.setItem('dailyUsage', JSON.stringify(dailyUsage));
}
// 检查是否可以创建新对话(未登录用户)
function canCreateNewChat() {
if (currentUser) return true; // 登录用户无限制
checkDailyUsage();
return dailyUsage.chatSessions < GUEST_LIMITS.maxChatSessionsPerDay;
}
// 检查是否可以发送对话消息(未登录用户)
function canSendChatMessage() {
if (currentUser) return true; // 登录用户无限制
checkDailyUsage();
return dailyUsage.chatMessages < GUEST_LIMITS.maxChatMessagesPerDay;
}
// 检查是否可以使用智能体(未登录用户)
function canUseAgent(agentId) {
if (currentUser) return true; // 登录用户无限制
checkDailyUsage();
// 未登录用户只能使用通用助手
if (agentId !== 'assistant') {
return { allowed: false, reason: 'guest_agent_only' };
}
// 如果今天还没用智能体,可以用
if (!dailyUsage.agentUsed) {
return { allowed: true };
}
// 如果已经用了其他智能体,不能换
if (dailyUsage.agentUsed !== agentId) {
return { allowed: false, reason: 'guest_one_agent_only' };
}
return { allowed: true };
}
// 检查是否可以发送智能体消息(未登录用户)
function canSendAgentMessage() {
if (currentUser) return true; // 登录用户无限制
checkDailyUsage();
return dailyUsage.agentMessages < GUEST_LIMITS.maxAgentMessagesPerDay;
}
// 增加对话会话计数
function incrementChatSession() {
if (!currentUser) {
checkDailyUsage();
dailyUsage.chatSessions++;
saveDailyUsage();
}
}
// 增加对话消息计数
function incrementChatMessage() {
if (!currentUser) {
checkDailyUsage();
dailyUsage.chatMessages++;
saveDailyUsage();
}
}
// 设置使用的智能体(未登录用户)
function setAgentUsed(agentId) {
if (!currentUser) {
checkDailyUsage();
if (!dailyUsage.agentUsed) {
dailyUsage.agentUsed = agentId;
saveDailyUsage();
}
}
}
// 增加智能体消息计数
function incrementAgentMessage() {
if (!currentUser) {
checkDailyUsage();
dailyUsage.agentMessages++;
saveDailyUsage();
}
}
// 获取剩余配额提示
function getRemainingQuota() {
if (currentUser) return null; // 登录用户无限制
checkDailyUsage();
const chatSessionRemain = GUEST_LIMITS.maxChatSessionsPerDay - dailyUsage.chatSessions;
const chatMsgRemain = GUEST_LIMITS.maxChatMessagesPerDay - dailyUsage.chatMessages;
const agentMsgRemain = GUEST_LIMITS.maxAgentMessagesPerDay - dailyUsage.agentMessages;
return {
chatSessionRemain,
chatMsgRemain,
agentMsgRemain,
agentUsed: dailyUsage.agentUsed
};
}
// 获取使用智能体的对话列表(按时间倒序)
function getAgentConversationHistory(limit = 5) {
return conversations
@@ -145,6 +305,21 @@ document.addEventListener('DOMContentLoaded', () => {
pinnedAgents = JSON.parse(savedPinnedAgents);
}
// 加载用户登录状态
const savedUser = localStorage.getItem('currentUser');
if (savedUser) {
currentUser = JSON.parse(savedUser);
}
// 加载每日使用统计
const savedDailyUsage = localStorage.getItem('dailyUsage');
if (savedDailyUsage) {
dailyUsage = JSON.parse(savedDailyUsage);
}
// 检查并重置每日使用统计
checkDailyUsage();
// 根据用户配置更新显示的智能体
updateAgentsDisplay();
@@ -1113,6 +1288,8 @@ function showAgentFavoritePage() {
// ==================== 我的页面 ====================
function renderProfilePage() {
const quota = getRemainingQuota();
return `
<div class="profile-page">
<header class="page-header">
@@ -1121,15 +1298,46 @@ function renderProfilePage() {
<div class="profile-content">
<div class="profile-card">
<div class="profile-avatar">👤</div>
<div class="profile-name">用户</div>
<div class="profile-avatar">${currentUser ? '👤' : '👤'}</div>
<div class="profile-name">${currentUser ? currentUser.username : '游客'}</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>`
}
</div>
<div class="profile-section">
<div class="profile-item" id="settingsBtn">
<svg viewBox="0 0 24 24" width="20" height="20"><path fill="currentColor" d="M19.14 12.94c.04-.31.06-.63.06-.94 0-.31-.02-.63-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.44.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>
<span>设置</span>
${!currentUser && quota ? `
<div class="quota-card">
<div class="quota-title">今日使用配额(游客)</div>
<div class="quota-items">
<div class="quota-item">
<span class="quota-label">对话会话</span>
<span class="quota-value">${quota.chatSessionRemain}/${GUEST_LIMITS.maxChatSessionsPerDay}</span>
</div>
<div class="quota-item">
<span class="quota-label">对话消息</span>
<span class="quota-value">${quota.chatMsgRemain}/${GUEST_LIMITS.maxChatMessagesPerDay}</span>
</div>
<div class="quota-item">
<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>
@@ -1141,7 +1349,7 @@ function renderProfilePage() {
</div>
<div class="profile-footer">
<p>AI助手 v3.2.1</p>
<p>AI助手 v3.3.0</p>
<p>基于智谱 GLM-4.5-Air</p>
</div>
</div>
@@ -1150,12 +1358,53 @@ function renderProfilePage() {
}
function bindProfilePageEvents() {
// 登录按钮
const loginBtn = document.getElementById('loginBtn');
if (loginBtn) {
loginBtn.addEventListener('click', () => {
showLoginPage();
});
}
// 注册按钮
const registerBtn = document.getElementById('registerBtn');
if (registerBtn) {
registerBtn.addEventListener('click', () => {
showRegisterPage();
});
}
// 跳过按钮(游客模式)
const skipBtn = document.getElementById('skipBtn');
if (skipBtn) {
skipBtn.addEventListener('click', () => {
showToast('以游客模式继续使用');
switchPage('chats');
});
}
// 退出登录按钮
const logoutBtn = document.getElementById('logoutBtn');
if (logoutBtn) {
logoutBtn.addEventListener('click', () => {
if (confirm('确定要退出登录吗?')) {
currentUser = null;
saveCurrentUser();
showToast('已退出登录');
switchPage('profile');
}
});
}
// 清除数据按钮
const clearDataBtn = document.getElementById('clearDataBtn');
if (clearDataBtn) {
clearDataBtn.addEventListener('click', () => {
if (confirm('确定要清除所有数据吗?这将删除所有对话记录。')) {
if (confirm('确定要清除所有数据吗?这将删除所有对话记录和账户信息。')) {
localStorage.clear();
conversations = [];
currentUser = null;
dailyUsage = { date: null, chatSessions: 0, chatMessages: 0, agentUsed: null, agentMessages: 0 };
showToast('数据已清除');
showMainPage();
}
@@ -1163,6 +1412,280 @@ function bindProfilePageEvents() {
}
}
// ==================== 登录页面 ====================
function showLoginPage() {
const loginHtml = `
<div class="auth-page">
<header class="auth-header">
<button class="back-btn-white" id="loginBackBtn">
<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="auth-content">
<div class="auth-form">
<div class="auth-input-group">
<label>用户名</label>
<input type="text" id="loginUsername" placeholder="请输入用户名" autocomplete="username">
</div>
<div class="auth-input-group">
<label>密码</label>
<input type="password" id="loginPassword" placeholder="请输入密码" autocomplete="current-password">
</div>
<button class="auth-submit-btn" id="loginSubmitBtn">登录</button>
</div>
<div class="auth-footer">
<p>还没有账号?<span class="auth-link" id="goToRegister">立即注册</span></p>
</div>
</div>
</div>
`;
appContainer.innerHTML = loginHtml;
// 绑定事件
const backBtn = document.getElementById('loginBackBtn');
if (backBtn) {
backBtn.addEventListener('click', () => {
showMainPage();
});
}
const submitBtn = document.getElementById('loginSubmitBtn');
if (submitBtn) {
submitBtn.addEventListener('click', handleLogin);
}
const goToRegister = document.getElementById('goToRegister');
if (goToRegister) {
goToRegister.addEventListener('click', () => {
showRegisterPage();
});
}
// 回车登录
document.getElementById('loginPassword')?.addEventListener('keydown', (e) => {
if (e.key === 'Enter') handleLogin();
});
}
function handleLogin() {
const username = document.getElementById('loginUsername')?.value.trim();
const password = document.getElementById('loginPassword')?.value;
if (!username) {
showToast('请输入用户名');
return;
}
if (!password) {
showToast('请输入密码');
return;
}
// 检查本地存储的用户
const users = JSON.parse(localStorage.getItem('registeredUsers') || '[]');
const user = users.find(u => u.username === username && u.password === password);
if (user) {
currentUser = { username: user.username, registeredAt: user.registeredAt };
saveCurrentUser();
showToast('登录成功');
showMainPage();
} else {
showToast('用户名或密码错误');
}
}
// ==================== 注册页面 ====================
function showRegisterPage() {
const registerHtml = `
<div class="auth-page">
<header class="auth-header">
<button class="back-btn-white" id="registerBackBtn">
<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="auth-content">
<div class="auth-form">
<div class="auth-input-group">
<label>用户名</label>
<input type="text" id="registerUsername" placeholder="请输入用户名3-20字符" autocomplete="username">
</div>
<div class="auth-input-group">
<label>密码</label>
<input type="password" id="registerPassword" placeholder="请输入密码6-20字符" autocomplete="new-password">
</div>
<div class="auth-input-group">
<label>确认密码</label>
<input type="password" id="registerPasswordConfirm" placeholder="请再次输入密码" autocomplete="new-password">
</div>
<button class="auth-submit-btn" id="registerSubmitBtn">注册</button>
</div>
<div class="auth-footer">
<p>已有账号?<span class="auth-link" id="goToLogin">立即登录</span></p>
</div>
</div>
</div>
`;
appContainer.innerHTML = registerHtml;
// 绑定事件
const backBtn = document.getElementById('registerBackBtn');
if (backBtn) {
backBtn.addEventListener('click', () => {
showMainPage();
});
}
const submitBtn = document.getElementById('registerSubmitBtn');
if (submitBtn) {
submitBtn.addEventListener('click', handleRegister);
}
const goToLogin = document.getElementById('goToLogin');
if (goToLogin) {
goToLogin.addEventListener('click', () => {
showLoginPage();
});
}
// 回车注册
document.getElementById('registerPasswordConfirm')?.addEventListener('keydown', (e) => {
if (e.key === 'Enter') handleRegister();
});
}
function handleRegister() {
const username = document.getElementById('registerUsername')?.value.trim();
const password = document.getElementById('registerPassword')?.value;
const passwordConfirm = document.getElementById('registerPasswordConfirm')?.value;
if (!username || username.length < 3 || username.length > 20) {
showToast('用户名需要3-20个字符');
return;
}
if (!password || password.length < 6 || password.length > 20) {
showToast('密码需要6-20个字符');
return;
}
if (password !== passwordConfirm) {
showToast('两次密码不一致');
return;
}
// 检查用户名是否已存在
const users = JSON.parse(localStorage.getItem('registeredUsers') || '[]');
if (users.find(u => u.username === username)) {
showToast('用户名已存在');
return;
}
// 注册新用户
const newUser = {
username,
password,
registeredAt: Date.now()
};
users.push(newUser);
localStorage.setItem('registeredUsers', JSON.stringify(users));
// 自动登录
currentUser = { username: newUser.username, registeredAt: newUser.registeredAt };
saveCurrentUser();
showToast('注册成功');
showMainPage();
}
// ==================== 限制提示 ====================
function showLimitDialog(type) {
let title = '';
let message = '';
let showLoginBtn = true;
switch (type) {
case 'chat_session':
title = '对话会话已达上限';
message = `游客每天只能创建 ${GUEST_LIMITS.maxChatSessionsPerDay} 个对话会话。\n登录后即可无限使用。`;
break;
case 'chat_message':
title = '对话消息已达上限';
message = `游客每天最多发送 ${GUEST_LIMITS.maxChatMessagesPerDay} 条对话消息。\n登录后即可无限使用。`;
break;
case 'agent_type':
title = '智能体限制';
message = `游客只能使用「通用助手」智能体。\n登录后即可使用全部智能体。`;
break;
case 'agent_change':
title = '智能体切换限制';
message = `游客每天只能使用一个智能体。\n您今天已使用过智能体,明天才能更换。\n登录后即可自由切换智能体。`;
break;
case 'agent_message':
title = '智能体消息已达上限';
message = `游客每天最多发送 ${GUEST_LIMITS.maxAgentMessagesPerDay} 条智能体消息。\n登录后即可无限使用。`;
break;
default:
title = '使用限制';
message = '登录后解锁全部功能。';
}
const dialogHtml = `
<div class="limit-dialog" id="limitDialog">
<div class="limit-dialog-content">
<div class="limit-dialog-icon">⚠️</div>
<div class="limit-dialog-title">${title}</div>
<div class="limit-dialog-message">${message}</div>
<div class="limit-dialog-actions">
${showLoginBtn ? '<button class="limit-dialog-btn login" id="limitLoginBtn">登录</button>' : ''}
<button class="limit-dialog-btn cancel" id="limitCancelBtn">知道了</button>
</div>
</div>
</div>
`;
// 添加到页面
const existingDialog = document.getElementById('limitDialog');
if (existingDialog) existingDialog.remove();
document.body.insertAdjacentHTML('beforeend', dialogHtml);
// 绑定事件
const loginBtn = document.getElementById('limitLoginBtn');
if (loginBtn) {
loginBtn.addEventListener('click', () => {
document.getElementById('limitDialog')?.remove();
showLoginPage();
});
}
const cancelBtn = document.getElementById('limitCancelBtn');
if (cancelBtn) {
cancelBtn.addEventListener('click', () => {
document.getElementById('limitDialog')?.remove();
});
}
// 点击背景关闭
document.getElementById('limitDialog')?.addEventListener('click', (e) => {
if (e.target.id === 'limitDialog') {
document.getElementById('limitDialog')?.remove();
}
});
}
// 查看全部历史使用
function showAgentHistoryPage() {
const allAgentConvos = getAgentConversationHistory(100); // 获取所有
@@ -1222,11 +1745,36 @@ function openAgent(agentId) {
currentAgent = systemAgents.find(a => a.id === agentId);
if (!currentAgent) return;
// 检查未登录用户的智能体限制
if (!currentUser) {
const check = canUseAgent(agentId);
if (!check.allowed) {
if (check.reason === 'guest_agent_only') {
showLimitDialog('agent_type');
return;
} else if (check.reason === 'guest_one_agent_only') {
showLimitDialog('agent_change');
return;
}
}
// 记录使用的智能体
setAgentUsed(agentId);
}
// 创建新对话并设置智能体
createNewConversation();
currentConversation.agentId = agentId;
currentConversation.title = currentAgent.name;
const newConv = {
id: Date.now().toString(),
title: currentAgent.name,
messages: [],
agentId: agentId,
createdAt: Date.now(),
updatedAt: Date.now()
};
conversations.unshift(newConv);
saveConversations();
currentConversation = newConv;
// 显示对话界面
showAgentChatPage();
@@ -1895,6 +2443,12 @@ function togglePinConversation(convId) {
// 创建新对话
function createNewConversation() {
// 检查未登录用户的对话限制
if (!currentUser && !canCreateNewChat()) {
showLimitDialog('chat_session');
return;
}
const newConv = {
id: Date.now().toString(),
title: '新对话',
@@ -1905,6 +2459,10 @@ function createNewConversation() {
conversations.unshift(newConv);
saveConversations();
// 增加对话会话计数(未登录用户)
incrementChatSession();
openConversation(newConv.id);
}
@@ -2161,6 +2719,23 @@ async function sendMessage() {
const text = userInput.value.trim();
if (!text || isLoading) return;
// 检查未登录用户的消息限制
if (!currentUser) {
if (currentConversation.agentId) {
// 智能体对话
if (!canSendAgentMessage()) {
showLimitDialog('agent_message');
return;
}
} else {
// 普通对话
if (!canSendChatMessage()) {
showLimitDialog('chat_message');
return;
}
}
}
// 隐藏欢迎界面
welcome.style.display = 'none';
@@ -2181,6 +2756,15 @@ async function sendMessage() {
currentConversation.updatedAt = Date.now();
saveConversations();
// 增加消息计数(未登录用户)
if (!currentUser) {
if (currentConversation.agentId) {
incrementAgentMessage();
} else {
incrementChatMessage();
}
}
// 发送新消息时启用自动滚动
autoScrollEnabled = true;

View File

@@ -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.0.0">
<link rel="stylesheet" href="style.css?v=3.3.0">
<link rel="manifest" href="manifest.json">
</head>
<body>
<div id="app"></div>
<script src="marked.min.js?v=3.0.0"></script>
<script src="app.js?v=3.0.0"></script>
<script src="marked.min.js?v=3.3.0"></script>
<script src="app.js?v=3.3.0"></script>
</body>
</html>

View File

@@ -689,6 +689,321 @@ body {
opacity: 1;
}
/* ==================== 登录注册页面 ==================== */
.auth-page {
display: flex;
flex-direction: column;
height: 100vh;
height: 100dvh;
background: #f5f5f5;
}
.auth-header {
display: flex;
align-items: center;
padding: 12px 16px;
background: linear-gradient(135deg, var(--primary) 0%, #764ba2 100%);
color: white;
}
.auth-header h1 {
font-size: 18px;
font-weight: 600;
margin-left: 12px;
}
.auth-content {
flex: 1;
padding: 24px 16px;
display: flex;
flex-direction: column;
}
.auth-form {
background: white;
padding: 24px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.auth-input-group {
margin-bottom: 20px;
}
.auth-input-group label {
display: block;
font-size: 14px;
font-weight: 500;
color: var(--text-color);
margin-bottom: 8px;
}
.auth-input-group input {
width: 100%;
padding: 12px 16px;
border: 2px solid var(--border-color);
border-radius: 10px;
font-size: 16px;
outline: none;
transition: border-color 0.2s;
}
.auth-input-group input:focus {
border-color: var(--primary);
}
.auth-submit-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: 600;
cursor: pointer;
transition: transform 0.2s;
}
.auth-submit-btn:hover {
transform: scale(1.02);
}
.auth-submit-btn:active {
transform: scale(0.98);
}
.auth-footer {
text-align: center;
padding: 24px;
color: var(--text-light);
}
.auth-link {
color: var(--primary);
font-weight: 500;
cursor: pointer;
}
.auth-link:hover {
color: #5a67d8;
}
/* ==================== 我的页面 ==================== */
.profile-card {
display: flex;
flex-direction: column;
align-items: center;
padding: 24px;
background: white;
border-radius: 12px;
margin-bottom: 16px;
}
.profile-avatar {
font-size: 48px;
margin-bottom: 8px;
}
.profile-name {
font-size: 18px;
font-weight: 600;
color: var(--text-color);
margin-bottom: 8px;
}
.profile-status {
font-size: 14px;
color: #22c55e;
margin-bottom: 16px;
}
.profile-status.guest {
color: var(--text-light);
}
.profile-login-buttons {
display: flex;
flex-direction: column;
gap: 12px;
width: 100%;
}
.login-btn, .register-btn, .logout-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;
cursor: pointer;
transition: transform 0.2s;
}
.login-btn:hover, .register-btn:hover, .logout-btn:hover {
transform: scale(1.02);
}
.register-btn {
background: white;
border: 2px solid var(--primary);
color: var(--primary);
}
.logout-btn {
background: #fee2e2;
color: #e53e3e;
}
.skip-btn {
width: 100%;
padding: 10px;
background: transparent;
border: 1px solid var(--border-color);
color: var(--text-light);
border-radius: 8px;
font-size: 14px;
cursor: pointer;
}
.skip-btn:hover {
background: rgba(0,0,0,0.05);
}
/* ==================== 配额显示 ==================== */
.quota-card {
background: white;
padding: 16px;
border-radius: 12px;
margin-bottom: 16px;
border: 1px solid var(--border-color);
}
.quota-title {
font-size: 14px;
font-weight: 600;
color: var(--text-color);
margin-bottom: 12px;
}
.quota-items {
display: flex;
flex-direction: column;
gap: 8px;
}
.quota-item {
display: flex;
justify-content: space-between;
align-items: center;
}
.quota-label {
font-size: 14px;
color: var(--text-light);
}
.quota-value {
font-size: 14px;
font-weight: 500;
color: var(--primary);
}
.quota-tip {
text-align: center;
font-size: 12px;
color: #f59e0b;
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid var(--border-color);
}
/* ==================== 限制提示对话框 ==================== */
.limit-dialog {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 2000;
}
.limit-dialog-content {
background: white;
padding: 24px;
border-radius: 16px;
max-width: 300px;
text-align: center;
animation: dialogSlideUp 0.2s ease;
}
@keyframes dialogSlideUp {
from { transform: translateY(20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.limit-dialog-icon {
font-size: 48px;
margin-bottom: 16px;
}
.limit-dialog-title {
font-size: 18px;
font-weight: 600;
color: var(--text-color);
margin-bottom: 12px;
}
.limit-dialog-message {
font-size: 14px;
color: var(--text-light);
line-height: 1.6;
margin-bottom: 20px;
white-space: pre-line;
}
.limit-dialog-actions {
display: flex;
gap: 12px;
}
.limit-dialog-btn {
flex: 1;
padding: 12px;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.limit-dialog-btn.login {
background: linear-gradient(135deg, var(--primary) 0%, #764ba2 100%);
color: white;
border: none;
}
.limit-dialog-btn.login:hover {
transform: scale(1.02);
}
.limit-dialog-btn.cancel {
background: white;
border: 1px solid var(--border-color);
color: var(--text-light);
}
.limit-dialog-btn.cancel:hover {
background: var(--bg-color);
}
/* 最近使用的智能体列表 */
.recent-agents-list {
display: flex;