1215 行
56 KiB
PHP
1215 行
56 KiB
PHP
<?php
|
|
// 启用页面混淆和Gzip压缩
|
|
include 'minify.php';
|
|
|
|
// 启动会话
|
|
session_start();
|
|
|
|
// 引入模块
|
|
require_once 'modules/user.php';
|
|
require_once 'modules/message.php';
|
|
require_once 'modules/friend.php';
|
|
|
|
// 数据库初始化已在db.php中完成
|
|
|
|
// 自动登录:检查记住我令牌
|
|
autoLoginWithRememberToken();
|
|
|
|
// 每次访问都重新获取IP地址和地理位置
|
|
if (isLoggedIn()) {
|
|
$userId = $_SESSION['user_id'];
|
|
$db = Database::getInstance();
|
|
|
|
// 获取用户信息(包括头像)
|
|
$userResult = $db->query("SELECT * FROM users WHERE id = :user_id", ['user_id' => $userId]);
|
|
$currentUser = !empty($userResult) ? $userResult[0] : null;
|
|
|
|
// 直接获取用户IP地址
|
|
$userIp = getUserIP();
|
|
|
|
if ($userIp) {
|
|
// 调用IP地理位置API
|
|
$locationData = getIpLocation($userIp);
|
|
|
|
// 更新用户地理位置信息
|
|
if ($locationData && isset($locationData['status']) && $locationData['status'] === 'success') {
|
|
$db->execute("UPDATE users SET last_ip = :ip, country = :country, province = :province, city = :city WHERE id = :user_id", [
|
|
'ip' => $userIp,
|
|
'country' => $locationData['country'] ?? '',
|
|
'province' => $locationData['regionName'] ?? '',
|
|
'city' => $locationData['city'] ?? '',
|
|
'user_id' => $userId
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 处理登出请求
|
|
if (isset($_GET['action']) && $_GET['action'] == 'logout') {
|
|
logout();
|
|
header('Location: index.php');
|
|
exit;
|
|
}
|
|
|
|
// 处理邮箱验证请求
|
|
if (isset($_GET['action']) && $_GET['action'] == 'verify_email' && isset($_GET['code'])) {
|
|
$verificationCode = $_GET['code'];
|
|
if (verifyEmail($verificationCode)) {
|
|
// 验证成功,重定向到首页以刷新用户状态
|
|
header('Location: index.php?success=邮箱验证成功!');
|
|
exit;
|
|
} else {
|
|
// 验证失败,重定向到首页显示错误信息
|
|
header('Location: index.php?error=邮箱验证失败,请检查验证链接是否正确。');
|
|
exit;
|
|
}
|
|
}
|
|
|
|
// 检查是否需要用户同意协议
|
|
function requireTermsAgreement() {
|
|
if (!isLoggedIn()) {
|
|
return 1; // 未登录用户默认需要同意
|
|
}
|
|
|
|
$userId = $_SESSION['user_id'];
|
|
$db = Database::getInstance();
|
|
$result = $db->query("SELECT getip FROM users WHERE id = :user_id", ['user_id' => $userId]);
|
|
return !empty($result) ? (int)$result[0]['getip'] : 1; // 默认开启
|
|
}
|
|
|
|
// 获取用户IP地址
|
|
function getUserIP() {
|
|
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
|
|
return $_SERVER['HTTP_CLIENT_IP'];
|
|
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
|
return $_SERVER['HTTP_X_FORWARDED_FOR'];
|
|
} else {
|
|
return $_SERVER['REMOTE_ADDR'] ?? '';
|
|
}
|
|
}
|
|
|
|
// 获取IP地理位置信息
|
|
function getIpLocation($ip) {
|
|
$url = "http://ip-api.com/json/{$ip}?lang=zh-CN";
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
|
|
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36');
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
|
curl_setopt($ch, CURLOPT_MAXREDIRS, 3);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
$error = curl_error($ch);
|
|
curl_close($ch);
|
|
|
|
if ($error) {
|
|
return false;
|
|
}
|
|
|
|
if ($httpCode !== 200) {
|
|
return false;
|
|
}
|
|
|
|
$data = json_decode($response, true);
|
|
|
|
if (!$data) {
|
|
return false;
|
|
}
|
|
|
|
if (!isset($data['status']) || $data['status'] !== 'success') {
|
|
return false;
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
// 处理登录请求
|
|
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['login'])) {
|
|
// 验证是否同意协议
|
|
if (requireTermsAgreement() && (!isset($_POST['agree_terms']) || $_POST['agree_terms'] != 'on')) {
|
|
$error = '请阅读并同意用户协议';
|
|
} else {
|
|
$username = $_POST['username'];
|
|
// 必须使用加密后的密码
|
|
if (!isset($_POST['password_hash']) || empty($_POST['password_hash'])) {
|
|
$error = '无效的请求';
|
|
} else {
|
|
$password = $_POST['password_hash'];
|
|
$rememberMe = isset($_POST['remember_me']) && $_POST['remember_me'] == 'on';
|
|
if (login($username, $password, $rememberMe)) {
|
|
// 获取用户IP地址
|
|
$userIp = getUserIP();
|
|
|
|
// 检查用户是否同意获取IP
|
|
$userId = $_SESSION['user_id'];
|
|
$db = Database::getInstance();
|
|
$userResult = $db->query("SELECT getip FROM users WHERE id = :user_id", ['user_id' => $userId]);
|
|
$userGetIp = !empty($userResult) ? (int)$userResult[0]['getip'] : 1;
|
|
|
|
if ($userGetIp == 1 && $userIp) {
|
|
// 调用IP地理位置API
|
|
$locationData = getIpLocation($userIp);
|
|
|
|
// 更新用户地理位置信息
|
|
if ($locationData && isset($locationData['status']) && $locationData['status'] === 'success') {
|
|
$db->execute("UPDATE users SET last_ip = :ip, country = :country, province = :province, city = :city WHERE id = :user_id", [
|
|
'ip' => $userIp,
|
|
'country' => $locationData['country'] ?? '',
|
|
'province' => $locationData['regionName'] ?? '',
|
|
'city' => $locationData['city'] ?? '',
|
|
'user_id' => $userId
|
|
]);
|
|
} else {
|
|
// API调用失败,只更新IP地址
|
|
$db->execute("UPDATE users SET last_ip = :ip, country = '', province = '', city = '' WHERE id = :user_id", [
|
|
'ip' => $userIp,
|
|
'user_id' => $userId
|
|
]);
|
|
}
|
|
} else {
|
|
// 用户不同意获取IP,清空地理位置信息
|
|
$db->execute("UPDATE users SET last_ip = '', country = '', province = '', city = '' WHERE id = :user_id", [
|
|
'user_id' => $userId
|
|
]);
|
|
}
|
|
|
|
header('Location: index.php');
|
|
exit;
|
|
} else {
|
|
$error = '用户名或密码错误';
|
|
}
|
|
} // 关闭 password_hash 检查的 else
|
|
}
|
|
}
|
|
|
|
// 处理URL参数中的成功和错误信息
|
|
if (isset($_GET['success'])) {
|
|
$success = $_GET['success'];
|
|
}
|
|
if (isset($_GET['error'])) {
|
|
$error = $_GET['error'];
|
|
}
|
|
|
|
// 处理注册请求
|
|
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['register'])) {
|
|
// 验证是否同意协议
|
|
if (requireTermsAgreement() && (!isset($_POST['agree_terms']) || $_POST['agree_terms'] != 'on')) {
|
|
$error = '请阅读并同意用户协议';
|
|
} else {
|
|
$username = $_POST['username'];
|
|
$email = $_POST['email'];
|
|
$password = $_POST['password'];
|
|
if (register($username, $password, $email)) {
|
|
login($username, $password);
|
|
|
|
// 获取用户IP地址
|
|
$userIp = getUserIP();
|
|
|
|
// 检查用户是否同意获取IP
|
|
$userId = $_SESSION['user_id'];
|
|
$db = Database::getInstance();
|
|
$userResult = $db->query("SELECT getip FROM users WHERE id = :user_id", ['user_id' => $userId]);
|
|
$userGetIp = !empty($userResult) ? (int)$userResult[0]['getip'] : 1;
|
|
|
|
if ($userGetIp == 1 && $userIp) {
|
|
// 调用IP地理位置API
|
|
$locationData = getIpLocation($userIp);
|
|
|
|
// 更新用户地理位置信息
|
|
if ($locationData && isset($locationData['status']) && $locationData['status'] === 'success') {
|
|
$db->execute("UPDATE users SET last_ip = :ip, country = :country, province = :province, city = :city WHERE id = :user_id", [
|
|
'ip' => $userIp,
|
|
'country' => $locationData['country'] ?? '',
|
|
'province' => $locationData['regionName'] ?? '',
|
|
'city' => $locationData['city'] ?? '',
|
|
'user_id' => $userId
|
|
]);
|
|
} else {
|
|
// API调用失败,只更新IP地址
|
|
$db->execute("UPDATE users SET last_ip = :ip, country = '', province = '', city = '' WHERE id = :user_id", [
|
|
'ip' => $userIp,
|
|
'user_id' => $userId
|
|
]);
|
|
}
|
|
} else {
|
|
// 用户不同意获取IP,清空地理位置信息
|
|
$db->execute("UPDATE users SET last_ip = '', country = '', city = '' WHERE id = :user_id", [
|
|
'user_id' => $userId
|
|
]);
|
|
}
|
|
|
|
header('Location: index.php');
|
|
exit;
|
|
} else {
|
|
$error = '用户名已存在';
|
|
}
|
|
}
|
|
}
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Parlz - 实时聊天应用</title>
|
|
<link rel="stylesheet" href="style.css">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
|
|
</head>
|
|
<body>
|
|
<?php if (!isLoggedIn()): ?>
|
|
<!-- 登录/注册页面 -->
|
|
<div class="login-container">
|
|
<div class="login-box">
|
|
<h1 class="app-title">Parlz</h1>
|
|
<div class="login-tabs">
|
|
<button class="tab-btn active" data-tab="login">登录</button>
|
|
<button class="tab-btn" data-tab="register">注册</button>
|
|
</div>
|
|
|
|
<!-- 登录表单 -->
|
|
<div id="login" class="tab-content active">
|
|
<?php if (isset($error)) echo '<div class="error">' . $error . '</div>'; ?>
|
|
<form id="login-form" method="POST">
|
|
<div class="input-group" id="login-username-group">
|
|
<i class="fas fa-user"></i>
|
|
<input type="text" name="username" id="login-username" placeholder="用户名">
|
|
<span class="required-mark">*</span>
|
|
<div class="error-message" id="login-username-error">请输入用户名</div>
|
|
</div>
|
|
<div class="input-group" id="login-password-group">
|
|
<i class="fas fa-lock"></i>
|
|
<input type="password" id="login-password" placeholder="密码">
|
|
<input type="hidden" name="password_hash" id="password-hash-field">
|
|
<span class="required-mark">*</span>
|
|
<div class="error-message" id="login-password-error">请输入密码</div>
|
|
</div>
|
|
<div class="remember-me">
|
|
<input type="checkbox" name="remember_me" id="remember-me">
|
|
<label for="remember-me">记住我</label>
|
|
</div>
|
|
<?php if (requireTermsAgreement()): ?>
|
|
<div class="terms-checkbox" id="login-terms-group">
|
|
<input type="checkbox" name="agree_terms" id="login-agree">
|
|
<label for="login-agree">我已阅读并同意 <a href="terms.php" target="_blank">《Parlz用户协议守则》</a></label>
|
|
<div class="error-message" id="login-terms-error">请阅读并同意用户协议</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
<button type="submit" name="login" class="login-btn">登录</button>
|
|
</form>
|
|
|
|
<!-- 第三方登录 -->
|
|
<div class="third-party-login">
|
|
<div class="login-divider">
|
|
<span>或使用以下方式登录</span>
|
|
</div>
|
|
<button class="third-party-btn qq-login-btn" id="qq-login-btn">
|
|
<i class="fab fa-qq"></i>
|
|
<span>使用QQ登录</span>
|
|
</button>
|
|
<button class="third-party-btn github-login-btn" id="github-login-btn">
|
|
<i class="fab fa-github"></i>
|
|
<span>使用GitHub登录</span>
|
|
</button>
|
|
<button class="third-party-btn microsoft-login-btn" id="microsoft-login-btn">
|
|
<i class="fab fa-microsoft"></i>
|
|
<span>使用微软登录</span>
|
|
</button>
|
|
<button class="third-party-btn steam-login-btn" id="steam-login-btn">
|
|
<i class="fab fa-steam"></i>
|
|
<span>使用Steam登录</span>
|
|
</button>
|
|
<button class="third-party-btn discord-login-btn" id="discord-login-btn">
|
|
<i class="fab fa-discord"></i>
|
|
<span>使用Discord登录</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 注册表单 -->
|
|
<div id="register" class="tab-content">
|
|
<?php if (isset($error)) echo '<div class="error">' . $error . '</div>'; ?>
|
|
<form id="register-form" method="POST">
|
|
<div class="input-group" id="register-username-group">
|
|
<i class="fas fa-user"></i>
|
|
<input type="text" name="username" id="register-username" placeholder="用户名">
|
|
<span class="required-mark">*</span>
|
|
<div class="error-message" id="register-username-error">请输入用户名</div>
|
|
</div>
|
|
<div class="input-group" id="register-email-group">
|
|
<i class="fas fa-envelope"></i>
|
|
<input type="email" name="email" id="register-email" placeholder="邮箱">
|
|
<span class="required-mark">*</span>
|
|
<div class="error-message" id="register-email-error">请输入邮箱</div>
|
|
</div>
|
|
<div class="input-group" id="register-password-group">
|
|
<i class="fas fa-lock"></i>
|
|
<input type="password" name="password" id="register-password" placeholder="密码">
|
|
<span class="required-mark">*</span>
|
|
<div class="error-message" id="register-password-error">请输入密码(至少6位)</div>
|
|
</div>
|
|
<?php if (requireTermsAgreement()): ?>
|
|
<div class="terms-checkbox" id="register-terms-group">
|
|
<input type="checkbox" name="agree_terms" id="register-agree">
|
|
<label for="register-agree">我已阅读并同意 <a href="terms.php" target="_blank">《Parlz用户协议守则》</a></label>
|
|
<div class="error-message" id="register-terms-error">请阅读并同意用户协议</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
<button type="submit" name="register" class="login-btn">注册</button>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- 自定义弹窗提醒 -->
|
|
<div id="custom-alert-overlay" class="custom-alert-overlay">
|
|
<div class="custom-alert">
|
|
<div class="custom-alert-icon">
|
|
<i class="fas fa-exclamation-circle"></i>
|
|
</div>
|
|
<div class="custom-alert-title">提示</div>
|
|
<div class="custom-alert-message" id="custom-alert-message"></div>
|
|
<button class="custom-alert-btn" id="custom-alert-btn">确定</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php else: ?>
|
|
<!-- 检查用户是否已绑定邮箱 -->
|
|
<?php
|
|
$currentUserId = $_SESSION['user_id'];
|
|
$userEmail = getUserEmail($currentUserId);
|
|
$emailVerified = isEmailVerified($currentUserId);
|
|
?>
|
|
|
|
<!-- 邮箱绑定提醒横幅 -->
|
|
<?php if (empty($userEmail) || !$emailVerified): ?>
|
|
<div class="email-banner">
|
|
<div class="banner-content">
|
|
<i class="fas fa-envelope"></i>
|
|
<div class="banner-text">
|
|
<h4>请绑定并验证您的邮箱</h4>
|
|
<p>绑定邮箱后才能添加好友、创建或加入群组</p>
|
|
</div>
|
|
<button class="banner-btn" id="bind-email-btn">绑定邮箱</button>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- 主聊天界面 -->
|
|
<div class="chat-app-container">
|
|
<!-- 侧边栏背景遮罩 -->
|
|
<div class="sidebar-backdrop" id="sidebar-backdrop"></div>
|
|
<div class="chat-app">
|
|
<!-- 左侧边栏 -->
|
|
<div class="sidebar">
|
|
<!-- 顶部用户信息 -->
|
|
<div class="sidebar-header">
|
|
<div class="user-info">
|
|
<div class="avatar" style="cursor: pointer;" onclick="window.location.href='profile.php?name=<?php echo urlencode($_SESSION['username']); ?>'">
|
|
<?php
|
|
if (!empty($currentUser) && !empty($currentUser['avatar'])) {
|
|
echo '<img src="' . htmlspecialchars($currentUser['avatar']) . '" alt="头像" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover; cursor: pointer;">';
|
|
} else {
|
|
echo '<i class="fas fa-user-circle" style="cursor: pointer;"></i>';
|
|
}
|
|
?>
|
|
</div>
|
|
<div class="username"><?php echo $_SESSION['username']; ?></div>
|
|
</div>
|
|
<div class="header-actions">
|
|
<button class="action-btn" id="add-friend-btn">
|
|
<i class="fas fa-user-plus"></i>
|
|
</button>
|
|
<button class="action-btn" id="friend-requests-btn">
|
|
<i class="fas fa-bell"></i>
|
|
<span id="request-badge" style="display: none; position: absolute; top: 0; right: 0; background: #ff4d4f; color: white; border-radius: 50%; width: 16px; height: 16px; font-size: 10px; display: flex; align-items: center; justify-content: center;">0</span>
|
|
</button>
|
|
<button class="action-btn theme-toggle-btn" id="theme-toggle-btn" title="切换主题">
|
|
<i class="fas fa-sun sun-icon"></i>
|
|
<i class="fas fa-moon moon-icon"></i>
|
|
</button>
|
|
<div class="dropdown-menu-container">
|
|
<button class="action-btn" id="menu-btn">
|
|
<i class="fas fa-ellipsis-v"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 搜索框 -->
|
|
<div class="search-box">
|
|
<i class="fas fa-search"></i>
|
|
<input type="text" placeholder="搜索好友或群组" id="search-friends">
|
|
</div>
|
|
|
|
<!-- 电脑版标签页 -->
|
|
<div class="tabs">
|
|
<button class="tab-btn active" data-tab="friends">好友</button>
|
|
<button class="tab-btn" data-tab="groups">群组</button>
|
|
</div>
|
|
|
|
<!-- 电脑版分开的聊天列表 -->
|
|
<div class="chat-lists">
|
|
<!-- 好友列表 -->
|
|
<div class="tab-content active" id="friends-tab">
|
|
<div class="friends-list" id="friends-list">
|
|
<!-- 好友列表将通过JavaScript动态生成 -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 群组列表 -->
|
|
<div class="tab-content" id="groups-tab">
|
|
<div class="groups-list" id="groups-list">
|
|
<!-- 群组列表将通过JavaScript动态生成 -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 手机版合并的聊天列表 -->
|
|
<div class="mobile-chat-list" id="mobile-chat-list">
|
|
<!-- 好友列表区域 -->
|
|
<div class="chat-section">
|
|
<div class="section-header">
|
|
<h3>好友</h3>
|
|
</div>
|
|
<div class="friends-list-mobile">
|
|
<!-- 好友列表将通过JavaScript动态生成 -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 群组列表区域 -->
|
|
<div class="chat-section">
|
|
<div class="section-header">
|
|
<h3>群组</h3>
|
|
</div>
|
|
<div class="groups-list-mobile">
|
|
<!-- 群组列表将通过JavaScript动态生成 -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 右侧聊天区域 -->
|
|
<div class="chat-main">
|
|
<!-- 聊天头部 -->
|
|
<div class="chat-header" id="chat-header">
|
|
<button class="action-btn toggle-sidebar-btn" id="toggle-sidebar-btn">
|
|
<i class="fas fa-bars"></i>
|
|
</button>
|
|
<div class="chat-user-info">
|
|
<div class="avatar">
|
|
<i class="fas fa-user-circle"></i>
|
|
</div>
|
|
<div class="username" id="chat-username">选择一个好友开始聊天</div>
|
|
</div>
|
|
<div class="chat-actions">
|
|
<button class="action-btn" id="search-chat-btn">
|
|
<i class="fas fa-search"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 群公告显示区域 -->
|
|
<div class="group-announcement" id="group-announcement" style="display: none;">
|
|
<div style="display: flex; align-items: flex-start;">
|
|
<i class="fas fa-bullhorn"></i>
|
|
<div style="flex: 1;">
|
|
<div class="announcement-title">群公告</div>
|
|
<div id="announcement-content"></div>
|
|
<div id="announcement-time" class="announcement-time"></div>
|
|
</div>
|
|
<button id="close-announcement-btn" class="close-announcement-btn">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 聊天搜索栏 -->
|
|
<div class="chat-search" id="chat-search" style="display: none;">
|
|
<div class="search-box">
|
|
<i class="fas fa-search"></i>
|
|
<input type="text" placeholder="搜索聊天记录" id="search-chat-input">
|
|
<button class="action-btn close-search-btn">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
<div class="search-results" id="search-results" style="display: none;">
|
|
<div class="search-header">
|
|
<span>搜索结果</span>
|
|
<span class="search-count" id="search-count">0</span>
|
|
</div>
|
|
<div class="search-list" id="search-list">
|
|
<!-- 搜索结果将通过JavaScript动态生成 -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 聊天内容 -->
|
|
<div class="chat-content" id="chat-content">
|
|
<div class="empty-chat">
|
|
<i class="fas fa-comments"></i>
|
|
<p>选择一个好友开始聊天</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 聊天输入框 -->
|
|
<div class="chat-input-container" id="chat-input-container" style="display: none;">
|
|
<div class="input-tools">
|
|
<button class="tool-btn">
|
|
<i class="fas fa-smile"></i>
|
|
</button>
|
|
<button class="tool-btn">
|
|
<i class="fas fa-image"></i>
|
|
</button>
|
|
<button class="tool-btn">
|
|
<i class="fas fa-paperclip"></i>
|
|
</button>
|
|
</div>
|
|
<div class="input-wrapper">
|
|
<textarea id="message-input" placeholder="输入消息..." rows="1"></textarea>
|
|
<button class="send-btn" id="send-btn">
|
|
<i class="fas fa-paper-plane"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 底部导航栏 -->
|
|
<div class="bottom-nav">
|
|
<button class="nav-item active" data-nav="chat">
|
|
<i class="fas fa-comment"></i>
|
|
<span>消息</span>
|
|
</button>
|
|
<button class="nav-item" data-nav="contacts">
|
|
<i class="fas fa-address-book"></i>
|
|
<span>通讯录</span>
|
|
</button>
|
|
<button class="nav-item" data-nav="discover">
|
|
<i class="fas fa-compass"></i>
|
|
<span>发现</span>
|
|
</button>
|
|
<button class="nav-item" data-nav="me">
|
|
<i class="fas fa-user"></i>
|
|
<span>我</span>
|
|
</button>
|
|
<button class="nav-item theme-toggle-btn" id="theme-toggle-btn-mobile" title="切换主题">
|
|
<i class="fas fa-sun sun-icon"></i>
|
|
<i class="fas fa-moon moon-icon"></i>
|
|
<span>主题</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 添加好友模态框 -->
|
|
<div class="modal" id="add-friend-modal">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h3>添加好友</h3>
|
|
<button class="close-btn" id="close-modal">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="input-group">
|
|
<i class="fas fa-user"></i>
|
|
<input type="text" id="add-friend-username" placeholder="输入好友用户名">
|
|
</div>
|
|
<div id="add-friend-result"></div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-cancel" id="cancel-add-friend">取消</button>
|
|
<button class="btn btn-primary" id="confirm-add-friend">添加</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 创建群组模态框 -->
|
|
<div class="modal" id="create-group-modal">
|
|
<div class="modal-content" style="max-width: 500px;">
|
|
<div class="modal-header">
|
|
<h3>创建群组</h3>
|
|
<button class="close-btn" id="close-group-modal">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="input-group">
|
|
<i class="fas fa-users"></i>
|
|
<input type="text" id="group-name" placeholder="输入群组名称">
|
|
</div>
|
|
<div style="margin-top: 20px;">
|
|
<h4 style="margin-bottom: 10px;">选择群成员</h4>
|
|
<div id="friends-list-for-group" style="max-height: 200px; overflow-y: auto;">
|
|
<!-- 好友列表将通过JavaScript动态生成 -->
|
|
</div>
|
|
</div>
|
|
<div id="create-group-result" style="margin-top: 10px;"></div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-cancel" id="cancel-create-group">取消</button>
|
|
<button class="btn btn-primary" id="confirm-create-group">创建</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 通过群号加入群组模态框 -->
|
|
<div class="modal" id="join-group-modal">
|
|
<div class="modal-content" style="max-width: 400px;">
|
|
<div class="modal-header">
|
|
<h3>通过群号加入群组</h3>
|
|
<button class="close-btn" id="close-join-group-modal">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="input-group">
|
|
<i class="fas fa-hashtag"></i>
|
|
<input type="text" id="join-group-id" placeholder="输入群号">
|
|
</div>
|
|
<div id="join-group-result" style="margin-top: 10px;"></div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-cancel" id="cancel-join-group">取消</button>
|
|
<button class="btn btn-primary" id="confirm-join-group">加入</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 邀请好友加入群组模态框 -->
|
|
<div class="modal" id="invite-group-modal">
|
|
<div class="modal-content" style="max-width: 500px;">
|
|
<div class="modal-header">
|
|
<h3>邀请好友加入群组</h3>
|
|
<button class="close-btn" id="close-invite-group-modal">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="input-group">
|
|
<i class="fas fa-user-plus"></i>
|
|
<input type="text" id="invite-friend-username" placeholder="输入好友用户名">
|
|
</div>
|
|
<div id="invite-group-result" style="margin-top: 10px;"></div>
|
|
|
|
<!-- 好友列表 -->
|
|
<div style="margin-top: 20px;">
|
|
<h4 style="margin-bottom: 10px;">选择好友</h4>
|
|
<div id="friends-list-for-invite" style="max-height: 300px; overflow-y: auto;">
|
|
<!-- 好友列表将通过JavaScript动态生成 -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-cancel" id="cancel-invite-group">取消</button>
|
|
<button class="btn btn-primary" id="confirm-invite-group">邀请</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 群组设置模态框 -->
|
|
<div class="modal" id="group-settings-modal">
|
|
<div class="modal-content" style="max-width: 500px;">
|
|
<div class="modal-header">
|
|
<h3>群组设置</h3>
|
|
<button class="close-btn" id="close-group-settings-modal">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<!-- 修改群头像 -->
|
|
<div style="margin-bottom: 20px; text-align: center;">
|
|
<h4 style="margin-bottom: 10px;">群头像</h4>
|
|
<div style="position: relative; display: inline-block; margin-bottom: 10px;">
|
|
<div id="group-avatar-preview" style="width: 100px; height: 100px; border-radius: 50%; overflow: hidden; border: 2px solid #ddd;">
|
|
<img src="" alt="群头像" style="width: 100%; height: 100%; object-fit: cover;">
|
|
</div>
|
|
<button id="change-group-avatar-btn" class="btn btn-primary" style="position: absolute; bottom: 0; right: 0; transform: translate(25%, 25%); border-radius: 50%; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; padding: 0;">
|
|
<i class="fas fa-camera"></i>
|
|
</button>
|
|
<input type="file" id="group-avatar-input" accept="image/*" style="display: none;">
|
|
</div>
|
|
<div id="group-avatar-result" style="margin-top: 10px;"></div>
|
|
</div>
|
|
|
|
<!-- 修改群组名称 -->
|
|
<div style="margin-bottom: 20px;">
|
|
<h4 style="margin-bottom: 10px;">群组名称</h4>
|
|
<div class="input-group">
|
|
<i class="fas fa-users"></i>
|
|
<input type="text" id="group-settings-name" placeholder="输入群组名称">
|
|
</div>
|
|
<div id="group-name-result" style="margin-top: 10px;"></div>
|
|
</div>
|
|
|
|
<!-- 群组公告管理 -->
|
|
<div style="margin-bottom: 20px;">
|
|
<h4 style="margin-bottom: 10px;">群组公告</h4>
|
|
<div id="group-announcement-admin" style="display: none;">
|
|
<textarea id="group-announcement-content" placeholder="输入群组公告内容" rows="4" style="width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px;"></textarea>
|
|
<button id="save-announcement-btn" class="btn btn-primary" style="margin-top: 10px;">发布公告</button>
|
|
<div id="announcement-result" style="margin-top: 10px;"></div>
|
|
</div>
|
|
<div id="group-announcements-list" style="max-height: 200px; overflow-y: auto;">
|
|
<!-- 群组公告列表将通过JavaScript动态生成 -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 群组成员管理 -->
|
|
<div>
|
|
<h4 style="margin-bottom: 10px;">群组成员</h4>
|
|
<div id="group-members-list" style="max-height: 200px; overflow-y: auto;">
|
|
<!-- 群组成员列表将通过JavaScript动态生成 -->
|
|
</div>
|
|
<div id="group-members-result" style="margin-top: 10px;"></div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-danger" id="delete-group-btn" style="margin-right: auto;">解散群聊</button>
|
|
<button class="btn btn-cancel" id="cancel-group-settings">取消</button>
|
|
<button class="btn btn-primary" id="save-group-settings">保存设置</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 群组公告模态框 -->
|
|
<div class="modal" id="group-announcement-modal">
|
|
<div class="modal-content" style="max-width: 500px;">
|
|
<div class="modal-header">
|
|
<h3>群组公告</h3>
|
|
<button class="close-btn" id="close-group-announcement-modal">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div id="group-announcement-admin-view" style="display: none; margin-bottom: 20px;">
|
|
<textarea id="group-announcement-content-view" placeholder="输入群组公告内容" rows="4" style="width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px;"></textarea>
|
|
<button id="save-announcement-btn-view" class="btn btn-primary" style="margin-top: 10px;">发布公告</button>
|
|
<div id="announcement-result-view" style="margin-top: 10px;"></div>
|
|
</div>
|
|
<div id="group-announcements-list-view" style="max-height: 300px; overflow-y: auto;">
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-cancel" id="cancel-group-announcement">关闭</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 好友请求模态框 -->
|
|
<div class="modal" id="friend-requests-modal">
|
|
<div class="modal-content" style="max-width: 400px;">
|
|
<div class="modal-header">
|
|
<h3>好友请求</h3>
|
|
<button class="close-btn" id="close-friend-requests-modal">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div id="friend-requests-list" style="max-height: 300px; overflow-y: auto;">
|
|
<!-- 好友请求列表将通过JavaScript动态生成 -->
|
|
</div>
|
|
<div id="friend-requests-result" style="margin-top: 10px;"></div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-cancel" id="cancel-friend-requests">关闭</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 绑定邮箱模态框 -->
|
|
<div class="modal" id="bind-email-modal">
|
|
<div class="modal-content" style="max-width: 400px;">
|
|
<div class="modal-header">
|
|
<h3>绑定邮箱</h3>
|
|
<button class="close-btn" id="close-bind-email-modal">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="input-group">
|
|
<i class="fas fa-envelope"></i>
|
|
<input type="email" id="bind-email-input" placeholder="输入您的邮箱" required>
|
|
</div>
|
|
<div id="bind-email-result" style="margin-top: 10px;"></div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-cancel" id="cancel-bind-email">取消</button>
|
|
<button class="btn btn-primary" id="confirm-bind-email">绑定</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 备注编辑弹窗 -->
|
|
<div class="modal" id="set-note-modal">
|
|
<div class="modal-content" style="max-width: 400px;">
|
|
<div class="modal-header">
|
|
<h3>设置备注</h3>
|
|
<button class="close-btn" id="close-set-note-modal">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="input-group">
|
|
<i class="fas fa-sticky-note"></i>
|
|
<input type="text" id="note-input" placeholder="输入备注名称" maxlength="50">
|
|
</div>
|
|
<div style="font-size: 12px; color: #888; margin-top: 8px;">
|
|
当前名称: <span id="current-name"></span>
|
|
</div>
|
|
<input type="hidden" id="note-target-id">
|
|
<input type="hidden" id="note-target-type">
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-cancel" id="cancel-set-note">取消</button>
|
|
<button class="btn btn-primary" id="confirm-set-note">保存备注</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 右键菜单 -->
|
|
<div class="context-menu" id="context-menu">
|
|
<ul>
|
|
<li id="context-menu-profile">
|
|
<i class="fas fa-user"></i>
|
|
<span>名片</span>
|
|
</li>
|
|
<li id="context-menu-set-note">
|
|
<i class="fas fa-sticky-note"></i>
|
|
<span>设置备注</span>
|
|
</li>
|
|
</ul>
|
|
<input type="hidden" id="context-menu-target-id">
|
|
<input type="hidden" id="context-menu-target-type">
|
|
<input type="hidden" id="context-menu-target-name">
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- 引入JavaScript -->
|
|
<script>
|
|
// 自定义弹窗函数
|
|
function showAlert(message) {
|
|
document.getElementById('custom-alert-message').textContent = message;
|
|
document.getElementById('custom-alert-overlay').classList.add('show');
|
|
}
|
|
|
|
function hideAlert() {
|
|
document.getElementById('custom-alert-overlay').classList.remove('show');
|
|
}
|
|
|
|
// 弹窗关闭按钮事件
|
|
document.getElementById('custom-alert-btn').addEventListener('click', hideAlert);
|
|
|
|
// 点击遮罩层关闭弹窗
|
|
document.getElementById('custom-alert-overlay').addEventListener('click', function(e) {
|
|
if (e.target === this) {
|
|
hideAlert();
|
|
}
|
|
});
|
|
|
|
// 显示输入框错误状态
|
|
function showFieldError(fieldId, groupId) {
|
|
document.getElementById(groupId).classList.add('error');
|
|
document.getElementById(fieldId).focus();
|
|
}
|
|
|
|
// 清除输入框错误状态
|
|
function clearFieldError(groupId) {
|
|
document.getElementById(groupId).classList.remove('error');
|
|
}
|
|
|
|
// SHA-256 哈希函数(纯JavaScript实现)
|
|
function sha256(input) {
|
|
if (!input || typeof input !== 'string') {
|
|
console.error('sha256 函数:输入无效');
|
|
return '';
|
|
}
|
|
const chars = '0123456789abcdef';
|
|
const K = [
|
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
|
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
|
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
|
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
|
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
|
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
|
|
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
|
|
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
|
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
|
];
|
|
|
|
function rightRotate(value, amount) {
|
|
return (value >>> amount) | (value << (32 - amount));
|
|
}
|
|
|
|
let message = new Uint8Array(input.length);
|
|
for (let i = 0; i < input.length; i++) {
|
|
message[i] = input.charCodeAt(i);
|
|
}
|
|
|
|
const originalBitLength = message.length * 8;
|
|
const paddingLength = (512 - ((originalBitLength + 64) % 512)) % 512;
|
|
|
|
const paddedMessage = new Uint8Array(message.length + paddingLength / 8 + 8);
|
|
paddedMessage.set(message);
|
|
paddedMessage[message.length] = 0x80;
|
|
|
|
for (let i = 0; i < 8; i++) {
|
|
paddedMessage[paddedMessage.length - 8 + i] = (originalBitLength >>> (56 - i * 8)) & 0xff;
|
|
}
|
|
|
|
const blocks = [];
|
|
for (let i = 0; i < paddedMessage.length; i += 64) {
|
|
const block = [];
|
|
for (let j = 0; j < 64; j += 4) {
|
|
let word = 0;
|
|
for (let k = 0; k < 4; k++) {
|
|
word = (word << 8) | paddedMessage[i + j + k];
|
|
}
|
|
block.push(word);
|
|
}
|
|
blocks.push(block);
|
|
}
|
|
|
|
let h0 = 0x6a09e667, h1 = 0xbb67ae85, h2 = 0x3c6ef372, h3 = 0xa54ff53a;
|
|
let h4 = 0x510e527f, h5 = 0x9b05688c, h6 = 0x1f83d9ab, h7 = 0x5be0cd19;
|
|
|
|
for (const block of blocks) {
|
|
const w = new Array(64);
|
|
for (let i = 0; i < 16; i++) {
|
|
w[i] = block[i];
|
|
}
|
|
for (let i = 16; i < 64; i++) {
|
|
const s0 = rightRotate(w[i - 15], 7) ^ rightRotate(w[i - 15], 18) ^ (w[i - 15] >>> 3);
|
|
const s1 = rightRotate(w[i - 2], 17) ^ rightRotate(w[i - 2], 19) ^ (w[i - 2] >>> 10);
|
|
w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0;
|
|
}
|
|
|
|
let a = h0, b = h1, c = h2, d = h3;
|
|
let e = h4, f = h5, g = h6, h = h7;
|
|
|
|
for (let i = 0; i < 64; i++) {
|
|
const S1 = rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25);
|
|
const ch = (e & f) ^ (~e & g);
|
|
const temp1 = (h + S1 + ch + K[i] + w[i]) >>> 0;
|
|
const S0 = rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22);
|
|
const maj = (a & b) ^ (a & c) ^ (b & c);
|
|
const temp2 = (S0 + maj) >>> 0;
|
|
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = (d + temp1) >>> 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = (temp1 + temp2) >>> 0;
|
|
}
|
|
|
|
h0 = (h0 + a) >>> 0;
|
|
h1 = (h1 + b) >>> 0;
|
|
h2 = (h2 + c) >>> 0;
|
|
h3 = (h3 + d) >>> 0;
|
|
h4 = (h4 + e) >>> 0;
|
|
h5 = (h5 + f) >>> 0;
|
|
h6 = (h6 + g) >>> 0;
|
|
h7 = (h7 + h) >>> 0;
|
|
}
|
|
|
|
const hash = [h0, h1, h2, h3, h4, h5, h6, h7];
|
|
let result = '';
|
|
for (const word of hash) {
|
|
for (let i = 3; i >= 0; i--) {
|
|
result += chars[(word >>> (i * 8)) & 0xf];
|
|
result += chars[(word >>> (i * 8 + 4)) & 0xf];
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
document.getElementById('login-form').addEventListener('submit', function(e) {
|
|
const username = document.getElementById('login-username').value.trim();
|
|
const password = document.getElementById('login-password').value.trim();
|
|
const agreeTerms = document.getElementById('login-agree');
|
|
|
|
// 清除所有错误状态
|
|
clearFieldError('login-username-group');
|
|
clearFieldError('login-password-group');
|
|
<?php if (requireTermsAgreement()): ?>
|
|
clearFieldError('login-terms-group');
|
|
<?php endif; ?>
|
|
|
|
let hasError = false;
|
|
|
|
if (!username) {
|
|
e.preventDefault();
|
|
showFieldError('login-username', 'login-username-group');
|
|
hasError = true;
|
|
}
|
|
|
|
if (!password) {
|
|
e.preventDefault();
|
|
showFieldError('login-password', 'login-password-group');
|
|
hasError = true;
|
|
}
|
|
|
|
<?php if (requireTermsAgreement()): ?>
|
|
if (!agreeTerms.checked) {
|
|
e.preventDefault();
|
|
showFieldError('login-agree', 'login-terms-group');
|
|
hasError = true;
|
|
}
|
|
<?php endif; ?>
|
|
|
|
if (hasError) {
|
|
e.preventDefault();
|
|
showAlert('请填写所有必填项');
|
|
return;
|
|
}
|
|
|
|
// 加密密码后再提交
|
|
e.preventDefault();
|
|
|
|
// 计算密码哈希
|
|
const hashedPassword = sha256(password);
|
|
console.log('计算的密码哈希:', hashedPassword);
|
|
console.log('密码哈希长度:', hashedPassword.length);
|
|
|
|
// 设置隐藏字段值
|
|
const passwordHashField = document.getElementById('password-hash-field');
|
|
console.log('隐藏字段元素:', passwordHashField);
|
|
if (passwordHashField) {
|
|
passwordHashField.value = hashedPassword;
|
|
console.log('隐藏字段值已设置:', passwordHashField.value);
|
|
} else {
|
|
console.error('未找到密码哈希隐藏字段!');
|
|
}
|
|
|
|
// 检查表单中的所有字段
|
|
const formData = new FormData(this);
|
|
console.log('表单数据:');
|
|
for (let [key, value] of formData.entries()) {
|
|
console.log(key + ': "' + value + '"');
|
|
}
|
|
|
|
// 提交表单
|
|
this.submit();
|
|
});
|
|
|
|
// 输入框输入时清除错误状态
|
|
document.getElementById('login-username').addEventListener('input', function() {
|
|
clearFieldError('login-username-group');
|
|
});
|
|
|
|
document.getElementById('login-password').addEventListener('input', function() {
|
|
clearFieldError('login-password-group');
|
|
});
|
|
|
|
<?php if (requireTermsAgreement()): ?>
|
|
document.getElementById('login-agree').addEventListener('change', function() {
|
|
clearFieldError('login-terms-group');
|
|
});
|
|
<?php endif; ?>
|
|
|
|
// 注册表单验证
|
|
document.getElementById('register-form').addEventListener('submit', function(e) {
|
|
const username = document.getElementById('register-username').value.trim();
|
|
const email = document.getElementById('register-email').value.trim();
|
|
const password = document.getElementById('register-password').value.trim();
|
|
const agreeTerms = document.getElementById('register-agree');
|
|
|
|
// 清除所有错误状态
|
|
clearFieldError('register-username-group');
|
|
clearFieldError('register-email-group');
|
|
clearFieldError('register-password-group');
|
|
<?php if (requireTermsAgreement()): ?>
|
|
clearFieldError('register-terms-group');
|
|
<?php endif; ?>
|
|
|
|
let hasError = false;
|
|
|
|
if (!username) {
|
|
showFieldError('register-username', 'register-username-group');
|
|
hasError = true;
|
|
}
|
|
|
|
if (!email) {
|
|
showFieldError('register-email', 'register-email-group');
|
|
hasError = true;
|
|
} else {
|
|
// 简单的邮箱格式验证
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
if (!emailRegex.test(email)) {
|
|
document.getElementById('register-email-group').classList.add('error');
|
|
document.getElementById('register-email-error').textContent = '请输入有效的邮箱地址';
|
|
document.getElementById('register-email').focus();
|
|
hasError = true;
|
|
}
|
|
}
|
|
|
|
if (!password) {
|
|
showFieldError('register-password', 'register-password-group');
|
|
hasError = true;
|
|
} else if (password.length < 6) {
|
|
document.getElementById('register-password-group').classList.add('error');
|
|
document.getElementById('register-password-error').textContent = '密码长度不能少于6位';
|
|
document.getElementById('register-password').focus();
|
|
hasError = true;
|
|
}
|
|
|
|
<?php if (requireTermsAgreement()): ?>
|
|
if (!agreeTerms.checked) {
|
|
showFieldError('register-agree', 'register-terms-group');
|
|
hasError = true;
|
|
}
|
|
<?php endif; ?>
|
|
|
|
if (hasError) {
|
|
e.preventDefault();
|
|
showAlert('请填写所有必填项');
|
|
}
|
|
});
|
|
|
|
// 输入框输入时清除错误状态
|
|
document.getElementById('register-username').addEventListener('input', function() {
|
|
clearFieldError('register-username-group');
|
|
});
|
|
|
|
document.getElementById('register-email').addEventListener('input', function() {
|
|
clearFieldError('register-email-group');
|
|
document.getElementById('register-email-error').textContent = '请输入邮箱';
|
|
});
|
|
|
|
document.getElementById('register-password').addEventListener('input', function() {
|
|
clearFieldError('register-password-group');
|
|
document.getElementById('register-password-error').textContent = '请输入密码(至少6位)';
|
|
});
|
|
|
|
<?php if (requireTermsAgreement()): ?>
|
|
document.getElementById('register-agree').addEventListener('change', function() {
|
|
clearFieldError('register-terms-group');
|
|
});
|
|
<?php endif; ?>
|
|
</script>
|
|
<script src="script.js"></script>
|
|
|
|
<!-- 主菜单 - 移动到body直接子元素中 -->
|
|
<div class="dropdown-menu" id="main-menu">
|
|
<ul>
|
|
<li id="menu-create-group">
|
|
<i class="fas fa-users"></i>
|
|
<span>创建群组</span>
|
|
</li>
|
|
<li id="menu-join-group">
|
|
<i class="fas fa-sign-in-alt"></i>
|
|
<span>加入群组</span>
|
|
</li>
|
|
<li id="menu-settings">
|
|
<i class="fas fa-cog"></i>
|
|
<span>设置</span>
|
|
</li>
|
|
<li id="menu-go-docs">
|
|
<i class="fas fa-book"></i>
|
|
<span>跳转至文档</span>
|
|
</li>
|
|
<li class="divider"></li>
|
|
<li id="menu-logout">
|
|
<i class="fas fa-sign-out-alt"></i>
|
|
<span>登出</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</body>
|
|
</html>
|