文件
2026-06-04 22:42:27 +08:00

6868 行
288 KiB
JavaScript

// 初始化邮箱绑定功能
function initEmailBind() {
// 获取绑定邮箱按钮
const bindEmailBtn = document.getElementById('bind-email-btn');
// 获取绑定邮箱模态框
const bindEmailModal = document.getElementById('bind-email-modal');
// 获取关闭按钮
const closeBindEmailModal = document.getElementById('close-bind-email-modal');
const cancelBindEmail = document.getElementById('cancel-bind-email');
// 获取确认按钮
const confirmBindEmail = document.getElementById('confirm-bind-email');
// 获取邮箱输入框
const bindEmailInput = document.getElementById('bind-email-input');
// 获取结果显示区域
const bindEmailResult = document.getElementById('bind-email-result');
// 打开绑定邮箱模态框
if (bindEmailBtn) {
bindEmailBtn.addEventListener('click', function() {
bindEmailModal.classList.add('active');
bindEmailInput.value = '';
bindEmailResult.innerHTML = '';
});
}
// 关闭绑定邮箱模态框
function closeModal() {
bindEmailModal.classList.remove('active');
}
if (closeBindEmailModal) {
closeBindEmailModal.addEventListener('click', closeModal);
}
if (cancelBindEmail) {
cancelBindEmail.addEventListener('click', closeModal);
}
// 点击模态框外部关闭
if (bindEmailModal) {
bindEmailModal.addEventListener('click', function(e) {
if (e.target === bindEmailModal) {
closeModal();
}
});
}
// 确认绑定邮箱
if (confirmBindEmail) {
confirmBindEmail.addEventListener('click', function() {
const email = bindEmailInput.value.trim();
if (!email) {
bindEmailResult.innerHTML = '<div class="error">请输入邮箱</div>';
return;
}
// 发送绑定邮箱请求
fetch('api.php?action=updateEmail', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'email=' + encodeURIComponent(email)
})
.then(response => response.json())
.then(data => {
if (data.success) {
bindEmailResult.innerHTML = '<div style="color: #07c160; padding: 10px; background: #e8f5e8; border-radius: 5px;">邮箱绑定成功!请检查您的邮箱进行验证。</div>';
// 2秒后关闭模态框并刷新页面
setTimeout(() => {
closeModal();
location.reload();
}, 2000);
} else {
bindEmailResult.innerHTML = '<div class="error">' + data.message + '</div>';
}
})
.catch(error => {
bindEmailResult.innerHTML = '<div class="error">绑定邮箱失败,请稍后重试</div>';
});
});
}
}
// DOM加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
// 标签切换逻辑
const tabBtns = document.querySelectorAll('.tab-btn');
const tabContents = document.querySelectorAll('.tab-content');
if (tabBtns.length > 0) {
tabBtns.forEach(btn => {
btn.addEventListener('click', function() {
const targetTab = this.getAttribute('data-tab');
// 移除所有active类
tabBtns.forEach(b => b.classList.remove('active'));
tabContents.forEach(c => c.classList.remove('active'));
// 添加当前active类
this.classList.add('active');
// 尝试直接匹配ID
let targetElement = document.getElementById(targetTab);
// 如果没找到,尝试匹配带-tab后缀的ID(用于聊天应用标签)
if (!targetElement) {
targetElement = document.getElementById(targetTab + '-tab');
}
if (targetElement) {
targetElement.classList.add('active');
}
});
});
}
// 主聊天界面功能
const chatApp = document.querySelector('.chat-app');
if (chatApp) {
initChatApp();
initEmailBind();
}
});
// 初始化聊天应用功能
function initChatApp() {
// 全局变量
// 当前选中的好友
let currentFriend = null;
// 当前选中的群组
let currentGroup = null;
// 好友列表
let friends = [];
// 群组列表
let groups = [];
// 正在进行的请求跟踪
let ongoingRequests = {};
// 加载状态计数器
let loadingCount = 0;
// 消息状态缓存
let messageStatusCache = {};
// 设备检测
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
// 创建加载状态元素
function createLoadingElement() {
let loadingElement = document.getElementById('loading-indicator');
if (!loadingElement) {
loadingElement = document.createElement('div');
loadingElement.id = 'loading-indicator';
loadingElement.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 12px 20px;
border-radius: 20px;
font-size: 14px;
z-index: 9999;
display: none;
align-items: center;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
`;
loadingElement.innerHTML = `
<i class="fas fa-spinner fa-spin mr-2"></i>
<span>正在加载...</span>
`;
document.body.appendChild(loadingElement);
}
return loadingElement;
}
// 显示加载状态
function showLoading() {
const loadingElement = createLoadingElement();
loadingCount++;
if (loadingCount > 0) {
loadingElement.style.display = 'flex';
}
}
// 隐藏加载状态
function hideLoading() {
const loadingElement = createLoadingElement();
loadingCount--;
if (loadingCount <= 0) {
loadingCount = 0;
loadingElement.style.display = 'none';
}
}
// 手机端侧边栏切换功能
const toggleSidebarBtn = document.getElementById('toggle-sidebar-btn');
const sidebar = document.querySelector('.sidebar');
const sidebarBackdrop = document.getElementById('sidebar-backdrop');
if (toggleSidebarBtn && sidebar && sidebarBackdrop) {
toggleSidebarBtn.addEventListener('click', function(e) {
e.stopPropagation();
sidebar.classList.toggle('active');
sidebarBackdrop.classList.toggle('active');
});
// 点击背景遮罩关闭侧边栏
sidebarBackdrop.addEventListener('click', function() {
sidebar.classList.remove('active');
sidebarBackdrop.classList.remove('active');
});
// 点击侧边栏外部关闭侧边栏
document.addEventListener('click', function(e) {
if (sidebar.classList.contains('active') &&
!sidebar.contains(e.target) &&
!toggleSidebarBtn.contains(e.target) &&
!sidebarBackdrop.contains(e.target)) {
sidebar.classList.remove('active');
sidebarBackdrop.classList.remove('active');
}
});
}
// 添加好友模态框
const addFriendModal = document.getElementById('add-friend-modal');
const addFriendBtn = document.getElementById('add-friend-btn');
const closeModalBtn = document.getElementById('close-modal');
const cancelAddFriendBtn = document.getElementById('cancel-add-friend');
const confirmAddFriendBtn = document.getElementById('confirm-add-friend');
const addFriendUsername = document.getElementById('add-friend-username');
const addFriendResult = document.getElementById('add-friend-result');
// 图片上传相关
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'image/*';
fileInput.style.display = 'none';
document.body.appendChild(fileInput);
// 图片预览容器
const imagePreviewModal = document.createElement('div');
imagePreviewModal.className = 'modal';
imagePreviewModal.innerHTML = `
<div class="modal-content" style="max-width: 500px;">
<div class="modal-header">
<h3>预览图片</h3>
<button class="close-btn" id="close-image-preview">&times;</button>
</div>
<div class="modal-body" style="text-align: center;">
<img id="preview-image" src="" alt="预览图片" style="max-width: 100%; max-height: 300px;">
</div>
<div class="modal-footer">
<button class="btn btn-cancel" id="cancel-image-send">取消</button>
<button class="btn btn-primary" id="send-image-btn">发送</button>
</div>
</div>
`;
document.body.appendChild(imagePreviewModal);
// 图片预览相关元素
const previewImage = document.getElementById('preview-image');
const closeImagePreview = document.getElementById('close-image-preview');
const cancelImageSend = document.getElementById('cancel-image-send');
const sendImageBtn = document.getElementById('send-image-btn');
let currentImageFile = null;
// Emoji选择器
const emojiSelector = document.createElement('div');
emojiSelector.className = 'emoji-selector';
emojiSelector.innerHTML = `
<div class="emoji-header">
<h4>表情</h4>
<button class="close-emoji">&times;</button>
</div>
<div class="emoji-categories">
<button class="emoji-category active" data-category="face">😀</button>
<button class="emoji-category" data-category="hand">🤲</button>
<button class="emoji-category" data-category="heart">❤️</button>
<button class="emoji-category" data-category="object">🚀</button>
</div>
<div class="emoji-content" id="emoji-content">
<!-- Emoji will be generated here -->
</div>
`;
document.body.appendChild(emojiSelector);
// Emoji数据
const emojis = {
face: ['😀', '😃', '😄', '😁', '😆', '😅', '😂', '🤣', '😊', '😇', '🙂', '🙃', '😉', '😌', '😍', '🥰', '😘', '😗', '😙', '😚', '😋', '😛', '😝', '😜', '🤪', '🤨', '🧐', '🤓', '😎', '🤩', '🥳', '😏', '😒', '😞', '😔', '😟', '😕', '🙁', '☹️', '😣', '😖', '😫', '😩', '🥺', '😢', '😭', '😤', '😠', '😡', '🤬', '😳', '🥵', '🥶', '😱', '😨', '😰', '😥', '😓', '🤗', '🤔', '🤭', '🤫', '🤥', '😶', '😐', '😑', '😬', '🙄', '😯', '😦', '😧', '😮', '😲', '🥱', '😴', '🤤', '😪', '😵', '🤐', '🥴', '🤢', '🤮', '🤧', '😷', '🤒', '🤕', '🤑'],
hand: ['🤲', '🤝', '🙏', '👍', '👎', '👌', '✌️', '🤞', '✊', '✋', '🤚', '🖐️', '🖖', '🤟', '🤘', '🤙', '👈', '👉', '👆', '👇', '☝️', '✋', '🤚', '🖐️', '🖖', '🤟', '🤘', '🤙', '👈', '👉', '👆', '👇', '☝️'],
heart: ['❤️', '🧡', '💛', '💚', '💙', '💜', '🖤', '💔', '💓', '💗', '💖', '💘', '💝', '💟', '💞', '💕', '💌', '💋', '💍', '💎'],
object: ['🚀', '🚗', '🚕', '🚙', '🚌', '🚎', '🏎️', '🚓', '🚑', '🚒', '🚐', '🚚', '🚛', '🚜', '🏍️', '🛵', '🚲', '🛴', '🛹', '🏂', '🚄', '🚅', '🚆', '🚇', '🚈', '🚉', '🚊', '🚝', '🚞', '🚟', '🚠', '🚡', '🚢', '✈️', '💺', '🚁', '🚟', '🚠', '🚡', '🚢', '✈️', '💺', '🚁']
};
// Emoji选择器相关元素
const closeEmojiBtn = document.querySelector('.close-emoji');
const emojiCategories = document.querySelectorAll('.emoji-category');
const emojiContent = document.getElementById('emoji-content');
let currentEmojiCategory = 'face';
let emojiToolBtn; // 声明为全局变量
// 为附件选择按钮添加点击事件(如果元素存在)
function bindToolButtonEvents() {
emojiToolBtn = document.querySelector('.tool-btn:nth-child(1)');
const imageToolBtn = document.querySelector('.tool-btn:nth-child(2)');
const attachmentToolBtn = document.querySelector('.tool-btn:nth-child(3)');
// 清除已有的事件监听器
if (emojiToolBtn) {
// 移除旧的事件监听器
const newEmojiBtn = emojiToolBtn.cloneNode(true);
emojiToolBtn.parentNode.replaceChild(newEmojiBtn, emojiToolBtn);
// 添加新的事件监听器
newEmojiBtn.addEventListener('click', function() {
if (!currentFriend && !currentGroup) {
showNotification('请先选择一个好友或群组', 'warning');
return;
}
// 显示/隐藏emoji选择器
emojiSelector.classList.toggle('active');
// 生成emoji
generateEmojis('face');
});
}
if (imageToolBtn) {
// 移除旧的事件监听器
const newImageBtn = imageToolBtn.cloneNode(true);
imageToolBtn.parentNode.replaceChild(newImageBtn, imageToolBtn);
// 添加新的事件监听器
newImageBtn.addEventListener('click', function() {
if (!currentFriend && !currentGroup) {
showNotification('请先选择一个好友或群组', 'warning');
return;
}
fileInput.click();
});
}
if (attachmentToolBtn) {
// 移除旧的事件监听器
const newAttachmentBtn = attachmentToolBtn.cloneNode(true);
attachmentToolBtn.parentNode.replaceChild(newAttachmentBtn, attachmentToolBtn);
// 添加新的事件监听器
newAttachmentBtn.addEventListener('click', function() {
if (!currentFriend && !currentGroup) {
showNotification('请先选择一个好友或群组', 'warning');
return;
}
// 创建附件输入框
const attachmentInput = document.createElement('input');
attachmentInput.type = 'file';
attachmentInput.accept = '.zip,.rar,.7z';
attachmentInput.style.display = 'none';
document.body.appendChild(attachmentInput);
attachmentInput.addEventListener('change', function(e) {
if (e.target.files.length > 0) {
const file = e.target.files[0];
uploadAttachment(file);
}
document.body.removeChild(attachmentInput);
});
attachmentInput.click();
});
}
}
// 上传附件
function uploadAttachment(file) {
const targetId = currentGroup ? currentGroup.id : currentFriend.id;
const isGroup = !!currentGroup;
const formData = new FormData();
formData.append('targetId', targetId);
formData.append('isGroup', isGroup);
formData.append('attachment', file);
fetch('api.php?action=sendAttachment', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('附件发送成功', 'success');
// 重新加载聊天历史
if (currentFriend) {
loadChatHistory(currentFriend.id, true);
} else if (currentGroup) {
loadChatHistory(currentGroup.id, true, false, true);
}
} else {
showNotification('附件发送失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('上传附件失败:', error);
showNotification('附件上传失败', 'error');
});
}
// 为Emoji分类按钮添加点击事件(如果元素存在)
if (emojiCategories.length > 0) {
emojiCategories.forEach(btn => {
btn.addEventListener('click', function() {
const category = this.getAttribute('data-category');
generateEmojis(category);
});
});
}
// 为关闭Emoji按钮添加点击事件(如果元素存在)
if (closeEmojiBtn) {
closeEmojiBtn.addEventListener('click', function() {
emojiSelector.classList.remove('active');
});
}
// 为发送按钮添加点击事件(如果元素存在)
const sendBtn = document.getElementById('send-btn');
if (sendBtn) {
}
// 为消息输入框添加键盘事件(如果元素存在)
const messageInput = document.getElementById('message-input');
if (messageInput) {
messageInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
// 阻止默认行为,避免页面刷新
e.preventDefault();
}
});
}
// 为搜索好友输入框添加输入事件(如果元素存在)
const searchFriendsInput = document.getElementById('search-friends');
if (searchFriendsInput) {
searchFriendsInput.addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
const friendItems = document.querySelectorAll('.friend-item');
friendItems.forEach(item => {
const friendName = item.querySelector('.friend-name').textContent.toLowerCase();
if (friendName.includes(searchTerm)) {
item.style.display = 'flex';
} else {
item.style.display = 'none';
}
});
});
}
// 打开添加好友模态框
if (addFriendBtn) {
addFriendBtn.addEventListener('click', function() {
addFriendModal.classList.add('active');
addFriendUsername.value = '';
addFriendResult.innerHTML = '';
});
}
// 关闭添加好友模态框
function closeAddFriendModal() {
addFriendModal.classList.remove('active');
}
if (closeModalBtn) {
closeModalBtn.addEventListener('click', closeAddFriendModal);
}
if (cancelAddFriendBtn) {
cancelAddFriendBtn.addEventListener('click', closeAddFriendModal);
}
// 点击模态框外部关闭
window.addEventListener('click', function(e) {
if (e.target === addFriendModal) {
closeAddFriendModal();
}
});
// 确认添加好友
if (confirmAddFriendBtn) {
confirmAddFriendBtn.addEventListener('click', function() {
const username = addFriendUsername.value.trim();
if (!username) {
addFriendResult.innerHTML = '<div class="error">请输入好友用户名</div>';
return;
}
// 发送添加好友请求
fetch('api.php?action=addFriend', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'username=' + encodeURIComponent(username)
})
.then(response => response.json())
.then(data => {
if (data.success) {
addFriendResult.innerHTML = '<div style="color: #07c160; padding: 10px; background: #e8f5e8; border-radius: 5px;">好友添加成功!</div>';
// 重新加载好友列表
loadFriendsList();
} else {
addFriendResult.innerHTML = '<div class="error">' + data.message + '</div>';
}
})
.catch(error => {
addFriendResult.innerHTML = '<div class="error">添加好友失败,请稍后重试</div>';
});
});
}
// 加载好友列表
function loadFriendsList() {
showLoading();
fetch('api.php?action=getFriends?' + Date.now()) // 添加时间戳避免缓存
.then(response => response.json())
.then(data => {
const friendsList = document.getElementById('friends-list');
friendsList.innerHTML = '';
// 更新全局好友列表
friends = data.friends || [];
// 调试:检查API返回的数据
console.log('=== 好友列表API调试 ===');
console.log('原始响应:', data);
if (friends.length > 0) {
console.log('第一个好友数据:', friends[0]);
console.log('第一个好友的note字段:', friends[0].note);
}
if (friends && friends.length > 0) {
friends.forEach(friend => {
const friendItem = document.createElement('div');
friendItem.className = 'friend-item';
friendItem.dataset.friendId = friend.id;
// 构建头像HTML
let avatarHtml = '';
if (friend.avatar) {
avatarHtml = `<img src="${friend.avatar}" alt="头像" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover;">`;
} else {
avatarHtml = '<i class="fas fa-user-circle"></i>';
}
// 显示名称:如果有备注则显示备注,否则显示用户名
const displayName = friend.note || friend.username;
friendItem.innerHTML = `
<div class="avatar">
${avatarHtml}
${friend.online ? '<div class="online-indicator"></div>' : ''}
</div>
<div class="friend-info">
<div class="friend-name">
${displayName}
${friend.note ? `<span style="font-size: 11px; color: var(--text-tertiary); margin-left: 5px;">(${friend.username})</span>` : ''}
${friend.online ? '<span class="online-status-text">在线</span>' : ''}
</div>
<div class="friend-last-message">${friend.lastMessage || '暂无消息'}</div>
</div>
<div class="friend-actions">
<button class="action-btn delete-friend-btn" data-friend-id="${friend.id}">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="friend-time">${friend.lastMessageTime || ''}</div>
`;
// 为好友头像添加点击事件,跳转到个人主页
const friendAvatar = friendItem.querySelector('.avatar');
if (friendAvatar) {
friendAvatar.style.cursor = 'pointer';
friendAvatar.addEventListener('click', function(e) {
e.stopPropagation(); // 阻止事件冒泡,避免触发聊天
window.location.href = `profile.php?name=${encodeURIComponent(friend.username)}`;
});
}
// 点击好友开始聊天
friendItem.addEventListener('click', function(e) {
// 如果点击的是删除按钮或头像,不触发聊天
if (e.target.closest('.delete-friend-btn') || e.target.closest('.avatar')) {
return;
}
// 移除所有active类
document.querySelectorAll('.friend-item').forEach(item => {
item.classList.remove('active');
});
// 添加当前active类
this.classList.add('active');
// 关闭手机端侧边栏和背景遮罩
const sidebar = document.querySelector('.sidebar');
const sidebarBackdrop = document.getElementById('sidebar-backdrop');
if (sidebar) {
sidebar.classList.remove('active');
}
if (sidebarBackdrop) {
sidebarBackdrop.classList.remove('active');
}
// 设置当前聊天好友
currentFriend = friend;
openChatWindow(friend);
});
// 删除好友按钮点击事件
const deleteFriendBtn = friendItem.querySelector('.delete-friend-btn');
deleteFriendBtn.addEventListener('click', function(e) {
e.stopPropagation(); // 阻止事件冒泡
const friendId = this.getAttribute('data-friend-id');
deleteFriend(friendId, friend.username);
});
friendsList.appendChild(friendItem);
});
} else {
friendsList.innerHTML = '<div style="text-align: center; padding: 20px; color: #999;">暂无好友,点击右上角添加好友</div>';
}
})
.catch(error => {
console.error('加载好友列表失败:', error);
})
.finally(() => {
hideLoading();
});
}
// 删除好友
function deleteFriend(friendId, friendUsername) {
if (confirm(`确定要删除好友 ${friendUsername} 吗?`)) {
fetch('api.php?action=removeFriend', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'friendId=' + encodeURIComponent(friendId)
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('好友已删除', 'success');
// 重新加载好友列表
loadFriendsList();
// 如果当前正在聊天的好友被删除,清空聊天窗口
if (currentFriend && currentFriend.id === friendId) {
currentFriend = null;
const chatContent = document.getElementById('chat-content');
const chatInputContainer = document.getElementById('chat-input-container');
chatContent.innerHTML = '<div class="empty-chat"><i class="fas fa-comments"></i><p>选择一个好友开始聊天</p></div>';
chatInputContainer.style.display = 'none';
}
} else {
showNotification('删除好友失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('删除好友失败:', error);
showNotification('删除好友失败', 'error');
});
}
}
// 打开聊天窗口
function openChatWindow(friend) {
const chatHeader = document.getElementById('chat-header');
const chatUsername = document.getElementById('chat-username');
const chatContent = document.getElementById('chat-content');
const chatInputContainer = document.getElementById('chat-input-container');
// 更新聊天头部信息
const displayName = friend.note || friend.username;
chatUsername.textContent = displayName;
if (friend.note) {
chatUsername.innerHTML = `${displayName} <span style="font-size: 12px; color: var(--text-tertiary);">(${friend.username})</span>`;
} else {
chatUsername.textContent = friend.username;
}
// 显示聊天输入框
chatInputContainer.style.display = 'block';
// 绑定工具按钮事件
bindToolButtonEvents();
// 清空聊天内容并加载历史消息
chatContent.innerHTML = '';
// 清空已处理链接集合,确保新聊天中的链接能被处理
processedLinks.clear();
loadChatHistory(friend.id, true);
}
// 聊天历史相关变量
let currentPage = 1;
let isLoading = false;
let hasMoreMessages = true;
let oldestMessageTimestamp = null;
// 加载聊天历史
function loadChatHistory(targetId, scrollToBottom = false, loadMore = false, isGroup = false) {
// 如果正在加载或没有更多消息,则不执行
if (isLoading || (loadMore && !hasMoreMessages)) return;
showLoading();
isLoading = true;
// 构建请求参数
const params = new URLSearchParams({
targetId: targetId,
page: loadMore ? currentPage + 1 : 1,
limit: 20,
isGroup: isGroup
});
// 如果是加载更多,添加时间戳参数
if (loadMore && oldestMessageTimestamp) {
params.append('olderThan', oldestMessageTimestamp);
}
fetch(`api.php?action=getChatHistory&${params.toString()}`)
.then(response => response.json())
.then(data => {
const chatContent = document.getElementById('chat-content');
// 保存当前滚动位置和高度
let currentScrollTop = 0;
let currentScrollHeight = 0;
let viewportHeight = 0;
if (loadMore) {
currentScrollTop = chatContent.scrollTop;
currentScrollHeight = chatContent.scrollHeight;
viewportHeight = chatContent.clientHeight;
}
// 如果不是加载更多,则清空聊天内容
if (!loadMore) {
chatContent.innerHTML = '';
currentPage = 1;
hasMoreMessages = true;
oldestMessageTimestamp = null;
// 清空已处理链接集合,确保重新加载的消息中的链接能被处理
processedLinks.clear();
}
if (data.messages && data.messages.length > 0) {
// 保存最早消息的时间戳
oldestMessageTimestamp = data.messages[0].timestamp;
// 记录添加新消息前的子元素数量
const beforeMessageCount = chatContent.children.length;
// 收集需要标记为已送达的发送者ID
const senderIdsToMark = new Set();
// 添加消息到聊天界面
data.messages.forEach(message => {
addMessageToChat(message, loadMore, false, isGroup);
// 标记接收到的消息为已送达
if (message.sender === 'other' && message.sender_id) {
senderIdsToMark.add(message.sender_id);
}
// 检查是否是Parlz官方发送的消息,并且不是"正在处理..."的消息
if (message.sender_id == '100000' && message.content !== '正在处理您的消息,请稍候...') {
// 如果是Parlz官方发送的实际回复,将等待标志位设置为false
window.isWaitingForAIReply = false;
}
});
// 对每个发送者标记消息为已送达
senderIdsToMark.forEach(senderId => {
markMessagesAsDelivered(senderId);
});
// 更新当前页码
if (loadMore) {
currentPage++;
}
// 更新是否有更多消息
hasMoreMessages = data.hasMore;
// 计算新添加的消息高度
if (loadMore) {
// 计算新添加消息后的滚动高度
const newScrollHeight = chatContent.scrollHeight;
const heightDiff = newScrollHeight - currentScrollHeight;
// 调整滚动位置,确保内容不跳动
setTimeout(() => {
// 保持相对于底部的滚动位置
// 计算当前看到的内容底部距离聊天区底部的距离
const distanceFromBottom = currentScrollHeight - currentScrollTop - viewportHeight;
// 新的滚动位置应该是新的总高度减去视口高度再减去这个距离
chatContent.scrollTop = chatContent.scrollHeight - viewportHeight - distanceFromBottom;
}, 0);
}
} else if (!loadMore) {
// 没有消息时显示提示
chatContent.innerHTML = '<div style="padding: 20px; text-align: center; color: #999;">暂无消息</div>';
}
// 根据需要滚动
if (scrollToBottom && !loadMore) {
// 使用setTimeout确保DOM更新后再滚动
setTimeout(() => {
// 确保真正滚动到底部
chatContent.scrollTop = chatContent.scrollHeight;
// 再次设置,确保滚动到底部
setTimeout(() => {
chatContent.scrollTop = chatContent.scrollHeight;
}, 100);
}, 0);
}
})
.catch(error => {
console.error('加载聊天历史失败:', error);
})
.finally(() => {
isLoading = false;
hideLoading();
});
}
// HTML转义函数
function escapeHtml(text) {
const map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text.replace(/[&<>'"]/g, function(m) { return map[m]; });
}
// Markdown解析函数
function parseMarkdown(text) {
// 先复制原始文本,不进行HTML转义,以便正确处理Markdown语法
let result = text;
// 处理标题 - 先处理标题再处理换行符
result = result.replace(/### (.*?)(\n|$)/gim, '<h3>$1</h3>');
result = result.replace(/## (.*?)(\n|$)/gim, '<h2>$1</h2>');
result = result.replace(/# (.*?)(\n|$)/gim, '<h1>$1</h1>');
// 处理换行
result = result.replace(/\n/g, '<br>');
// 处理粗体和斜体
result = result.replace(/\*\*(.*?)\*\*/gim, '<strong>$1</strong>');
result = result.replace(/\*(.*?)\*/gim, '<em>$1</em>');
result = result.replace(/__(.*?)__/gim, '<strong>$1</strong>');
result = result.replace(/_(.*?)_/gim, '<em>$1</em>');
// 处理代码块
result = result.replace(/```(.*?)```/gims, '<pre><code>$1</code></pre>');
result = result.replace(/`(.*?)`/gim, '<code>$1</code>');
// 处理列表
result = result.replace(/^\s*\* (.*$)/gim, '<ul><li>$1</li></ul>');
result = result.replace(/^\s*\d+\. (.*$)/gim, '<ol><li>$1</li></ol>');
// 处理引用
result = result.replace(/^> (.*$)/gim, '<blockquote>$1</blockquote>');
// 处理链接
result = result.replace(/\[(.*?)\]\((.*?)\)/gim, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
// 处理图片
result = result.replace(/!\[(.*?)\]\((.*?)\)/gim, '<img src="$2" alt="$1" style="max-width: 100%; height: auto; border-radius: 4px;">');
// 对HTML标签内的内容进行转义,防止XSS攻击
// 这里需要特别处理,只转义标签内的内容,不转义标签本身
result = result.replace(/(<[^>]*>)([^<]*)(<\/[^>]*>)/gim, function(match, startTag, content, endTag) {
return startTag + escapeHtml(content) + endTag;
});
return result;
}
// 格式化消息文本,处理链接和卡片
function formatMessageWithLinks(content) {
// 首先对消息内容进行HTML转义,防止XSS攻击
let formattedContent = escapeHtml(content);
// 处理艾特功能,将@用户名转换为高亮显示并可点击
const mentionRegex = /@([a-zA-Z0-9_]+)/g;
formattedContent = formattedContent.replace(mentionRegex, function(match, username) {
return '<a href="profile.php?name=' + encodeURIComponent(username) + '" class="mention" style="color: #007bff; font-weight: 500;">@' + username + '</a>';
});
// 正则表达式匹配 http 和 https 链接
const urlRegex = /https?:\/\/[^\s]+/g;
// 替换链接为可点击的 <a> 标签
formattedContent = formattedContent.replace(urlRegex, function(url) {
// 为所有http和https链接添加卡片解析标记
return '<a href="' + url + '" target="_blank" rel="noopener noreferrer" class="message-link" data-url="' + url + '">' + url + '</a>';
});
return formattedContent;
}
// 获取网页信息(使用OG标签)
function fetchPageInfo(url, callback) {
try {
// 设置5秒超时
const timeoutId = setTimeout(() => {
// 超时处理,使用URL作为标题
const urlObj = new URL(url);
callback({
title: urlObj.hostname,
favicon: urlObj.origin + '/favicon.ico',
description: '',
image: null,
site_name: urlObj.hostname
});
}, 5000);
// 使用服务器端API获取OG标签信息
fetch('api.php?action=getPageInfo&url=' + encodeURIComponent(url))
.then(response => {
clearTimeout(timeoutId);
return response.json();
})
.then(data => {
if (data.success && data.data) {
callback(data.data);
} else {
// 发生错误,使用URL作为标题
const urlObj = new URL(url);
callback({
title: urlObj.hostname,
favicon: urlObj.origin + '/favicon.ico',
description: '',
image: null,
site_name: urlObj.hostname
});
}
})
.catch(error => {
clearTimeout(timeoutId);
// 发生错误,使用URL作为标题
const urlObj = new URL(url);
callback({
title: urlObj.hostname,
favicon: urlObj.origin + '/favicon.ico',
description: '',
image: null,
site_name: urlObj.hostname
});
});
} catch (e) {
// 发生错误,使用URL作为标题
callback({
title: url,
favicon: null,
description: '',
image: null,
site_name: url
});
}
}
// 处理链接卡片解析
let processedLinks = new Set();
let linkProcessingLimit = 5;
let linksBeingProcessed = 0;
let linkObserver = null;
let linkQueue = [];
function processSingleLink(link, url) {
if (processedLinks.has(url)) {
return;
}
processedLinks.add(url);
linksBeingProcessed++;
const existingCard = link.nextElementSibling;
if (existingCard && existingCard.classList.contains('link-card')) {
linksBeingProcessed--;
return;
}
const cardContainer = document.createElement('div');
cardContainer.className = 'link-card';
cardContainer.style.cssText = "margin-top: 10px; padding: 15px; border: 1px solid #e0e0e0; border-radius: 8px; background: #f9f9f9; max-width: 400px; cursor: pointer; transition: all 0.2s ease;";
cardContainer.innerHTML = '<div style="display: flex; align-items: flex-start;"><div style="width: 48px; height: 48px; background: #e0e0e0; border-radius: 4px; display: flex; align-items: center; justify-content: center; margin-right: 12px;"><i class="fas fa-spinner fa-spin" style="color: #999;"></i></div><div style="flex: 1; overflow: hidden;"><div style="font-weight: 500; margin-bottom: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">加载中...</div><div style="font-size: 12px; color: #666; margin-bottom: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">' + url + '</div><div style="font-size: 12px; color: #999;">点击查看详情</div></div></div>';
cardContainer.addEventListener('click', function() {
window.open(url, '_blank', 'noopener noreferrer');
});
cardContainer.addEventListener('mouseenter', function() {
this.style.background = '#f0f0f0';
this.style.transform = 'translateY(-1px)';
});
cardContainer.addEventListener('mouseleave', function() {
this.style.background = '#f9f9f9';
this.style.transform = 'translateY(0)';
});
link.parentNode.insertBefore(cardContainer, link.nextSibling);
fetchPageInfo(url, function(info) {
try {
let cardHtml = '';
if (info.image) {
cardHtml = `
<div style="display: flex; align-items: flex-start;">
<div style="width: 80px; height: 60px; border-radius: 4px; overflow: hidden; margin-right: 12px; flex-shrink: 0;">
<img src="${info.image}" alt="${info.title}" style="width: 100%; height: 100%; object-fit: cover;">
</div>
<div style="flex: 1; overflow: hidden;">
<div style="font-weight: 500; margin-bottom: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: #000;">${info.title}</div>
${info.description ? `<div style="font-size: 12px; color: #666; margin-bottom: 5px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;">${info.description}</div>` : ''}
<div style="font-size: 12px; color: #999;">${info.site_name || new URL(url).hostname}</div>
</div>
</div>
`;
} else {
let faviconHtml = '';
if (info.favicon) {
faviconHtml = '<img src="' + info.favicon + '" alt="favicon" style="width: 100%; height: 100%; border-radius: 4px; object-fit: cover;" onerror="this.src=\'./web.png\'">';
} else {
faviconHtml = '<img src="./web.png" alt="web" style="width: 100%; height: 100%; border-radius: 4px; object-fit: cover;">';
}
cardHtml = `
<div style="display: flex; align-items: flex-start;">
<div style="width: 48px; height: 48px; background: #e0e0e0; border-radius: 4px; display: flex; align-items: center; justify-content: center; margin-right: 12px; overflow: hidden;">
${faviconHtml}
</div>
<div style="flex: 1; overflow: hidden;">
<div style="font-weight: 500; margin-bottom: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: #000;">${info.title}</div>
${info.description ? `<div style="font-size: 12px; color: #666; margin-bottom: 5px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;">${info.description}</div>` : ''}
<div style="font-size: 12px; color: #999;">${info.site_name || new URL(url).hostname}</div>
</div>
</div>
`;
}
cardContainer.innerHTML = cardHtml;
} catch (e) {
console.error('构建卡片失败:', e);
} finally {
linksBeingProcessed--;
processLinkQueue();
}
});
}
function processLinkQueue() {
while (linkQueue.length > 0 && linksBeingProcessed < linkProcessingLimit) {
const { link, url } = linkQueue.shift();
processSingleLink(link, url);
}
}
function processLinkCards() {
if (linkObserver) {
linkObserver.disconnect();
}
const links = document.querySelectorAll('.message-link[data-url^="http://"], .message-link[data-url^="https://"]');
// 直接处理所有可见的链接,不依赖 Intersection Observer
links.forEach(function(link) {
const url = link.getAttribute('data-url');
if (!url) return;
if (processedLinks.has(url)) {
return;
}
// 检查链接是否在视口中
const rect = link.getBoundingClientRect();
const chatContent = document.getElementById('chat-content');
const chatRect = chatContent.getBoundingClientRect();
// 检查链接是否在聊天区域内(包括100px的缓冲区)
const isVisible = (
rect.top >= chatRect.top - 100 &&
rect.left >= chatRect.left - 100 &&
rect.bottom <= chatRect.bottom + 100 &&
rect.right <= chatRect.right + 100
);
if (isVisible) {
if (linksBeingProcessed >= linkProcessingLimit) {
linkQueue.push({ link, url });
} else {
processSingleLink(link, url);
}
}
});
// 同时保留 Intersection Observer 来处理滚动时新出现的链接
linkObserver = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
const link = entry.target;
const url = link.getAttribute('data-url');
if (!url) return;
if (processedLinks.has(url)) {
linkObserver.unobserve(link);
return;
}
if (linksBeingProcessed >= linkProcessingLimit) {
linkQueue.push({ link, url });
} else {
processSingleLink(link, url);
}
linkObserver.unobserve(link);
}
});
}, {
root: document.getElementById('chat-content'),
rootMargin: '100px',
threshold: 0.1
});
links.forEach(function(link) {
if (!processedLinks.has(link.getAttribute('data-url'))) {
linkObserver.observe(link);
}
});
// 处理队列中的链接
processLinkQueue();
}
// 标记消息为已送达
function markMessagesAsDelivered(senderId) {
fetch(`api.php?action=markAsDelivered&senderId=${senderId}`)
.then(response => response.json())
.then(data => {
if (data.success) {
console.log('消息已标记为已送达');
}
})
.catch(error => {
console.error('标记消息为已送达失败:', error);
});
}
// 生成消息状态指示器
function getMessageStatusIndicator(message, isGroup = false) {
if (message.sender !== 'me') return '';
if (isGroup) return '';
let statusHtml = '';
if (message.delivered) {
statusHtml = '<i class="fas fa-check-circle" style="color: #22c55e;" title="已送达"></i>';
} else {
statusHtml = '<i class="fas fa-check" style="color: #9ca3af;" title="已发送"></i>';
}
return `<span class="message-status">${statusHtml}</span>`;
}
// 添加消息到聊天界面
function addMessageToChat(message, prepend = false, scrollToBottom = false, isGroup = false) {
const chatContent = document.getElementById('chat-content');
const messageDiv = document.createElement('div');
const isSent = message.sender === 'me';
messageDiv.className = `message ${isSent ? 'sent' : 'received'}`;
messageDiv.setAttribute('data-message-id', message.id);
// 添加右键菜单事件
messageDiv.addEventListener('contextmenu', function(e) {
e.preventDefault();
const contextMenu = createContextMenu();
// 显示右键菜单
contextMenu.style.left = e.clientX + 'px';
contextMenu.style.top = e.clientY + 'px';
contextMenu.style.display = 'block';
// 保存当前消息ID
contextMenu.setAttribute('data-message-id', message.id);
});
// 获取头像
let avatarHtml = '';
if (isSent) {
// 自己的头像
const sidebarAvatar = document.querySelector('.sidebar-header .avatar');
if (sidebarAvatar) {
avatarHtml = sidebarAvatar.innerHTML;
} else {
avatarHtml = '<i class="fas fa-user-circle"></i>';
}
} else {
// 好友或群成员的头像
if (isGroup) {
// 群成员的头像 - 使用消息中的发送者头像
if (message.sender_avatar) {
avatarHtml = `<img src="${message.sender_avatar}" alt="头像" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover;">`;
} else {
avatarHtml = '<i class="fas fa-user-circle"></i>';
}
} else {
// 好友的头像
if (currentFriend && currentFriend.avatar) {
avatarHtml = `<img src="${currentFriend.avatar}" alt="头像" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover;">`;
} else {
avatarHtml = '<i class="fas fa-user-circle"></i>';
}
}
}
// 检查消息是否被撤回
if (message.recalled) {
// 撤回的消息
messageDiv.innerHTML = `
<div class="message-avatar" style="cursor: pointer;">
${avatarHtml}
</div>
<div class="message-content">
<div class="message-text" style="color: #999; font-style: italic;">消息已撤回</div>
<div class="message-time">${message.time} ${getMessageStatusIndicator(message, isGroup)}</div>
${isSent ? `<button class="btn btn-primary btn-sm edit-recalled-btn" data-message-id="${message.id}" data-content="${encodeURIComponent(message.content)}">重新编辑</button>` : ''}
</div>
`;
// 为撤回消息中的头像添加点击事件
const messageAvatar = messageDiv.querySelector('.message-avatar');
if (messageAvatar && !isSent) {
messageAvatar.addEventListener('click', function() {
if (isGroup && message.sender_name) {
window.location.href = `profile.php?name=${encodeURIComponent(message.sender_name)}`;
} else if (currentFriend) {
window.location.href = `profile.php?name=${encodeURIComponent(currentFriend.username)}`;
}
});
// 添加长按事件,实现长按@对方
let longPressTimer;
messageAvatar.addEventListener('mousedown', function() {
longPressTimer = setTimeout(() => {
if (isGroup && message.sender_name) {
// 在群组中长按头像,直接@对方
if (messageInput) {
messageInput.value += '@' + message.sender_name + ' ';
messageInput.focus();
}
} else if (currentFriend) {
// 在私聊中长按头像,直接@对方
if (messageInput) {
messageInput.value += '@' + currentFriend.username + ' ';
messageInput.focus();
}
}
}, 500); // 500ms长按
});
messageAvatar.addEventListener('mouseup', function() {
clearTimeout(longPressTimer);
});
messageAvatar.addEventListener('mouseleave', function() {
clearTimeout(longPressTimer);
});
// 移动端触摸事件
messageAvatar.addEventListener('touchstart', function() {
longPressTimer = setTimeout(() => {
if (isGroup && message.sender_name) {
// 在群组中长按头像,直接@对方
if (messageInput) {
messageInput.value += '@' + message.sender_name + ' ';
messageInput.focus();
}
} else if (currentFriend) {
// 在私聊中长按头像,直接@对方
if (messageInput) {
messageInput.value += '@' + currentFriend.username + ' ';
messageInput.focus();
}
}
}, 500); // 500ms长按
});
messageAvatar.addEventListener('touchend', function() {
clearTimeout(longPressTimer);
});
}
} else if (message.is_image) {
// 图片消息
let messageContent = `
<div class="message-avatar" style="cursor: pointer;">
${avatarHtml}
</div>
<div class="message-content">
`;
// 在群组中显示发送者名称
if (isGroup) {
if (!isSent) {
// 其他群成员发送的消息
messageContent += `<div class="message-sender">${message.sender_name || '群成员'}</div>`;
} else {
// 自己发送的消息,显示自己的用户名
const currentUsername = document.querySelector('.sidebar-header .username');
const username = currentUsername ? currentUsername.textContent : '我';
messageContent += `<div class="message-sender">${username}</div>`;
}
}
// 添加引用消息
if (message.quote) {
messageContent += `
<div class="message-quote" ${message.quote.message_id ? 'data-message-id="' + message.quote.message_id + '"' : ''}>
<div class="quote-header">${message.quote.sender}</div>
<div class="quote-content">${message.quote.content}</div>
</div>
`;
}
messageContent += `
<div class="message-image">
<img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' fill='%23f0f0f0'/%3E%3Ctext x='50' y='55' font-family='Arial' font-size='14' text-anchor='middle' fill='%23999'%3E加载中...%3C/text%3E%3C/svg%3E" data-src="${message.content}" alt="图片消息" class="lazy-image" onclick="this.classList.toggle('expanded')">
</div>
<div class="message-time">${message.time} ${getMessageStatusIndicator(message, isGroup)}</div>
</div>
`;
messageDiv.innerHTML = messageContent;
// 为图片消息中的头像添加点击事件
const messageAvatar = messageDiv.querySelector('.message-avatar');
if (messageAvatar && !isSent) {
messageAvatar.addEventListener('click', function() {
if (isGroup && message.sender_name) {
window.location.href = `profile.php?name=${encodeURIComponent(message.sender_name)}`;
} else if (currentFriend) {
window.location.href = `profile.php?name=${encodeURIComponent(currentFriend.username)}`;
}
});
// 添加长按事件,实现长按@对方
let longPressTimer;
messageAvatar.addEventListener('mousedown', function() {
longPressTimer = setTimeout(() => {
if (isGroup && message.sender_name) {
// 在群组中长按头像,直接@对方
if (messageInput) {
messageInput.value += '@' + message.sender_name + ' ';
messageInput.focus();
}
} else if (currentFriend) {
// 在私聊中长按头像,直接@对方
if (messageInput) {
messageInput.value += '@' + currentFriend.username + ' ';
messageInput.focus();
}
}
}, 500); // 500ms长按
});
messageAvatar.addEventListener('mouseup', function() {
clearTimeout(longPressTimer);
});
messageAvatar.addEventListener('mouseleave', function() {
clearTimeout(longPressTimer);
});
// 移动端触摸事件
messageAvatar.addEventListener('touchstart', function() {
longPressTimer = setTimeout(() => {
if (isGroup && message.sender_name) {
// 在群组中长按头像,直接@对方
if (messageInput) {
messageInput.value += '@' + message.sender_name + ' ';
messageInput.focus();
}
} else if (currentFriend) {
// 在私聊中长按头像,直接@对方
if (messageInput) {
messageInput.value += '@' + currentFriend.username + ' ';
messageInput.focus();
}
}
}, 500); // 500ms长按
});
messageAvatar.addEventListener('touchend', function() {
clearTimeout(longPressTimer);
});
}
} else if (message.is_attachment) {
// 附件消息
let messageContent = `
<div class="message-avatar" style="cursor: pointer;">
${avatarHtml}
</div>
<div class="message-content">
`;
// 在群组中显示发送者名称
if (isGroup) {
if (!isSent) {
// 其他群成员发送的消息
messageContent += `<div class="message-sender">${message.sender_name || '群成员'}</div>`;
} else {
// 自己发送的消息,显示自己的用户名
const currentUsername = document.querySelector('.sidebar-header .username');
const username = currentUsername ? currentUsername.textContent : '我';
messageContent += `<div class="message-sender">${username}</div>`;
}
}
// 添加引用消息
if (message.quote) {
messageContent += `
<div class="message-quote" ${message.quote.message_id ? 'data-message-id="' + message.quote.message_id + '"' : ''}>
<div class="quote-header">${message.quote.sender}</div>
<div class="quote-content">${message.quote.content}</div>
</div>
`;
}
// 获取文件名
const fileName = message.content.split('/').pop();
messageContent += `
<div class="message-attachment">
<div class="attachment-icon">
<i class="fas fa-file-archive"></i>
</div>
<div class="attachment-info">
<div class="attachment-name">${fileName}</div>
<a href="${message.content}" download="${fileName}" class="attachment-download">
<i class="fas fa-download"></i> 下载
</a>
</div>
</div>
<div class="message-time">${message.time} ${getMessageStatusIndicator(message, isGroup)}</div>
</div>
`;
messageDiv.innerHTML = messageContent;
// 为附件消息中的头像添加点击事件
const messageAvatar = messageDiv.querySelector('.message-avatar');
if (messageAvatar && !isSent) {
messageAvatar.addEventListener('click', function() {
if (isGroup && message.sender_name) {
window.location.href = `profile.php?name=${encodeURIComponent(message.sender_name)}`;
} else if (currentFriend) {
window.location.href = `profile.php?name=${encodeURIComponent(currentFriend.username)}`;
}
});
// 添加长按事件,实现长按@对方
let longPressTimer;
messageAvatar.addEventListener('mousedown', function() {
longPressTimer = setTimeout(() => {
if (isGroup && message.sender_name) {
if (messageInput) {
messageInput.value += '@' + message.sender_name + ' ';
messageInput.focus();
}
} else if (currentFriend) {
if (messageInput) {
messageInput.value += '@' + currentFriend.username + ' ';
messageInput.focus();
}
}
}, 500);
});
messageAvatar.addEventListener('mouseup', function() {
clearTimeout(longPressTimer);
});
messageAvatar.addEventListener('mouseleave', function() {
clearTimeout(longPressTimer);
});
messageAvatar.addEventListener('touchstart', function() {
longPressTimer = setTimeout(() => {
if (isGroup && message.sender_name) {
if (messageInput) {
messageInput.value += '@' + message.sender_name + ' ';
messageInput.focus();
}
} else if (currentFriend) {
if (messageInput) {
messageInput.value += '@' + currentFriend.username + ' ';
messageInput.focus();
}
}
}, 500);
});
messageAvatar.addEventListener('touchend', function() {
clearTimeout(longPressTimer);
});
}
} else {
// 文本消息
let messageContent = `
<div class="message-avatar" style="cursor: pointer;">
${avatarHtml}
</div>
<div class="message-content">
`;
// 在群组中显示发送者名称
if (isGroup) {
if (!isSent) {
// 其他群成员发送的消息
messageContent += `<div class="message-sender">${message.sender_name || '群成员'}</div>`;
} else {
// 自己发送的消息,显示自己的用户名
const currentUsername = document.querySelector('.sidebar-header .username');
const username = currentUsername ? currentUsername.textContent : '我';
messageContent += `<div class="message-sender">${username}</div>`;
}
}
// 添加引用消息
if (message.quote) {
messageContent += `
<div class="message-quote" ${message.quote.message_id ? 'data-message-id="' + message.quote.message_id + '"' : ''}>
<div class="quote-header">${message.quote.sender}</div>
<div class="quote-content">${message.quote.content}</div>
</div>
`;
}
// 检查是否是Parlz官方发送的消息
const isSystemMessage = (message.sender_id == '100000'); // 使用==运算符进行比较,兼容数字和字符串类型
// 格式化消息内容
let formattedContent;
if (isSystemMessage) {
// 对于Parlz官方发送的消息,使用Markdown解析
formattedContent = parseMarkdown(message.content);
} else {
// 对于其他消息,仍然使用原来的链接处理
formattedContent = formatMessageWithLinks(message.content);
}
messageContent += `
<div class="message-text">${formattedContent}</div>
<div class="message-time">${message.time} ${getMessageStatusIndicator(message, isGroup)}</div>
</div>
`;
messageDiv.innerHTML = messageContent;
// 为文本消息中的头像添加点击事件
const messageAvatar = messageDiv.querySelector('.message-avatar');
if (messageAvatar && !isSent) {
messageAvatar.addEventListener('click', function() {
if (isGroup && message.sender_name) {
window.location.href = `profile.php?name=${encodeURIComponent(message.sender_name)}`;
} else if (currentFriend) {
window.location.href = `profile.php?name=${encodeURIComponent(currentFriend.username)}`;
}
});
// 添加长按事件,实现长按@对方
let longPressTimer;
messageAvatar.addEventListener('mousedown', function() {
longPressTimer = setTimeout(() => {
if (isGroup && message.sender_name) {
// 在群组中长按头像,直接@对方
if (messageInput) {
messageInput.value += '@' + message.sender_name + ' ';
messageInput.focus();
}
} else if (currentFriend) {
// 在私聊中长按头像,直接@对方
if (messageInput) {
messageInput.value += '@' + currentFriend.username + ' ';
messageInput.focus();
}
}
}, 500); // 500ms长按
});
messageAvatar.addEventListener('mouseup', function() {
clearTimeout(longPressTimer);
});
messageAvatar.addEventListener('mouseleave', function() {
clearTimeout(longPressTimer);
});
// 移动端触摸事件
messageAvatar.addEventListener('touchstart', function() {
longPressTimer = setTimeout(() => {
if (isGroup && message.sender_name) {
// 在群组中长按头像,直接@对方
if (messageInput) {
messageInput.value += '@' + message.sender_name + ' ';
messageInput.focus();
}
} else if (currentFriend) {
// 在私聊中长按头像,直接@对方
if (messageInput) {
messageInput.value += '@' + currentFriend.username + ' ';
messageInput.focus();
}
}
}, 500); // 500ms长按
});
messageAvatar.addEventListener('touchend', function() {
clearTimeout(longPressTimer);
});
}
}
// 根据是否是加载更多来决定添加位置
if (prepend) {
// 加载更多时添加到顶部
chatContent.insertBefore(messageDiv, chatContent.firstChild);
} else {
// 正常加载时添加到底部
// 添加动画效果
messageDiv.style.opacity = '0';
messageDiv.style.transform = isSent ? 'translateX(20px)' : 'translateX(-20px)';
messageDiv.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
chatContent.appendChild(messageDiv);
// 触发动画
setTimeout(() => {
messageDiv.style.opacity = '1';
messageDiv.style.transform = 'translateX(0)';
}, 10);
// 只有在明确需要时才滚动到底部
if (scrollToBottom) {
// 使用setTimeout确保DOM更新后再滚动
setTimeout(() => {
// 确保真正滚动到底部
chatContent.scrollTop = chatContent.scrollHeight;
// 再次设置,确保滚动到底部
setTimeout(() => {
chatContent.scrollTop = chatContent.scrollHeight;
}, 100);
}, 0);
}
}
// 为引用消息添加点击事件,跳转到原消息
setTimeout(() => {
const quoteElements = messageDiv.querySelectorAll('.message-quote[data-message-id]');
quoteElements.forEach(quoteElement => {
quoteElement.style.cursor = 'pointer';
quoteElement.addEventListener('click', function() {
const messageId = this.getAttribute('data-message-id');
jumpToMessage(messageId);
});
});
// 为重新编辑按钮添加点击事件
const editRecalledBtn = messageDiv.querySelector('.edit-recalled-btn');
if (editRecalledBtn) {
editRecalledBtn.addEventListener('click', function() {
const messageId = this.getAttribute('data-message-id');
const content = decodeURIComponent(this.getAttribute('data-content'));
editRecalledMessage(content);
});
}
}, 0);
// 处理链接卡片
setTimeout(processLinkCards, 0);
}
// 跳转到指定消息
function jumpToMessage(messageId) {
const chatContent = document.getElementById('chat-content');
const targetMessage = chatContent.querySelector(`[data-message-id="${messageId}"]`);
if (targetMessage) {
// 滚动到目标消息
targetMessage.scrollIntoView({ behavior: 'smooth', block: 'center' });
// 高亮显示目标消息
targetMessage.style.backgroundColor = '#f0f9ff';
setTimeout(() => {
targetMessage.style.backgroundColor = '';
}, 2000);
} else {
// 如果消息不在当前视图中,尝试加载更多消息
loadMoreMessages(messageId);
}
}
// 加载更多消息以查找目标消息
function loadMoreMessages(messageId) {
// 这里可以实现加载更多消息的逻辑
// 当用户点击引用消息但目标消息不在当前视图中时,加载更多消息
showNotification('正在加载更多消息...', 'info');
// 尝试加载更多历史消息
if (currentGroup) {
loadChatHistory(currentGroup.id, true, true, true);
} else if (currentFriend) {
loadChatHistory(currentFriend.id, true, true, false);
}
}
// 重新编辑撤回的消息
function editRecalledMessage(content) {
// 将内容填充到输入框
messageInput.value = content;
// 聚焦输入框
messageInput.focus();
// 显示通知
showNotification('请编辑消息内容后重新发送', 'info');
}
// 发送消息
// 节流函数
function throttle(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// 主题切换功能
function initThemeToggle() {
const themeToggleBtns = document.querySelectorAll('.theme-toggle-btn');
// 初始化主题
function initTheme() {
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark' || (!savedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.body.classList.add('dark-mode');
updateThemeIcons(true);
} else {
document.body.classList.remove('dark-mode');
updateThemeIcons(false);
}
}
// 更新主题图标
function updateThemeIcons(isDark) {
themeToggleBtns.forEach(btn => {
const sunIcon = btn.querySelector('.sun-icon');
const moonIcon = btn.querySelector('.moon-icon');
if (sunIcon && moonIcon) {
sunIcon.style.display = isDark ? 'none' : 'inline';
moonIcon.style.display = isDark ? 'inline' : 'none';
}
});
}
// 切换主题
function toggleTheme() {
const isDark = document.body.classList.toggle('dark-mode');
localStorage.setItem('theme', isDark ? 'dark' : 'light');
updateThemeIcons(isDark);
}
// 为主题切换按钮添加点击事件
themeToggleBtns.forEach(btn => {
btn.addEventListener('click', toggleTheme);
});
// 初始化主题
initTheme();
}
// 图片懒加载函数
function lazyLoadImages() {
const lazyImages = document.querySelectorAll('.lazy-image');
lazyImages.forEach(img => {
// 检查图片是否已经加载
if (img.src === img.dataset.src) return;
// 检查图片是否在视口内(增加一些缓冲区)
const rect = img.getBoundingClientRect();
const isVisible = (
rect.top >= -100 &&
rect.left >= -100 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) + 100 &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth) + 100
);
// 如果图片在视口内,则加载
if (isVisible) {
img.src = img.dataset.src;
img.classList.remove('lazy-image');
}
});
}
// 节流处理后的图片懒加载
const throttledLazyLoad = throttle(lazyLoadImages, 200);
// 初始加载时检查可视区域内的图片
window.addEventListener('load', lazyLoadImages);
window.addEventListener('resize', throttledLazyLoad);
// 添加滚动事件监听,实现滚动加载更多历史消息
const chatContent = document.getElementById('chat-content');
if (chatContent) {
// 滚动加载更多历史消息的函数
function handleScroll() {
// 当滚动到距离顶部50px以内时,加载更多历史消息
// 只有当用户主动向上滚动时才触发
if (this.scrollTop < 50 && (currentFriend || currentGroup) && !isLoading && hasMoreMessages) {
const isGroup = !!currentGroup;
const targetId = isGroup ? currentGroup.id : currentFriend.id;
loadChatHistory(targetId, false, true, isGroup);
}
// 检查并加载可视区域内的图片
throttledLazyLoad();
}
// 使用节流函数优化滚动事件
const throttledScroll = throttle(handleScroll, 100);
chatContent.addEventListener('scroll', throttledScroll);
}
let isSending = false; // 防止重复发送消息的标志位
function sendMessage() {
// 防止重复发送消息
if (isSending) return;
// 检查是否正在等待AI助手的回复
const isSendingToSystem = (currentFriend && currentFriend.id === '100000');
if (isSendingToSystem && window.isWaitingForAIReply) {
showNotification('AI助手正在处理您的消息,请稍候...', 'info');
return;
}
const content = messageInput.value.trim();
if (!content || (!currentFriend && !currentGroup)) return;
// 检查是否是发送给Parlz官方
if (isSendingToSystem) {
// 发送给Parlz官方时,设置一个特殊的标志位
window.isWaitingForAIReply = true;
}
// 检查是否有引用消息
const quoteInputContainer = document.querySelector('.quote-input-container');
let quote = null;
if (quoteInputContainer) {
const quoteSender = quoteInputContainer.querySelector('.quote-header span').textContent;
const quoteContent = quoteInputContainer.querySelector('.quote-content').textContent;
const quoteMessageId = quoteInputContainer.getAttribute('data-message-id');
quote = {
sender: quoteSender,
content: quoteContent
};
// 添加原消息的id
if (quoteMessageId) {
quote.message_id = quoteMessageId;
}
}
// 确定是好友还是群组
const isGroup = !!currentGroup;
const targetId = isGroup ? currentGroup.id : currentFriend.id;
// 禁用发送按钮,避免重复发送
if (sendBtn) {
sendBtn.disabled = true;
sendBtn.textContent = '发送中...';
}
// 设置发送标志位
isSending = true;
// 显示发送中提示
showNotification('消息发送中...', 'info');
// 发送消息到服务器
fetch('api.php?action=sendMessage', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `targetId=${targetId}&content=${encodeURIComponent(content)}&quote=${encodeURIComponent(JSON.stringify(quote))}&isGroup=${isGroup}`
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 清空输入框并重置高度
messageInput.value = '';
messageInput.style.height = 'auto';
// 移除引用输入容器
const quoteInputContainer = document.querySelector('.quote-input-container');
if (quoteInputContainer) {
quoteInputContainer.remove();
}
// 显示消息已发送的反馈
showNotification('消息已发送,等待回复...', 'success');
// 延迟更新聊天历史,给系统回复一些时间
setTimeout(() => {
loadChatHistory(targetId, true, false, isGroup);
}, 2000);
// 更新列表
if (isGroup) {
loadGroupsList();
} else {
loadFriendsList();
}
// 清除消息状态缓存,避免自己发送的消息触发新消息提示
const cacheKey = `${targetId}_${isGroup ? 'group' : 'friend'}`;
messageStatusCache[cacheKey] = null;
} else {
// 显示发送失败的反馈
showNotification('发送消息失败', 'error');
}
})
.catch(error => {
console.error('发送消息失败:', error);
// 显示发送失败的反馈
showNotification('发送消息失败,请检查网络连接', 'error');
})
.finally(() => {
// 恢复发送按钮状态
if (sendBtn) {
sendBtn.disabled = false;
sendBtn.textContent = '发送';
}
// 重置发送标志位
isSending = false;
});
}
// 点击发送按钮发送消息
if (sendBtn) {
sendBtn.addEventListener('click', function(e) {
e.preventDefault(); // 阻止默认行为,避免页面刷新
sendMessage();
});
}
// 按下Enter键发送消息
if (messageInput) {
// 自动调整textarea高度
messageInput.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = Math.min(this.scrollHeight, 120) + 'px';
});
messageInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault(); // 阻止默认行为,避免换行
sendMessage();
}
});
}
// @mention功能
let mentionDropdown = null;
let currentMentionQuery = '';
// 创建提及下拉菜单
function createMentionDropdown() {
if (mentionDropdown) {
document.body.removeChild(mentionDropdown);
}
mentionDropdown = document.createElement('div');
mentionDropdown.className = 'mention-dropdown';
mentionDropdown.style.cssText = `
position: absolute;
background: white;
border: 1px solid #e0e0e0;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
max-height: 300px;
overflow-y: auto;
z-index: 10000;
min-width: 250px;
padding: 5px 0;
`;
document.body.appendChild(mentionDropdown);
}
// 隐藏提及下拉菜单
function hideMentionDropdown() {
if (mentionDropdown) {
mentionDropdown.style.display = 'none';
}
}
// 过滤用户列表
function filterUsers(query) {
// 获取当前聊天对象的所有用户
let users = [];
if (currentGroup) {
// 群聊:获取群成员
const groupMembers = groups.find(g => g.id == currentGroup.id)?.members || [];
users = groupMembers;
} else if (currentFriend) {
// 私聊:只有对方用户
users = [currentFriend];
}
// 获取当前用户信息
const currentUsernameElement = document.querySelector('.sidebar-header .username');
const currentUsername = currentUsernameElement ? currentUsernameElement.textContent : '我';
const currentAvatarElement = document.querySelector('.sidebar-header .avatar');
const currentAvatar = currentAvatarElement ? currentAvatarElement.src : 'uploads/avatars/default.png';
// 添加当前用户
users.push({ id: 'current', username: currentUsername, avatar: currentAvatar });
// 去重
const uniqueUsers = [];
const userIds = new Set();
users.forEach(user => {
if (!userIds.has(user.id)) {
userIds.add(user.id);
uniqueUsers.push(user);
}
});
// 过滤
if (!query) {
return uniqueUsers;
}
return uniqueUsers.filter(user =>
user.username.toLowerCase().includes(query.toLowerCase())
);
}
// 渲染用户列表
function renderUserList(users) {
if (!mentionDropdown) return;
mentionDropdown.innerHTML = '';
users.forEach(user => {
const userItem = document.createElement('div');
userItem.className = 'mention-user-item';
userItem.style.cssText = `
padding: 10px 15px;
cursor: pointer;
display: flex;
align-items: center;
transition: background-color 0.2s;
border-bottom: 1px solid #f0f0f0;
`;
userItem.innerHTML = `
<div style="width: 36px; height: 36px; border-radius: 50%; overflow: hidden; margin-right: 12px; flex-shrink: 0;">
<img src="${user.avatar || 'uploads/avatars/default.png'}" style="width: 100%; height: 100%; object-fit: cover;">
</div>
<span style="font-size: 14px; color: #333;">${user.username}</span>
`;
// 添加悬停效果
userItem.addEventListener('mouseenter', function() {
this.style.backgroundColor = '#f5f5f5';
});
userItem.addEventListener('mouseleave', function() {
this.style.backgroundColor = '';
});
userItem.addEventListener('click', function() {
insertMention(user.username);
hideMentionDropdown();
});
mentionDropdown.appendChild(userItem);
});
}
// 插入提及到输入框
function insertMention(username) {
const cursorPosition = messageInput.selectionStart;
const textBeforeCursor = messageInput.value.substring(0, cursorPosition);
const textAfterCursor = messageInput.value.substring(cursorPosition);
// 找到@符号的位置
const atSymbolIndex = textBeforeCursor.lastIndexOf('@');
if (atSymbolIndex === -1) return;
// 替换@及其后的内容为@用户名
const newText = textBeforeCursor.substring(0, atSymbolIndex) + '@' + username + ' ' + textAfterCursor;
messageInput.value = newText;
// 设置光标位置
const newCursorPosition = atSymbolIndex + username.length + 2; // @ + 用户名 + 空格
messageInput.setSelectionRange(newCursorPosition, newCursorPosition);
// 聚焦输入框
messageInput.focus();
}
// 处理输入事件
if (messageInput && !isMobile) {
messageInput.addEventListener('input', function(e) {
const cursorPosition = this.selectionStart;
const textBeforeCursor = this.value.substring(0, cursorPosition);
const textAfterCursor = this.value.substring(cursorPosition);
// 检查是否输入了@符号
const atSymbolIndex = textBeforeCursor.lastIndexOf('@');
if (atSymbolIndex === -1) {
hideMentionDropdown();
return;
}
// 检查@符号是否是一个新的提及(前面是空格或行首)
const charBeforeAt = atSymbolIndex > 0 ? textBeforeCursor[atSymbolIndex - 1] : '';
if (charBeforeAt && !/\s/.test(charBeforeAt)) {
hideMentionDropdown();
return;
}
// 获取@后面的文本
currentMentionQuery = textBeforeCursor.substring(atSymbolIndex + 1);
// 过滤用户
const filteredUsers = filterUsers(currentMentionQuery);
// 立即显示用户列表(即使没有输入过滤条件)
if (filteredUsers.length > 0) {
// 只在首次显示时计算位置,搜索时不改变位置
if (mentionDropdown.style.display === 'none') {
// 获取输入框的位置
const rect = this.getBoundingClientRect();
const position = {
left: rect.left,
top: rect.bottom + window.scrollY
};
// 显示下拉菜单
showMentionDropdown(position);
}
// 渲染用户列表
renderUserList(filteredUsers);
} else {
hideMentionDropdown();
}
});
}
// 点击其他地方隐藏下拉菜单
document.addEventListener('click', function(e) {
if (e.target !== messageInput && !mentionDropdown?.contains(e.target)) {
hideMentionDropdown();
}
});
// 键盘导航支持
let currentSelectedUserIndex = -1;
if (messageInput && !isMobile) {
messageInput.addEventListener('keydown', function(e) {
if (!mentionDropdown || mentionDropdown.style.display === 'none') {
return;
}
const userItems = mentionDropdown.querySelectorAll('.mention-user-item');
if (userItems.length === 0) {
return;
}
switch (e.key) {
case 'ArrowDown':
e.preventDefault();
currentSelectedUserIndex = (currentSelectedUserIndex + 1) % userItems.length;
updateUserSelection(userItems);
break;
case 'ArrowUp':
e.preventDefault();
currentSelectedUserIndex = (currentSelectedUserIndex - 1 + userItems.length) % userItems.length;
updateUserSelection(userItems);
break;
case 'Enter':
e.preventDefault();
// 如果没有选中任何用户,默认选择第一个
if (currentSelectedUserIndex < 0 || currentSelectedUserIndex >= userItems.length) {
currentSelectedUserIndex = 0;
}
if (currentSelectedUserIndex >= 0 && currentSelectedUserIndex < userItems.length) {
userItems[currentSelectedUserIndex].click();
}
break;
case 'Escape':
hideMentionDropdown();
currentSelectedUserIndex = -1;
break;
}
});
}
// 更新用户选择状态
function updateUserSelection(userItems) {
userItems.forEach((item, index) => {
if (index === currentSelectedUserIndex) {
item.style.backgroundColor = '#f0f9ff';
} else {
item.style.backgroundColor = '';
}
});
}
// 重置选择索引
function resetMentionSelection() {
currentSelectedUserIndex = -1;
}
// 监听提及下拉菜单的显示,重置选择索引
function showMentionDropdown(position) {
if (!mentionDropdown) {
createMentionDropdown();
}
// 计算下拉菜单的高度
const dropdownHeight = mentionDropdown.offsetHeight || 300;
// 向上显示
mentionDropdown.style.left = position.left + 'px';
mentionDropdown.style.top = (position.top - dropdownHeight - 30) + 'px'; // 30px 间距,确保离上面有足够空间
mentionDropdown.style.display = 'block';
// 重置选择索引并默认选择第一个
currentSelectedUserIndex = 0;
const userItems = mentionDropdown.querySelectorAll('.mention-user-item');
if (userItems.length > 0) {
updateUserSelection(userItems);
}
}
// 搜索好友
if (searchFriendsInput) {
searchFriendsInput.addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
const friendItems = document.querySelectorAll('.friend-item');
friendItems.forEach(item => {
const friendName = item.querySelector('.friend-name').textContent.toLowerCase();
if (friendName.includes(searchTerm)) {
item.style.display = 'flex';
} else {
item.style.display = 'none';
}
});
});
}
// 初始加载好友列表和群组列表
let loadedFriends = false;
let loadedGroups = false;
function checkIfBothLoaded() {
if (loadedFriends && loadedGroups) {
// 好友和群组列表都加载完成后,检查URL参数
checkUrlParams();
}
}
// 修改loadFriendsList函数,在实际完成后标记加载完成
const originalLoadFriendsList = loadFriendsList;
loadFriendsList = function() {
fetch('api.php?action=getFriends')
.then(response => response.json())
.then(data => {
const friendsList = document.getElementById('friends-list');
friendsList.innerHTML = '';
// 更新全局好友列表
friends = data.friends || [];
if (friends && friends.length > 0) {
friends.forEach(friend => {
const friendItem = document.createElement('div');
friendItem.className = 'friend-item';
friendItem.dataset.friendId = friend.id;
// 构建头像HTML
let avatarHtml = '';
if (friend.avatar) {
avatarHtml = `<img src="${friend.avatar}" alt="头像" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover;">`;
} else {
avatarHtml = '<i class="fas fa-user-circle"></i>';
}
// 显示名称:如果有备注则显示备注,否则显示用户名
const displayName = friend.note || friend.username;
friendItem.innerHTML = `
<div class="avatar">
${avatarHtml}
${friend.online ? '<div class="online-indicator"></div>' : ''}
</div>
<div class="friend-info">
<div class="friend-name">
${displayName}
${friend.note ? `<span style="font-size: 11px; color: var(--text-tertiary); margin-left: 5px;">(${friend.username})</span>` : ''}
${friend.online ? '<span class="online-status-text">在线</span>' : ''}
</div>
<div class="friend-last-message">${friend.lastMessage || '暂无消息'}</div>
</div>
<div class="friend-actions">
<button class="action-btn delete-friend-btn" data-friend-id="${friend.id}">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="friend-time">${friend.lastMessageTime || ''}</div>
`;
// 为好友头像添加点击事件,跳转到个人主页
const friendAvatar = friendItem.querySelector('.avatar');
if (friendAvatar) {
friendAvatar.style.cursor = 'pointer';
friendAvatar.addEventListener('click', function(e) {
e.stopPropagation(); // 阻止事件冒泡,避免触发聊天
window.location.href = `profile.php?name=${encodeURIComponent(friend.username)}`;
});
}
// 点击好友开始聊天
friendItem.addEventListener('click', function(e) {
// 如果点击的是删除按钮或头像,不触发聊天
if (e.target.closest('.delete-friend-btn') || e.target.closest('.avatar')) {
return;
}
// 移除所有active类
document.querySelectorAll('.friend-item').forEach(item => {
item.classList.remove('active');
});
// 添加当前active类
this.classList.add('active');
// 关闭手机端侧边栏和背景遮罩
const sidebar = document.querySelector('.sidebar');
const sidebarBackdrop = document.getElementById('sidebar-backdrop');
if (sidebar) {
sidebar.classList.remove('active');
}
if (sidebarBackdrop) {
sidebarBackdrop.classList.remove('active');
}
// 设置当前聊天好友
currentFriend = friend;
openChatWindow(friend);
});
// 删除好友按钮点击事件
const deleteFriendBtn = friendItem.querySelector('.delete-friend-btn');
deleteFriendBtn.addEventListener('click', function(e) {
e.stopPropagation(); // 阻止事件冒泡
const friendId = this.getAttribute('data-friend-id');
deleteFriend(friendId, friend.username);
});
friendsList.appendChild(friendItem);
});
} else {
friendsList.innerHTML = '<div style="text-align: center; padding: 20px; color: #999;">暂无好友,点击右上角添加好友</div>';
}
})
.catch(error => {
console.error('加载好友列表失败:', error);
})
.finally(() => {
hideLoading();
// 标记好友列表加载完成
loadedFriends = true;
checkIfBothLoaded();
});
};
// 修改loadGroupsList函数,在实际完成后标记加载完成
const originalLoadGroupsList = loadGroupsList;
loadGroupsList = function() {
fetch('api.php?action=getUserGroups')
.then(response => response.json())
.then(data => {
const groupsList = document.getElementById('groups-list');
groupsList.innerHTML = '';
// 更新全局群组列表
groups = data.groups || [];
if (groups && groups.length > 0) {
groups.forEach(group => {
const groupItem = document.createElement('div');
groupItem.className = 'group-item';
groupItem.dataset.groupId = group.id;
// 构建头像HTML
let avatarHtml = '';
if (group.avatar) {
avatarHtml = `<img src="${group.avatar}" alt="群组头像" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover;">`;
} else {
avatarHtml = '<i class="fas fa-users"></i>';
}
groupItem.innerHTML = `
<div class="avatar">
${avatarHtml}
</div>
<div class="group-info">
<div class="group-name">${group.name}</div>
<div class="group-last-message">${group.lastMessage || '暂无消息'}</div>
</div>
<div class="group-time">${group.lastMessageTime || ''}</div>
`;
// 点击群组开始聊天
groupItem.addEventListener('click', function() {
// 移除所有active类
document.querySelectorAll('.group-item').forEach(item => {
item.classList.remove('active');
});
// 添加当前active类
this.classList.add('active');
// 关闭手机端侧边栏和背景遮罩
const sidebar = document.querySelector('.sidebar');
const sidebarBackdrop = document.getElementById('sidebar-backdrop');
if (sidebar) {
sidebar.classList.remove('active');
}
if (sidebarBackdrop) {
sidebarBackdrop.classList.remove('active');
}
// 设置当前聊天群组
currentGroup = group;
openGroupChatWindow(group);
});
groupsList.appendChild(groupItem);
});
} else {
groupsList.innerHTML = '<div style="text-align: center; padding: 20px; color: #999;">暂无群组,点击右上角创建群组</div>';
}
})
.catch(error => {
console.error('加载群组列表失败:', error);
})
.finally(() => {
// 标记群组列表加载完成
loadedGroups = true;
checkIfBothLoaded();
});
};
// 初始加载好友列表和群组列表
loadFriendsList();
loadGroupsList();
// 为手机版生成列表
function generateMobileLists() {
// 复制好友列表到手机版
const friendsList = document.getElementById('friends-list');
const friendsListMobile = document.querySelector('.friends-list-mobile');
if (friendsList && friendsListMobile) {
friendsListMobile.innerHTML = '';
const friendItems = friendsList.querySelectorAll('.friend-item');
friendItems.forEach(item => {
const mobileItem = item.cloneNode(true);
// 重新添加点击事件
mobileItem.addEventListener('click', function(e) {
if (e.target.closest('.delete-friend-btn') || e.target.closest('.avatar')) {
return;
}
document.querySelectorAll('.friend-item, .group-item').forEach(i => i.classList.remove('active'));
this.classList.add('active');
const sidebar = document.querySelector('.sidebar');
const sidebarBackdrop = document.getElementById('sidebar-backdrop');
if (sidebar) sidebar.classList.remove('active');
if (sidebarBackdrop) sidebarBackdrop.classList.remove('active');
currentGroup = null;
const friendId = this.dataset.friendId;
const friend = friends.find(f => f.id == friendId);
if (friend) {
currentFriend = friend;
openChatWindow(friend);
}
});
// 重新添加删除按钮事件
const deleteBtn = mobileItem.querySelector('.delete-friend-btn');
if (deleteBtn) {
deleteBtn.addEventListener('click', function(e) {
e.stopPropagation();
const friendId = this.getAttribute('data-friend-id');
const friend = friends.find(f => f.id == friendId);
if (friend) {
deleteFriend(friendId, friend.username);
}
});
}
// 重新添加头像点击事件
const avatar = mobileItem.querySelector('.avatar');
if (avatar) {
avatar.addEventListener('click', function(e) {
e.stopPropagation();
const friendId = mobileItem.dataset.friendId;
const friend = friends.find(f => f.id == friendId);
if (friend) {
window.location.href = `profile.php?name=${encodeURIComponent(friend.username)}`;
}
});
}
friendsListMobile.appendChild(mobileItem);
});
}
// 复制群组列表到手机版
const groupsList = document.getElementById('groups-list');
const groupsListMobile = document.querySelector('.groups-list-mobile');
if (groupsList && groupsListMobile) {
groupsListMobile.innerHTML = '';
const groupItems = groupsList.querySelectorAll('.group-item');
groupItems.forEach(item => {
const mobileItem = item.cloneNode(true);
// 重新添加点击事件
mobileItem.addEventListener('click', function() {
document.querySelectorAll('.friend-item, .group-item').forEach(i => i.classList.remove('active'));
this.classList.add('active');
const sidebar = document.querySelector('.sidebar');
const sidebarBackdrop = document.getElementById('sidebar-backdrop');
if (sidebar) sidebar.classList.remove('active');
if (sidebarBackdrop) sidebarBackdrop.classList.remove('active');
currentFriend = null;
const groupId = this.dataset.groupId;
const group = groups.find(g => g.id == groupId);
if (group) {
currentGroup = group;
openGroupChatWindow(group);
}
});
groupsListMobile.appendChild(mobileItem);
});
}
}
// 当列表加载完成后生成手机版列表
setTimeout(generateMobileLists, 1000);
// 设置好友列表和群组列表默认显示
const friendsList = document.getElementById('friends-list');
const groupsList = document.getElementById('groups-list');
if (friendsList) {
friendsList.style.display = 'block';
}
if (groupsList) {
groupsList.style.display = 'block';
}
// 加载群组列表
function loadGroupsList() {
fetch('api.php?action=getUserGroups')
.then(response => response.json())
.then(data => {
const groupsList = document.getElementById('groups-list');
groupsList.innerHTML = '';
// 更新全局群组列表
groups = data.groups || [];
if (groups && groups.length > 0) {
groups.forEach(group => {
const groupItem = document.createElement('div');
groupItem.className = 'group-item';
groupItem.dataset.groupId = group.id;
// 构建头像HTML
let avatarHtml = '';
if (group.avatar) {
avatarHtml = `<img src="${group.avatar}" alt="群组头像" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover;">`;
} else {
avatarHtml = '<i class="fas fa-users"></i>';
}
groupItem.innerHTML = `
<div class="avatar">
${avatarHtml}
</div>
<div class="group-info">
<div class="group-name">${group.name}</div>
<div class="group-last-message">${group.lastMessage || '暂无消息'}</div>
</div>
<div class="group-time">${group.lastMessageTime || ''}</div>
`;
// 点击群组开始聊天
groupItem.addEventListener('click', function() {
// 移除所有active类
document.querySelectorAll('.group-item').forEach(item => {
item.classList.remove('active');
});
// 添加当前active类
this.classList.add('active');
// 关闭手机端侧边栏和背景遮罩
const sidebar = document.querySelector('.sidebar');
const sidebarBackdrop = document.getElementById('sidebar-backdrop');
if (sidebar) {
sidebar.classList.remove('active');
}
if (sidebarBackdrop) {
sidebarBackdrop.classList.remove('active');
}
// 设置当前聊天群组
currentGroup = group;
openGroupChatWindow(group);
});
groupsList.appendChild(groupItem);
});
} else {
groupsList.innerHTML = '<div style="text-align: center; padding: 20px; color: #999;">暂无群组,点击右上角创建群组</div>';
}
})
.catch(error => {
console.error('加载群组列表失败:', error);
});
}
// 绑定工具按钮事件
bindToolButtonEvents();
// 生成emoji
function generateEmojis(category) {
emojiContent.innerHTML = '';
currentEmojiCategory = category;
// 更新分类按钮状态
emojiCategories.forEach(btn => {
btn.classList.remove('active');
if (btn.getAttribute('data-category') === category) {
btn.classList.add('active');
}
});
// 生成emoji按钮
emojis[category].forEach(emoji => {
const emojiBtn = document.createElement('button');
emojiBtn.className = 'emoji-item';
emojiBtn.textContent = emoji;
emojiBtn.addEventListener('click', function() {
// 将emoji添加到输入框
messageInput.value += emoji;
messageInput.focus();
});
emojiContent.appendChild(emojiBtn);
});
}
// 切换emoji分类
if (emojiCategories.length > 0) {
emojiCategories.forEach(btn => {
btn.addEventListener('click', function() {
const category = this.getAttribute('data-category');
generateEmojis(category);
});
});
}
// 关闭emoji选择器
if (closeEmojiBtn) {
closeEmojiBtn.addEventListener('click', function() {
emojiSelector.classList.remove('active');
});
}
// 点击外部关闭emoji选择器
document.addEventListener('click', function(e) {
if (!emojiSelector.contains(e.target) && emojiToolBtn && !emojiToolBtn.contains(e.target)) {
emojiSelector.classList.remove('active');
}
});
// 设置菜单
const settingsMenu = document.createElement('div');
settingsMenu.className = 'settings-menu';
settingsMenu.innerHTML = `
<ul>
<li id="menu-change-username">更改用户名</li>
<li id="menu-change-bio">更改简介</li>
<li id="menu-change-avatar">更改头像</li>
<li id="menu-change-status">状态设置</li>
</ul>
`;
document.body.appendChild(settingsMenu);
// 用户协议设置模态框
const termsSettingsModal = document.createElement('div');
termsSettingsModal.className = 'modal';
termsSettingsModal.innerHTML = `
<div class="modal-content" style="max-width: 400px;">
<div class="modal-header">
<h3>用户协议设置</h3>
<button class="close-btn" id="close-terms-settings-modal">&times;</button>
</div>
<div class="modal-body">
<div class="setting-item">
<div class="setting-label">是否同意获取IP</div>
<div class="setting-description">开启后,系统将获取您的IP地址用于显示位置信息</div>
<label class="switch">
<input type="checkbox" id="getip">
<span class="slider"></span>
</label>
</div>
<div id="terms-settings-error" style="color: #ff4d4f; margin-top: 10px; font-size: 14px;"></div>
<div id="terms-settings-success" style="color: #52c41a; margin-top: 10px; font-size: 14px;"></div>
</div>
<div class="modal-footer">
<button class="btn btn-cancel" id="cancel-terms-settings">取消</button>
<button class="btn btn-primary" id="save-terms-settings">保存设置</button>
</div>
</div>
`;
document.body.appendChild(termsSettingsModal);
// 状态设置模态框
const statusSettingsModal = document.createElement('div');
statusSettingsModal.className = 'modal';
statusSettingsModal.innerHTML = `
<div class="modal-content" style="max-width: 400px;">
<div class="modal-header">
<h3>状态设置</h3>
<button class="close-btn" id="close-status-settings-modal">&times;</button>
</div>
<div class="modal-body">
<div class="setting-item">
<div class="setting-label">在线</div>
<div class="setting-description">显示为在线状态,可接收所有消息通知</div>
<label class="switch">
<input type="radio" name="status" value="online" id="status-online" checked>
</label>
</div>
<div class="setting-item">
<div class="setting-label">勿扰</div>
<div class="setting-description">显示为勿扰状态,消息会静音但仍会收到</div>
<label class="switch">
<input type="radio" name="status" value="dnd" id="status-dnd">
</label>
</div>
<div class="setting-item">
<div class="setting-label">隐身</div>
<div class="setting-description">显示为离线状态,不会收到消息通知</div>
<label class="switch">
<input type="radio" name="status" value="invisible" id="status-invisible">
</label>
</div>
<div id="status-settings-error" style="color: #ff4d4f; margin-top: 10px; font-size: 14px;"></div>
<div id="status-settings-success" style="color: #52c41a; margin-top: 10px; font-size: 14px;"></div>
</div>
<div class="modal-footer">
<button class="btn btn-cancel" id="cancel-status-settings">取消</button>
<button class="btn btn-primary" id="save-status-settings">保存设置</button>
</div>
</div>
`;
document.body.appendChild(statusSettingsModal);
// 协议设置相关元素
const closeTermsSettingsModal = document.getElementById('close-terms-settings-modal');
const cancelTermsSettings = document.getElementById('cancel-terms-settings');
const saveTermsSettings = document.getElementById('save-terms-settings');
const getip = document.getElementById('getip');
const termsSettingsError = document.getElementById('terms-settings-error');
const termsSettingsSuccess = document.getElementById('terms-settings-success');
// 状态设置相关元素
const closeStatusSettingsModal = document.getElementById('close-status-settings-modal');
const cancelStatusSettings = document.getElementById('cancel-status-settings');
const saveStatusSettings = document.getElementById('save-status-settings');
const statusOnline = document.getElementById('status-online');
const statusDnd = document.getElementById('status-dnd');
const statusInvisible = document.getElementById('status-invisible');
const statusSettingsError = document.getElementById('status-settings-error');
const statusSettingsSuccess = document.getElementById('status-settings-success');
// 更改用户名模态框
const changeUsernameModal = document.createElement('div');
changeUsernameModal.className = 'modal';
changeUsernameModal.innerHTML = `
<div class="modal-content" style="max-width: 400px;">
<div class="modal-header">
<h3>更改用户名</h3>
<button class="close-btn" id="close-username-modal">&times;</button>
</div>
<div class="modal-body">
<div class="input-group">
<i class="fas fa-user"></i>
<input type="text" id="new-username" placeholder="输入新用户名" required>
</div>
<div id="username-error" style="color: #ff4d4f; margin-top: 10px; font-size: 14px;"></div>
</div>
<div class="modal-footer">
<button class="btn btn-cancel" id="cancel-username-change">取消</button>
<button class="btn btn-primary" id="confirm-username-change">确认</button>
</div>
</div>
`;
document.body.appendChild(changeUsernameModal);
// 更改简介模态框
const changeBioModal = document.createElement('div');
changeBioModal.className = 'modal';
changeBioModal.innerHTML = `
<div class="modal-content" style="max-width: 400px;">
<div class="modal-header">
<h3>更改简介</h3>
<button class="close-btn" id="close-bio-modal">&times;</button>
</div>
<div class="modal-body">
<div class="input-group">
<i class="fas fa-info-circle"></i>
<textarea id="new-bio" placeholder="输入个人简介" rows="4" style="padding: 12px 15px 12px 45px; border: 1px solid #ddd; border-radius: 25px; font-size: 14px; width: 100%; resize: none;"></textarea>
</div>
<div id="bio-error" style="color: #ff4d4f; margin-top: 10px; font-size: 14px;"></div>
<div style="color: #999; margin-top: 5px; font-size: 12px;">简介长度不能超过200个字符</div>
</div>
<div class="modal-footer">
<button class="btn btn-cancel" id="cancel-bio-change">取消</button>
<button class="btn btn-primary" id="confirm-bio-change">保存</button>
</div>
</div>
`;
document.body.appendChild(changeBioModal);
// 更改头像模态框
const changeAvatarModal = document.createElement('div');
changeAvatarModal.className = 'modal';
changeAvatarModal.innerHTML = `
<div class="modal-content" style="max-width: 400px;">
<div class="modal-header">
<h3>更改头像</h3>
<button class="close-btn" id="close-avatar-modal">&times;</button>
</div>
<div class="modal-body">
<div class="avatar-upload-container">
<div class="avatar-preview" id="avatar-preview">
<img src="" alt="预览头像" id="preview-avatar-img">
</div>
<input type="file" id="avatar-file" accept="image/*" style="display: none;">
<button class="btn btn-primary" id="choose-avatar-btn">选择图片</button>
</div>
<div id="avatar-error" style="color: #ff4d4f; margin-top: 10px; font-size: 14px;"></div>
</div>
<div class="modal-footer">
<button class="btn btn-cancel" id="cancel-avatar-change">取消</button>
<button class="btn btn-primary" id="confirm-avatar-change">保存</button>
</div>
</div>
`;
document.body.appendChild(changeAvatarModal);
// 更改位置模态框
const changeLocationModal = document.createElement('div');
changeLocationModal.className = 'modal';
changeLocationModal.innerHTML = `
<div class="modal-content" style="max-width: 400px;">
<div class="modal-header">
<h3>更改位置</h3>
<button class="close-btn" id="close-location-modal">&times;</button>
</div>
<div class="modal-body">
<div class="input-group">
<i class="fas fa-map-marker-alt"></i>
<select id="new-location" required style="padding: 12px 15px 12px 45px; border: 1px solid #ddd; border-radius: 25px; font-size: 14px; width: 100%; outline: none;">
<option value="">请选择位置</option>
<option value="北京市">北京市</option>
<option value="天津市">天津市</option>
<option value="河北省">河北省</option>
<option value="山西省">山西省</option>
<option value="内蒙古自治区">内蒙古自治区</option>
<option value="辽宁省">辽宁省</option>
<option value="吉林省">吉林省</option>
<option value="黑龙江省">黑龙江省</option>
<option value="上海市">上海市</option>
<option value="江苏省">江苏省</option>
<option value="浙江省">浙江省</option>
<option value="安徽省">安徽省</option>
<option value="福建省">福建省</option>
<option value="江西省">江西省</option>
<option value="山东省">山东省</option>
<option value="河南省">河南省</option>
<option value="湖北省">湖北省</option>
<option value="湖南省">湖南省</option>
<option value="广东省">广东省</option>
<option value="广西壮族自治区">广西壮族自治区</option>
<option value="海南省">海南省</option>
<option value="重庆市">重庆市</option>
<option value="四川省">四川省</option>
<option value="贵州省">贵州省</option>
<option value="云南省">云南省</option>
<option value="西藏自治区">西藏自治区</option>
<option value="陕西省">陕西省</option>
<option value="甘肃省">甘肃省</option>
<option value="青海省">青海省</option>
<option value="宁夏回族自治区">宁夏回族自治区</option>
<option value="新疆维吾尔自治区">新疆维吾尔自治区</option>
<option value="香港特别行政区">香港特别行政区</option>
<option value="澳门特别行政区">澳门特别行政区</option>
<option value="台湾省">台湾省</option>
</select>
</div>
<div id="location-error" style="color: #ff4d4f; margin-top: 10px; font-size: 14px;"></div>
<div style="color: #999; margin-top: 5px; font-size: 12px;">位置信息将显示在您的个人资料页面</div>
</div>
<div class="modal-footer">
<button class="btn btn-cancel" id="cancel-location-change">取消</button>
<button class="btn btn-primary" id="confirm-location-change">保存</button>
</div>
</div>
`;
document.body.appendChild(changeLocationModal);
// 更改用户名相关元素
const closeUsernameModal = document.getElementById('close-username-modal');
const cancelUsernameChange = document.getElementById('cancel-username-change');
const confirmUsernameChange = document.getElementById('confirm-username-change');
const newUsernameInput = document.getElementById('new-username');
const usernameError = document.getElementById('username-error');
// 更改简介相关元素
const closeBioModal = document.getElementById('close-bio-modal');
const cancelBioChange = document.getElementById('cancel-bio-change');
const confirmBioChange = document.getElementById('confirm-bio-change');
const newBioInput = document.getElementById('new-bio');
const bioError = document.getElementById('bio-error');
// 更改头像相关元素
const closeAvatarModal = document.getElementById('close-avatar-modal');
const cancelAvatarChange = document.getElementById('cancel-avatar-change');
const confirmAvatarChange = document.getElementById('confirm-avatar-change');
const chooseAvatarBtn = document.getElementById('choose-avatar-btn');
const avatarFile = document.getElementById('avatar-file');
const previewAvatarImg = document.getElementById('preview-avatar-img');
const avatarError = document.getElementById('avatar-error');
let currentAvatarFile = null;
// 更改位置相关元素
const closeLocationModal = document.getElementById('close-location-modal');
const cancelLocationChange = document.getElementById('cancel-location-change');
const confirmLocationChange = document.getElementById('confirm-location-change');
const newLocationInput = document.getElementById('new-location');
const locationError = document.getElementById('location-error');
// 关闭位置更改模态框
function closeLocationModalFunc() {
changeLocationModal.classList.remove('active');
}
if (closeLocationModal) {
closeLocationModal.addEventListener('click', closeLocationModalFunc);
}
if (cancelLocationChange) {
cancelLocationChange.addEventListener('click', closeLocationModalFunc);
}
// 点击模态框外部关闭
if (changeLocationModal) {
changeLocationModal.addEventListener('click', function(e) {
if (e.target === changeLocationModal) {
closeLocationModalFunc();
}
});
}
// 确认更改位置
if (confirmLocationChange) {
confirmLocationChange.addEventListener('click', function() {
const newLocation = newLocationInput.value.trim();
if (!newLocation) {
locationError.innerHTML = '请输入位置信息';
return;
}
// 发送更改位置请求
fetch('api.php?action=changeLocation', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'newLocation=' + encodeURIComponent(newLocation)
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 显示成功消息
locationError.innerHTML = '<span style="color: #07c160;">位置更新成功</span>';
// 2秒后关闭模态框
setTimeout(() => {
closeLocationModalFunc();
// 刷新页面以显示新位置
location.reload();
}, 1000);
} else {
locationError.innerHTML = data.message || '位置更新失败';
}
})
.catch(error => {
console.error('更改位置失败:', error);
locationError.innerHTML = '位置更新失败,请稍后重试';
});
});
}
// 打开设置菜单
const settingsBtn = document.getElementById('settings-btn');
if (settingsBtn) {
settingsBtn.addEventListener('click', function(e) {
e.stopPropagation();
// 显示设置菜单
const rect = settingsBtn.getBoundingClientRect();
settingsMenu.style.top = rect.bottom + 10 + 'px';
settingsMenu.style.right = (window.innerWidth - rect.right) + 'px';
settingsMenu.classList.add('active');
});
}
// 主菜单按钮点击事件
const menuBtn = document.getElementById('menu-btn');
const mainMenu = document.getElementById('main-menu');
if (menuBtn && mainMenu) {
menuBtn.addEventListener('click', function(e) {
e.stopPropagation();
// 计算菜单位置
const rect = menuBtn.getBoundingClientRect();
mainMenu.style.top = rect.bottom + 5 + 'px';
mainMenu.style.right = (window.innerWidth - rect.right) + 'px';
mainMenu.style.position = 'fixed';
mainMenu.style.zIndex = '9999';
mainMenu.classList.toggle('active');
});
// 菜单项点击事件
const menuCreateGroup = document.getElementById('menu-create-group');
if (menuCreateGroup) {
menuCreateGroup.addEventListener('click', function(e) {
e.stopPropagation();
openCreateGroupModal();
mainMenu.classList.remove('active');
});
}
const menuJoinGroup = document.getElementById('menu-join-group');
if (menuJoinGroup) {
menuJoinGroup.addEventListener('click', function(e) {
e.stopPropagation();
openJoinGroupModal();
mainMenu.classList.remove('active');
});
}
const menuSettings = document.getElementById('menu-settings');
if (menuSettings) {
menuSettings.addEventListener('click', function(e) {
e.stopPropagation();
// 直接打开设置菜单
const settingsMenu = document.querySelector('.settings-menu');
if (settingsMenu) {
const menuBtnRect = menuBtn.getBoundingClientRect();
settingsMenu.style.top = menuBtnRect.bottom + 10 + 'px';
settingsMenu.style.right = (window.innerWidth - menuBtnRect.right) + 'px';
settingsMenu.classList.add('active');
}
mainMenu.classList.remove('active');
});
}
const menuLogout = document.getElementById('menu-logout');
if (menuLogout) {
menuLogout.addEventListener('click', function(e) {
e.stopPropagation();
// 跳转到登出链接
window.location.href = 'index.php?action=logout';
});
}
const menuGoDocs = document.getElementById('menu-go-docs');
if (menuGoDocs) {
menuGoDocs.addEventListener('click', function(e) {
e.stopPropagation();
// 跳转到文档页面
window.location.href = 'docs/index.php';
mainMenu.classList.remove('active');
});
}
}
// 点击页面其他地方关闭下拉菜单
document.addEventListener('click', function() {
if (mainMenu) {
mainMenu.classList.remove('active');
}
// 关闭群组菜单
const groupMenu = document.getElementById('group-menu');
if (groupMenu) {
groupMenu.classList.remove('active');
}
// 关闭右键菜单
const contextMenu = document.getElementById('message-context-menu');
if (contextMenu) {
contextMenu.classList.remove('active');
contextMenu.style.display = 'none';
}
});
// 阻止下拉菜单内部点击事件冒泡
if (mainMenu) {
mainMenu.addEventListener('click', function(e) {
e.stopPropagation();
});
}
// 加入群组模态框
const joinGroupModal = document.getElementById('join-group-modal');
const joinGroupIdInput = document.getElementById('join-group-id');
const joinGroupResult = document.getElementById('join-group-result');
const confirmJoinGroupBtn = document.getElementById('confirm-join-group');
const closeJoinGroupModalBtn = document.getElementById('close-join-group-modal');
const cancelJoinGroupBtn = document.getElementById('cancel-join-group');
// 邀请好友加入群组模态框
const inviteGroupModal = document.getElementById('invite-group-modal');
const inviteFriendUsernameInput = document.getElementById('invite-friend-username');
const inviteGroupResult = document.getElementById('invite-group-result');
const confirmInviteGroupBtn = document.getElementById('confirm-invite-group');
const closeInviteGroupModalBtn = document.getElementById('close-invite-group-modal');
const cancelInviteGroupBtn = document.getElementById('cancel-invite-group');
// 好友请求相关元素
const friendRequestsBtn = document.getElementById('friend-requests-btn');
const requestBadge = document.getElementById('request-badge');
const friendRequestsModal = document.getElementById('friend-requests-modal');
const friendRequestsList = document.getElementById('friend-requests-list');
const friendRequestsResult = document.getElementById('friend-requests-result');
const closeFriendRequestsModalBtn = document.getElementById('close-friend-requests-modal');
const cancelFriendRequestsBtn = document.getElementById('cancel-friend-requests');
// 分享群组模态框
const shareGroupModal = document.createElement('div');
shareGroupModal.className = 'modal';
shareGroupModal.innerHTML = `
<div class="modal-content" style="max-width: 500px;">
<div class="modal-header">
<h3><i class="fas fa-share-alt"></i> 分享群组</h3>
<button class="close-btn" id="close-share-group-modal">&times;</button>
</div>
<div class="modal-body">
<div id="share-group-result"></div>
<div class="share-group-info">
<h4 id="share-group-name"></h4>
<p>分享链接</p>
<div class="share-link-container">
<input type="text" id="share-link" readonly class="share-link-input">
<button class="btn btn-primary" id="copy-share-link">
<i class="fas fa-copy"></i> 复制
</button>
</div>
<p class="share-tip">复制链接发送给好友,好友点击链接即可加入群组</p>
<!-- 二维码显示区域 -->
<div class="qrcode-section">
<p>二维码分享</p>
<div class="qrcode-container">
<div id="qrcode" style="display: flex; justify-content: center; margin: 20px 0;"></div>
<div class="qrcode-loading" id="qrcode-loading" style="display: none; text-align: center; padding: 20px;">
<i class="fas fa-spinner fa-spin"></i> 生成二维码中...
</div>
</div>
<div class="qrcode-actions">
<button class="btn btn-primary" id="download-qrcode">
<i class="fas fa-download"></i> 下载二维码
</button>
<button class="btn btn-secondary" id="refresh-qrcode">
<i class="fas fa-sync-alt"></i> 刷新二维码
</button>
</div>
<p class="share-tip">扫描二维码即可加入群组</p>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" id="close-share-group-btn">关闭</button>
</div>
</div>
`;
document.body.appendChild(shareGroupModal);
// 分享群组模态框相关元素
const closeShareGroupModalBtn = document.getElementById('close-share-group-modal');
const closeShareGroupBtn = document.getElementById('close-share-group-btn');
const shareGroupResult = document.getElementById('share-group-result');
const shareGroupName = document.getElementById('share-group-name');
const shareLink = document.getElementById('share-link');
const copyShareLinkBtn = document.getElementById('copy-share-link');
const qrcodeElement = document.getElementById('qrcode');
const qrcodeLoading = document.getElementById('qrcode-loading');
const downloadQrcodeBtn = document.getElementById('download-qrcode');
const refreshQrcodeBtn = document.getElementById('refresh-qrcode');
let qrcodeInstance = null;
// 打开加入群组模态框
function openJoinGroupModal() {
joinGroupModal.classList.add('active');
joinGroupIdInput.value = '';
joinGroupResult.innerHTML = '';
}
// 关闭加入群组模态框
function closeJoinGroupModal() {
joinGroupModal.classList.remove('active');
}
// 打开邀请好友加入群组模态框
// 打开备注编辑弹窗
function openSetNoteModal(targetId, targetName, targetType) {
const setNoteModal = document.getElementById('set-note-modal');
const closeSetNoteModalBtn = document.getElementById('close-set-note-modal');
const cancelSetNoteBtn = document.getElementById('cancel-set-note');
const confirmSetNoteBtn = document.getElementById('confirm-set-note');
const noteInput = document.getElementById('note-input');
const currentNameSpan = document.getElementById('current-name');
const noteTargetId = document.getElementById('note-target-id');
const noteTargetType = document.getElementById('note-target-type');
// 设置当前名称
currentNameSpan.textContent = targetName;
// 设置目标信息
noteTargetId.value = targetId;
noteTargetType.value = targetType;
// 清空输入框
noteInput.value = '';
// 获取现有备注
fetch(`api.php?action=getNote&target_id=${encodeURIComponent(targetId)}&target_type=${encodeURIComponent(targetType)}`)
.then(response => response.json())
.then(data => {
if (data.success && data.note) {
noteInput.value = data.note;
}
})
.catch(error => {
console.error('获取备注失败:', error);
});
// 显示弹窗
setNoteModal.style.display = 'block';
// 关闭弹窗事件
function closeModal() {
setNoteModal.style.display = 'none';
closeSetNoteModalBtn.removeEventListener('click', closeModal);
cancelSetNoteBtn.removeEventListener('click', closeModal);
confirmSetNoteBtn.removeEventListener('click', confirmSetNote);
}
// 保存备注事件
function confirmSetNote() {
const note = noteInput.value.trim();
fetch('api.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `action=setNote&target_id=${encodeURIComponent(targetId)}&target_type=${encodeURIComponent(targetType)}&note=${encodeURIComponent(note)}`
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('备注设置成功', 'success');
closeModal();
// 更新聊天头部显示的名称(如果有备注)
if (note) {
const chatUsername = document.getElementById('chat-username');
if (chatUsername) {
chatUsername.innerHTML = note;
}
}
} else {
showNotification(data.message || '设置备注失败', 'error');
}
})
.catch(error => {
console.error('设置备注失败:', error);
showNotification('设置备注失败', 'error');
});
}
closeSetNoteModalBtn.addEventListener('click', closeModal);
cancelSetNoteBtn.addEventListener('click', closeModal);
confirmSetNoteBtn.addEventListener('click', confirmSetNote);
}
function openInviteGroupModal() {
if (!currentGroup) {
showNotification('请先选择一个群组', 'warning');
return;
}
const friendsListForInvite = document.getElementById('friends-list-for-invite');
inviteGroupModal.classList.add('active');
inviteFriendUsernameInput.value = '';
inviteGroupResult.innerHTML = '';
// 加载好友列表供选择
fetch('api.php?action=getFriends')
.then(response => response.json())
.then(data => {
friendsListForInvite.innerHTML = '';
if (data.friends && data.friends.length > 0) {
data.friends.forEach(friend => {
const displayName = friend.note || friend.username;
const friendItem = document.createElement('div');
friendItem.className = 'friend-checkbox-item';
friendItem.innerHTML = `
<input type="checkbox" value="${friend.id}" id="invite-friend-${friend.id}">
<label for="invite-friend-${friend.id}">${displayName}${friend.note ? ` (${friend.username})` : ''}</label>
`;
friendsListForInvite.appendChild(friendItem);
// 点击好友项时,将用户名填入输入框
friendItem.addEventListener('click', function() {
inviteFriendUsernameInput.value = friend.username;
});
});
} else {
friendsListForInvite.innerHTML = '<div style="padding: 10px; color: #999;">暂无好友</div>';
}
})
.catch(error => {
console.error('加载好友列表失败:', error);
friendsListForInvite.innerHTML = '<div style="padding: 10px; color: #ff4d4f;">加载失败,请刷新页面重试</div>';
});
}
// 关闭邀请好友加入群组模态框
function closeInviteGroupModal() {
inviteGroupModal.classList.remove('active');
}
// 确认加入群组
if (confirmJoinGroupBtn) {
confirmJoinGroupBtn.addEventListener('click', function() {
const groupId = joinGroupIdInput.value.trim();
if (!groupId) {
joinGroupResult.innerHTML = '<div class="error">请输入群号</div>';
return;
}
// 发送加入群组请求
fetch('api.php?action=joinGroupByGroupId', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'group_id=' + encodeURIComponent(groupId)
})
.then(response => response.json())
.then(data => {
if (data.success) {
joinGroupResult.innerHTML = '<div style="color: #07c160; padding: 10px; background: #e8f5e8; border-radius: 5px;">加入群组成功!</div>';
// 重新加载群组列表
loadGroupsList();
// 3秒后关闭模态框
setTimeout(closeJoinGroupModal, 2000);
} else {
joinGroupResult.innerHTML = '<div class="error">' + data.message + '</div>';
}
})
.catch(error => {
console.error('加入群组失败:', error);
joinGroupResult.innerHTML = '<div class="error">加入群组失败,请稍后重试</div>';
});
});
}
// 确认邀请好友加入群组
if (confirmInviteGroupBtn) {
confirmInviteGroupBtn.addEventListener('click', function() {
const friendUsername = inviteFriendUsernameInput.value.trim();
if (!friendUsername || !currentGroup) {
inviteGroupResult.innerHTML = '<div class="error">请输入好友用户名</div>';
return;
}
// 发送邀请请求
fetch('api.php?action=inviteToGroup', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'groupId=' + encodeURIComponent(currentGroup.id) + '&friendUsername=' + encodeURIComponent(friendUsername)
})
.then(response => response.json())
.then(data => {
if (data.success) {
inviteGroupResult.innerHTML = '<div style="color: #07c160; padding: 10px; background: #e8f5e8; border-radius: 5px;">邀请发送成功!</div>';
// 3秒后关闭模态框
setTimeout(closeInviteGroupModal, 2000);
} else {
inviteGroupResult.innerHTML = '<div class="error">' + data.message + '</div>';
}
})
.catch(error => {
console.error('邀请好友失败:', error);
inviteGroupResult.innerHTML = '<div class="error">邀请好友失败,请稍后重试</div>';
});
});
}
// 关闭加入群组模态框
if (closeJoinGroupModalBtn) {
closeJoinGroupModalBtn.addEventListener('click', closeJoinGroupModal);
}
if (cancelJoinGroupBtn) {
cancelJoinGroupBtn.addEventListener('click', closeJoinGroupModal);
}
// 关闭邀请好友加入群组模态框
if (closeInviteGroupModalBtn) {
closeInviteGroupModalBtn.addEventListener('click', closeInviteGroupModal);
}
if (cancelInviteGroupBtn) {
cancelInviteGroupBtn.addEventListener('click', closeInviteGroupModal);
}
// 群组设置模态框
const groupSettingsModal = document.getElementById('group-settings-modal');
const groupSettingsNameInput = document.getElementById('group-settings-name');
const groupNameResult = document.getElementById('group-name-result');
const groupMembersList = document.getElementById('group-members-list');
const groupMembersResult = document.getElementById('group-members-result');
const saveGroupSettingsBtn = document.getElementById('save-group-settings');
const closeGroupSettingsModalBtn = document.getElementById('close-group-settings-modal');
const cancelGroupSettingsBtn = document.getElementById('cancel-group-settings');
// 格式化时间戳函数
function formatTimestamp(timestamp) {
const date = new Date(timestamp * 1000);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
// 举报消息
function reportMessage(messageId) {
showLoading();
fetch('api.php?action=reportMessage', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'messageId=' + encodeURIComponent(messageId)
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('举报成功,管理员将尽快处理', 'success');
} else {
showNotification('举报失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('举报消息失败:', error);
showNotification('举报消息失败', 'error');
})
.finally(() => {
hideLoading();
});
}
// 为举报菜单项添加点击事件
function initReportMenuItem() {
const menuReport = document.getElementById('menu-report');
if (menuReport) {
menuReport.addEventListener('click', function() {
const currentMessageElement = window.currentMessageElement;
if (currentMessageElement) {
// 获取消息ID
const messageId = currentMessageElement.getAttribute('data-message-id');
if (messageId) {
// 举报消息
reportMessage(messageId);
// 关闭右键菜单
const contextMenu = document.querySelector('.context-menu');
if (contextMenu) {
contextMenu.classList.remove('active');
}
} else {
console.error('获取消息ID失败');
showNotification('获取消息ID失败', 'error');
}
} else {
console.error('获取当前消息元素失败');
showNotification('获取当前消息元素失败', 'error');
}
});
} else {
console.error('举报菜单项不存在,稍后尝试初始化');
// 延迟100ms后再次尝试初始化
setTimeout(initReportMenuItem, 100);
}
}
// 初始化举报菜单项
initReportMenuItem();
// 群组公告相关元素
const groupAnnouncementAdmin = document.getElementById('group-announcement-admin');
const groupAnnouncementContent = document.getElementById('group-announcement-content');
const saveAnnouncementBtn = document.getElementById('save-announcement-btn');
const announcementResult = document.getElementById('announcement-result');
const groupAnnouncementsList = document.getElementById('group-announcements-list');
// 群组公告模态框相关元素
const groupAnnouncementModal = document.getElementById('group-announcement-modal');
const closeGroupAnnouncementModalBtn = document.getElementById('close-group-announcement-modal');
const cancelGroupAnnouncementBtn = document.getElementById('cancel-group-announcement');
const saveAnnouncementBtnView = document.getElementById('save-announcement-btn-view');
// 群聊界面公告显示元素
const groupAnnouncement = document.getElementById('group-announcement');
const announcementContent = document.getElementById('announcement-content');
const announcementTime = document.getElementById('announcement-time');
const closeAnnouncementBtn = document.getElementById('close-announcement-btn');
// 打开群组设置模态框
function openGroupSettingsModal(group) {
groupSettingsModal.classList.add('active');
groupSettingsNameInput.value = group.name;
groupNameResult.innerHTML = '';
groupMembersResult.innerHTML = '';
announcementResult.innerHTML = '';
// 显示当前群头像
const groupAvatarPreview = document.getElementById('group-avatar-preview');
const groupAvatarImg = groupAvatarPreview.querySelector('img');
if (group.avatar) {
groupAvatarImg.src = group.avatar;
} else {
groupAvatarImg.src = '';
groupAvatarImg.alt = '默认群头像';
}
// 加载群组成员
loadGroupMembers(group.id);
// 加载群组公告
loadGroupAnnouncements(group.id);
// 检查用户是否是管理员
if (group.role === 'admin') {
groupAnnouncementAdmin.style.display = 'block';
document.getElementById('change-group-avatar-btn').style.display = 'flex';
} else {
groupAnnouncementAdmin.style.display = 'none';
document.getElementById('change-group-avatar-btn').style.display = 'none';
}
// 保存当前群组信息,用于后续操作
window.currentGroup = group;
}
// 为群头像上传按钮添加事件监听
document.addEventListener('DOMContentLoaded', function() {
// 延迟执行,确保所有元素都已加载
setTimeout(function() {
const changeGroupAvatarBtn = document.getElementById('change-group-avatar-btn');
const groupAvatarInput = document.getElementById('group-avatar-input');
console.log('群头像按钮:', changeGroupAvatarBtn);
console.log('群头像输入:', groupAvatarInput);
if (changeGroupAvatarBtn && groupAvatarInput) {
// 确保按钮有正确的样式
changeGroupAvatarBtn.style.position = 'absolute';
changeGroupAvatarBtn.style.bottom = '0';
changeGroupAvatarBtn.style.right = '0';
changeGroupAvatarBtn.style.transform = 'translate(25%, 25%)';
changeGroupAvatarBtn.style.borderRadius = '50%';
changeGroupAvatarBtn.style.width = '30px';
changeGroupAvatarBtn.style.height = '30px';
changeGroupAvatarBtn.style.display = 'flex';
changeGroupAvatarBtn.style.alignItems = 'center';
changeGroupAvatarBtn.style.justifyContent = 'center';
changeGroupAvatarBtn.style.padding = '0';
changeGroupAvatarBtn.style.backgroundColor = '#007bff';
changeGroupAvatarBtn.style.color = 'white';
changeGroupAvatarBtn.style.border = 'none';
changeGroupAvatarBtn.style.cursor = 'pointer';
changeGroupAvatarBtn.style.zIndex = '10'; // 确保按钮在最上层
changeGroupAvatarBtn.addEventListener('click', function(e) {
e.stopPropagation();
console.log('点击了群头像上传按钮');
groupAvatarInput.click();
});
groupAvatarInput.addEventListener('change', function(e) {
const file = e.target.files[0];
if (file) {
console.log('选择了文件:', file);
uploadGroupAvatar(file);
}
});
} else {
console.log('群头像按钮或输入元素不存在');
}
}, 1000);
});
// 上传群头像
function uploadGroupAvatar(file) {
if (!window.currentGroup) {
showNotification('群组信息未加载', 'error');
return;
}
const groupId = window.currentGroup.id;
const formData = new FormData();
formData.append('avatar', file);
formData.append('groupId', groupId);
showLoading();
fetch('api.php?action=changeGroupAvatar', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 更新本地群组信息
window.currentGroup.avatar = data.avatar;
// 更新群头像预览
const groupAvatarPreview = document.getElementById('group-avatar-preview');
const groupAvatarImg = groupAvatarPreview.querySelector('img');
groupAvatarImg.src = data.avatar;
// 更新群组列表中的头像
const groupElement = document.querySelector(`.chat-item[data-id="${groupId}"]`);
if (groupElement) {
const groupAvatarElement = groupElement.querySelector('.avatar');
if (groupAvatarElement) {
groupAvatarElement.innerHTML = `<img src="${data.avatar}" alt="群组头像" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover;">`;
}
}
showNotification('群头像更新成功', 'success');
} else {
showNotification('群头像更新失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('上传群头像失败:', error);
showNotification('上传群头像失败', 'error');
})
.finally(() => {
hideLoading();
});
}
// 关闭群组设置模态框
function closeGroupSettingsModal() {
groupSettingsModal.classList.remove('active');
}
// 打开群组公告模态框
function openGroupAnnouncementModal(group) {
const groupAnnouncementModal = document.getElementById('group-announcement-modal');
const groupAnnouncementAdminView = document.getElementById('group-announcement-admin-view');
const groupAnnouncementsListView = document.getElementById('group-announcements-list-view');
const groupAnnouncementContentView = document.getElementById('group-announcement-content-view');
const announcementResultView = document.getElementById('announcement-result-view');
if (groupAnnouncementModal) {
groupAnnouncementModal.classList.add('active');
announcementResultView.innerHTML = '';
// 检查用户是否是管理员
if (group.role === 'admin') {
groupAnnouncementAdminView.style.display = 'block';
} else {
groupAnnouncementAdminView.style.display = 'none';
}
// 加载群组公告
loadGroupAnnouncementsForModal(group.id);
}
}
// 加载群组公告到模态框
function loadGroupAnnouncementsForModal(groupId) {
const groupAnnouncementsListView = document.getElementById('group-announcements-list-view');
fetch('api.php?action=getGroupAnnouncements&groupId=' + encodeURIComponent(groupId))
.then(response => response.json())
.then(data => {
if (data.success) {
const announcements = data.announcements;
groupAnnouncementsListView.innerHTML = '';
if (announcements.length === 0) {
groupAnnouncementsListView.innerHTML = '<p style="text-align: center; color: #999; margin-top: 20px;">暂无公告</p>';
} else {
announcements.forEach(announcement => {
const announcementItem = document.createElement('div');
announcementItem.style.cssText = 'padding: 15px; border-bottom: 1px solid #f0f0f0; position: relative; background: #f9f9f9; border-radius: 8px; margin-bottom: 10px;';
// 检查当前用户是否是管理员
const isAdmin = currentGroup && currentGroup.role === 'admin';
// 公告内容
const contentDiv = document.createElement('div');
contentDiv.style.cssText = 'margin-bottom: 8px; font-size: 14px; line-height: 1.5;';
contentDiv.textContent = announcement.content;
// 公告时间
const timeDiv = document.createElement('div');
timeDiv.style.cssText = 'font-size: 12px; color: #999;';
timeDiv.textContent = formatTimestamp(announcement.created_at);
// 删除按钮(仅管理员可见)
if (isAdmin) {
const deleteBtn = document.createElement('button');
deleteBtn.innerHTML = '<i class="fas fa-trash"></i>';
deleteBtn.style.cssText = 'position: absolute; top: 10px; right: 10px; background: none; border: none; color: #ff4d4f; cursor: pointer; font-size: 14px;';
deleteBtn.addEventListener('click', function() {
if (confirm('确定要删除这条公告吗?')) {
deleteAnnouncement(groupId, announcement.id);
loadGroupAnnouncementsForModal(groupId);
}
});
announcementItem.appendChild(deleteBtn);
}
announcementItem.appendChild(contentDiv);
announcementItem.appendChild(timeDiv);
groupAnnouncementsListView.appendChild(announcementItem);
});
}
} else {
groupAnnouncementsListView.innerHTML = '<p style="text-align: center; color: #ff4d4f; margin-top: 20px;">加载公告失败</p>';
}
})
.catch(error => {
console.error('加载群公告失败:', error);
groupAnnouncementsListView.innerHTML = '<p style="text-align: center; color: #ff4d4f; margin-top: 20px;">加载公告失败</p>';
});
}
// 保存公告到模态框
function saveAnnouncementForModal() {
if (!currentGroup) {
showNotification('请先选择一个群组', 'warning');
return;
}
const groupAnnouncementContentView = document.getElementById('group-announcement-content-view');
const announcementResultView = document.getElementById('announcement-result-view');
const content = groupAnnouncementContentView.value.trim();
if (!content) {
announcementResultView.innerHTML = '<div class="error">公告内容不能为空</div>';
return;
}
fetch('api.php?action=updateGroupAnnouncement', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'groupId=' + encodeURIComponent(currentGroup.id) + '&content=' + encodeURIComponent(content)
})
.then(response => response.json())
.then(data => {
if (data.success) {
announcementResultView.innerHTML = '<div style="color: #07c160; padding: 10px; background: #e8f5e8; border-radius: 5px;">公告发布成功!</div>';
groupAnnouncementContentView.value = '';
loadGroupAnnouncementsForModal(currentGroup.id);
} else {
announcementResultView.innerHTML = '<div class="error">' + data.message + '</div>';
}
})
.catch(error => {
console.error('发布公告失败:', error);
announcementResultView.innerHTML = '<div class="error">发布公告失败,请稍后重试</div>';
});
}
// 加载群组公告
function loadGroupAnnouncements(groupId) {
fetch('api.php?action=getGroupAnnouncements&groupId=' + encodeURIComponent(groupId))
.then(response => response.json())
.then(data => {
if (data.success) {
const announcements = data.announcements;
groupAnnouncementsList.innerHTML = '';
if (announcements.length === 0) {
groupAnnouncementsList.innerHTML = '<p style="text-align: center; color: #999; margin-top: 20px;">暂无公告</p>';
} else {
announcements.forEach(announcement => {
const announcementItem = document.createElement('div');
announcementItem.style.cssText = 'padding: 10px; border-bottom: 1px solid #f0f0f0; position: relative;';
// 检查当前用户是否是管理员
const isAdmin = currentGroup && currentGroup.role === 'admin';
// 公告内容
const contentDiv = document.createElement('div');
contentDiv.style.cssText = 'margin-bottom: 8px;';
contentDiv.textContent = announcement.content;
// 公告时间
const timeDiv = document.createElement('div');
timeDiv.style.cssText = 'font-size: 12px; color: #999;';
timeDiv.textContent = formatTimestamp(announcement.created_at);
// 删除按钮(仅管理员可见)
if (isAdmin) {
const deleteBtn = document.createElement('button');
deleteBtn.innerHTML = '<i class="fas fa-trash"></i>';
deleteBtn.style.cssText = 'position: absolute; top: 10px; right: 10px; background: none; border: none; color: #ff4d4f; cursor: pointer; font-size: 14px;';
deleteBtn.addEventListener('click', function() {
if (confirm('确定要删除这条公告吗?')) {
deleteAnnouncement(groupId, announcement.id);
}
});
announcementItem.appendChild(deleteBtn);
}
announcementItem.appendChild(contentDiv);
announcementItem.appendChild(timeDiv);
groupAnnouncementsList.appendChild(announcementItem);
});
}
} else {
groupAnnouncementsList.innerHTML = '<p style="text-align: center; color: #ff4d4f; margin-top: 20px;">加载公告失败</p>';
}
})
.catch(error => {
console.error('加载公告失败:', error);
groupAnnouncementsList.innerHTML = '<p style="text-align: center; color: #ff4d4f; margin-top: 20px;">加载公告失败</p>';
});
}
// 保存公告
function saveAnnouncement() {
if (!currentGroup) return;
const content = groupAnnouncementContent.value.trim();
if (!content) {
announcementResult.innerHTML = '<div class="error">公告内容不能为空</div>';
return;
}
fetch('api.php?action=updateGroupAnnouncement', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'groupId=' + encodeURIComponent(currentGroup.id) + '&content=' + encodeURIComponent(content)
})
.then(response => response.json())
.then(data => {
if (data.success) {
announcementResult.innerHTML = '<div style="color: #07c160; padding: 10px; background: #e8f5e8; border-radius: 5px;">公告发布成功!</div>';
// 清空公告内容
groupAnnouncementContent.value = '';
// 重新加载公告列表
loadGroupAnnouncements(currentGroup.id);
} else {
announcementResult.innerHTML = '<div class="error">' + data.message + '</div>';
}
})
.catch(error => {
console.error('发布公告失败:', error);
announcementResult.innerHTML = '<div class="error">发布公告失败,请稍后重试</div>';
});
}
// 删除公告
function deleteAnnouncement(groupId, announcementId) {
fetch('api.php?action=deleteGroupAnnouncement', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'groupId=' + encodeURIComponent(groupId) + '&announcementId=' + encodeURIComponent(announcementId)
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('公告删除成功', 'success');
// 重新加载公告列表
loadGroupAnnouncements(groupId);
} else {
showNotification('删除公告失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('删除公告失败:', error);
showNotification('删除公告失败,请稍后重试', 'error');
});
}
// 为解散群聊按钮添加点击事件
const deleteGroupBtn = document.getElementById('delete-group-btn');
if (deleteGroupBtn) {
deleteGroupBtn.addEventListener('click', function() {
if (currentGroup) {
if (confirm('确定要解散该群组吗?此操作不可恢复。')) {
fetch('api.php?action=deleteGroup', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'groupId=' + encodeURIComponent(currentGroup.id)
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('群组已成功解散', 'success');
// 关闭群组设置模态框
closeGroupSettingsModal();
// 清空当前群组
currentGroup = null;
// 切换到好友标签
// 直接加载好友列表,不需要标签切换
loadFriendsList();
// 清空聊天内容
const chatContent = document.getElementById('chat-content');
const chatInputContainer = document.getElementById('chat-input-container');
chatContent.innerHTML = '<div class="empty-chat"><i class="fas fa-comments"></i><p>选择一个好友开始聊天</p></div>';
chatInputContainer.style.display = 'none';
} else {
showNotification('解散群组失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('解散群组失败:', error);
showNotification('解散群组失败', 'error');
});
}
}
});
}
// 为保存公告按钮添加点击事件
if (saveAnnouncementBtn) {
saveAnnouncementBtn.addEventListener('click', saveAnnouncement);
}
// 为群组公告模态框添加事件监听器
if (closeGroupAnnouncementModalBtn) {
closeGroupAnnouncementModalBtn.addEventListener('click', function() {
if (groupAnnouncementModal) {
groupAnnouncementModal.classList.remove('active');
}
});
}
if (cancelGroupAnnouncementBtn) {
cancelGroupAnnouncementBtn.addEventListener('click', function() {
if (groupAnnouncementModal) {
groupAnnouncementModal.classList.remove('active');
}
});
}
if (saveAnnouncementBtnView) {
saveAnnouncementBtnView.addEventListener('click', saveAnnouncementForModal);
}
// 加载群组成员
function loadGroupMembers(groupId) {
fetch('api.php?action=getGroupMembers&groupId=' + groupId)
.then(response => response.json())
.then(data => {
if (data.success && data.members) {
groupMembersList.innerHTML = '';
data.members.forEach(member => {
const memberItem = document.createElement('div');
memberItem.className = 'group-member-item';
// 检查成员是否被禁言
const isMuted = member.muted || false;
memberItem.innerHTML = `
<div class="member-info">
<div class="avatar" style="width: 30px; height: 30px; margin-right: 10px;">
${member.avatar ? `<img src="${member.avatar}" alt="头像" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover;">` : '<i class="fas fa-user-circle"></i>'}
</div>
<div>
<div>${member.username} ${isMuted ? '<span style="color: #ff4d4f; font-size: 12px; margin-left: 5px;">(已禁言)</span>' : ''}</div>
<div style="font-size: 12px; color: #999;">${member.role === 'admin' ? '管理员' : '成员'}</div>
</div>
</div>
<div class="member-actions">
${member.role !== 'admin' ? `<button class="btn btn-danger btn-sm remove-member-btn" data-member-id="${member.id}">移除</button>` : ''}
${member.role !== 'admin' ? `<button class="btn ${isMuted ? 'btn-success' : 'btn-warning'} btn-sm mute-member-btn" data-member-id="${member.id}" data-muted="${isMuted}">${isMuted ? '解禁' : '禁言'}</button>` : ''}
${member.role !== 'admin' ? `<button class="btn btn-primary btn-sm set-admin-btn" data-member-id="${member.id}">设为管理员</button>` : ''}
</div>
`;
groupMembersList.appendChild(memberItem);
});
// 为移除成员按钮添加点击事件
document.querySelectorAll('.remove-member-btn').forEach(btn => {
btn.addEventListener('click', function() {
const memberId = this.getAttribute('data-member-id');
removeGroupMember(currentGroup.id, memberId);
});
});
// 为禁言/解禁按钮添加点击事件
document.querySelectorAll('.mute-member-btn').forEach(btn => {
btn.addEventListener('click', function() {
const memberId = this.getAttribute('data-member-id');
const isMuted = this.getAttribute('data-muted') === 'true';
const newMutedState = !isMuted;
fetch('api.php?action=muteUser', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `groupId=${encodeURIComponent(currentGroup.id)}&userId=${encodeURIComponent(memberId)}&muted=${encodeURIComponent(newMutedState)}`
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification(newMutedState ? '用户已被禁言' : '用户已被解禁', 'success');
// 重新加载群组成员列表
loadGroupMembers(currentGroup.id);
} else {
showNotification('操作失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('操作失败:', error);
showNotification('操作失败', 'error');
});
});
});
// 为设为管理员按钮添加点击事件
document.querySelectorAll('.set-admin-btn').forEach(btn => {
btn.addEventListener('click', function() {
const memberId = this.getAttribute('data-member-id');
if (confirm('确定要将该成员设为管理员吗?')) {
fetch('api.php?action=updateGroupMemberRole', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `groupId=${encodeURIComponent(currentGroup.id)}&userId=${encodeURIComponent(memberId)}&role=admin`
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('该成员已被设为管理员', 'success');
// 重新加载群组成员列表
loadGroupMembers(currentGroup.id);
} else {
showNotification('操作失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('操作失败:', error);
showNotification('操作失败', 'error');
});
}
});
});
} else {
groupMembersList.innerHTML = '<div style="padding: 10px; color: #999;">加载成员失败</div>';
}
})
.catch(error => {
console.error('加载群组成员失败:', error);
groupMembersList.innerHTML = '<div style="padding: 10px; color: #999;">加载成员失败</div>';
});
}
// 移除群成员
function removeGroupMember(groupId, memberId) {
if (confirm('确定要移除该成员吗?')) {
fetch('api.php?action=removeGroupMember', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'groupId=' + encodeURIComponent(groupId) + '&userId=' + encodeURIComponent(memberId)
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('成员移除成功', 'success');
// 重新加载群组成员
loadGroupMembers(groupId);
} else {
showNotification('移除成员失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('移除成员失败:', error);
showNotification('移除成员失败', 'error');
});
}
}
// 保存群组设置
if (saveGroupSettingsBtn) {
saveGroupSettingsBtn.addEventListener('click', function() {
if (!currentGroup) return;
const newGroupName = groupSettingsNameInput.value.trim();
if (!newGroupName) {
groupNameResult.innerHTML = '<div class="error">群组名称不能为空</div>';
return;
}
// 更新群组名称
fetch('api.php?action=updateGroup', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'groupId=' + encodeURIComponent(currentGroup.id) + '&groupName=' + encodeURIComponent(newGroupName)
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('群组名称更新成功', 'success');
// 更新当前群组信息
currentGroup.name = newGroupName;
// 重新打开群组聊天窗口以更新界面
openGroupChatWindow(currentGroup);
// 关闭模态框
closeGroupSettingsModal();
} else {
groupNameResult.innerHTML = '<div class="error">' + data.message + '</div>';
}
})
.catch(error => {
console.error('更新群组名称失败:', error);
groupNameResult.innerHTML = '<div class="error">更新群组名称失败,请稍后重试</div>';
});
});
}
// 关闭群组设置模态框
if (closeGroupSettingsModalBtn) {
closeGroupSettingsModalBtn.addEventListener('click', closeGroupSettingsModal);
}
if (cancelGroupSettingsBtn) {
cancelGroupSettingsBtn.addEventListener('click', closeGroupSettingsModal);
}
// 点击模态框外部关闭
window.addEventListener('click', function(e) {
if (e.target === joinGroupModal) {
closeJoinGroupModal();
}
if (e.target === inviteGroupModal) {
closeInviteGroupModal();
}
if (e.target === groupSettingsModal) {
closeGroupSettingsModal();
}
if (e.target === friendRequestsModal) {
closeFriendRequestsModal();
}
if (e.target === shareGroupModal) {
closeShareGroupModal();
}
});
// 为分享群组模态框的关闭按钮添加事件监听器
if (closeShareGroupModalBtn) {
closeShareGroupModalBtn.addEventListener('click', closeShareGroupModal);
}
if (closeShareGroupBtn) {
closeShareGroupBtn.addEventListener('click', closeShareGroupModal);
}
// 为复制分享链接按钮添加事件监听器
if (copyShareLinkBtn) {
copyShareLinkBtn.addEventListener('click', copyShareLink);
}
// 为下载二维码按钮添加事件监听器
if (downloadQrcodeBtn) {
downloadQrcodeBtn.addEventListener('click', downloadQRCode);
}
// 为刷新二维码按钮添加事件监听器
if (refreshQrcodeBtn) {
refreshQrcodeBtn.addEventListener('click', function() {
const linkValue = document.getElementById('share-link').value;
if (linkValue) {
generateQRCode(linkValue);
showNotification('二维码已刷新', 'success');
} else {
showNotification('请先生成分享链接', 'warning');
}
});
}
// 打开好友请求模态框
if (friendRequestsBtn) {
friendRequestsBtn.addEventListener('click', function() {
openFriendRequestsModal();
});
}
// 打开好友请求模态框
function openFriendRequestsModal() {
friendRequestsModal.classList.add('active');
friendRequestsResult.innerHTML = '';
// 加载好友请求
loadFriendRequests();
}
// 关闭好友请求模态框
function closeFriendRequestsModal() {
friendRequestsModal.classList.remove('active');
}
// 生成二维码
function generateQRCode(shareLink) {
// 清空之前的二维码
qrcodeElement.innerHTML = '';
// 显示加载状态
document.getElementById('qrcode-loading').style.display = 'flex';
// 延迟生成二维码,确保加载状态显示
setTimeout(() => {
try {
// 生成二维码
qrcodeInstance = new QRCode(qrcodeElement, {
text: shareLink,
width: 200,
height: 200,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H
});
} catch (error) {
console.error('生成二维码失败:', error);
shareGroupResult.innerHTML = '<div class="error">生成二维码失败,请稍后重试</div>';
} finally {
// 隐藏加载状态
document.getElementById('qrcode-loading').style.display = 'none';
}
}, 500);
}
// 下载二维码
function downloadQRCode() {
const canvas = qrcodeElement.querySelector('canvas');
if (!canvas) {
showNotification('请先生成二维码', 'warning');
return;
}
try {
// 将canvas转换为图片
const dataURL = canvas.toDataURL('image/png');
// 创建下载链接
const link = document.createElement('a');
link.href = dataURL;
link.download = `group_qrcode_${shareGroupName.textContent}.png`;
link.click();
showNotification('二维码已下载', 'success');
} catch (error) {
console.error('下载二维码失败:', error);
showNotification('下载二维码失败,请稍后重试', 'error');
}
}
// 打开分享群组模态框
function openShareGroupModal(group) {
// 清空结果
shareGroupResult.innerHTML = '';
// 显示群组名称
shareGroupName.textContent = group.name;
// 清空之前的二维码
qrcodeElement.innerHTML = '';
// 获取分享链接
fetch('api.php?action=getGroupShareLink', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'groupId=' + encodeURIComponent(group.id)
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 显示分享链接
shareLink.value = data.shareLink;
// 生成二维码
generateQRCode(data.shareLink);
} else {
shareGroupResult.innerHTML = '<div class="error">' + data.message + '</div>';
}
})
.catch(error => {
console.error('获取分享链接失败:', error);
shareGroupResult.innerHTML = '<div class="error">获取分享链接失败,请稍后重试</div>';
});
// 显示模态框
shareGroupModal.classList.add('active');
}
// 关闭分享群组模态框
function closeShareGroupModal() {
shareGroupModal.classList.remove('active');
}
// 复制分享链接
function copyShareLink() {
shareLink.select();
document.execCommand('copy');
showNotification('链接已复制到剪贴板', 'success');
}
// 加载好友请求
function loadFriendRequests() {
fetch('api.php?action=getFriendRequests')
.then(response => response.json())
.then(data => {
if (data.success) {
friendRequestsList.innerHTML = '';
if (data.requests.length === 0) {
friendRequestsList.innerHTML = '<div style="text-align: center; padding: 20px; color: #999;">暂无好友请求</div>';
} else {
data.requests.forEach(request => {
const requestItem = document.createElement('div');
requestItem.className = 'friend-request-item';
requestItem.innerHTML = `
<div class="request-info">
<div class="avatar" style="width: 40px; height: 40px; margin-right: 12px; cursor: pointer;">
${request.requester_avatar ? `<img src="${request.requester_avatar}" alt="头像" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover;">` : '<i class="fas fa-user-circle"></i>'}
</div>
<div>
<div style="font-weight: 500; margin-bottom: 4px;">${request.requester_username}</div>
<div style="font-size: 12px; color: #999;">请求添加您为好友</div>
</div>
</div>
<div class="request-actions">
<button class="btn btn-primary btn-sm accept-request-btn" data-request-id="${request.id}">接受</button>
<button class="btn btn-cancel btn-sm reject-request-btn" data-request-id="${request.id}">拒绝</button>
</div>
`;
friendRequestsList.appendChild(requestItem);
// 为好友请求中的头像添加点击事件
const requestAvatar = requestItem.querySelector('.avatar');
if (requestAvatar) {
requestAvatar.addEventListener('click', function() {
window.location.href = `profile.php?name=${encodeURIComponent(request.requester_username)}`;
});
}
});
// 为接受按钮添加点击事件
document.querySelectorAll('.accept-request-btn').forEach(btn => {
btn.addEventListener('click', function() {
const requestId = this.getAttribute('data-request-id');
acceptFriendRequest(requestId);
});
});
// 为拒绝按钮添加点击事件
document.querySelectorAll('.reject-request-btn').forEach(btn => {
btn.addEventListener('click', function() {
const requestId = this.getAttribute('data-request-id');
rejectFriendRequest(requestId);
});
});
}
} else {
friendRequestsList.innerHTML = '<div style="text-align: center; padding: 20px; color: #ff4d4f;">加载好友请求失败</div>';
}
})
.catch(error => {
console.error('加载好友请求失败:', error);
friendRequestsList.innerHTML = '<div style="text-align: center; padding: 20px; color: #ff4d4f;">加载好友请求失败</div>';
});
}
// 接受好友请求
function acceptFriendRequest(requestId) {
fetch('api.php?action=acceptFriendRequest', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'requestId=' + encodeURIComponent(requestId)
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('好友请求已接受', 'success');
// 重新加载好友请求列表
loadFriendRequests();
// 重新加载好友列表
loadFriendsList();
// 更新请求徽章
updateRequestBadge();
} else {
showNotification('接受好友请求失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('接受好友请求失败:', error);
showNotification('接受好友请求失败', 'error');
});
}
// 拒绝好友请求
function rejectFriendRequest(requestId) {
fetch('api.php?action=rejectFriendRequest', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'requestId=' + encodeURIComponent(requestId)
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('好友请求已拒绝', 'success');
// 重新加载好友请求列表
loadFriendRequests();
// 更新请求徽章
updateRequestBadge();
} else {
showNotification('拒绝好友请求失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('拒绝好友请求失败:', error);
showNotification('拒绝好友请求失败', 'error');
});
}
// 更新好友请求徽章
function updateRequestBadge() {
fetch('api.php?action=getFriendRequests')
.then(response => response.json())
.then(data => {
if (data.success) {
const requestCount = data.requests.length;
if (requestCount > 0) {
requestBadge.style.display = 'flex';
requestBadge.textContent = requestCount;
} else {
requestBadge.style.display = 'none';
}
}
})
.catch(error => {
console.error('更新好友请求徽章失败:', error);
});
}
// 关闭好友请求模态框
if (closeFriendRequestsModalBtn) {
closeFriendRequestsModalBtn.addEventListener('click', closeFriendRequestsModal);
}
if (cancelFriendRequestsBtn) {
cancelFriendRequestsBtn.addEventListener('click', closeFriendRequestsModal);
}
// 在群组聊天头部添加加入和邀请按钮
// 注意:这里需要在openGroupChatWindow函数中添加相应的UI元素
// 打开创建群组模态框
function openCreateGroupModal() {
const createGroupModal = document.getElementById('create-group-modal');
const closeGroupModalBtn = document.getElementById('close-group-modal');
const cancelCreateGroupBtn = document.getElementById('cancel-create-group');
const confirmCreateGroupBtn = document.getElementById('confirm-create-group');
const groupNameInput = document.getElementById('group-name');
const friendsListForGroup = document.getElementById('friends-list-for-group');
const createGroupResult = document.getElementById('create-group-result');
// 清空输入框
groupNameInput.value = '';
createGroupResult.innerHTML = '';
// 加载好友列表供选择
fetch('api.php?action=getFriends')
.then(response => response.json())
.then(data => {
friendsListForGroup.innerHTML = '';
if (data.friends && data.friends.length > 0) {
data.friends.forEach(friend => {
const displayName = friend.note || friend.username;
const friendItem = document.createElement('div');
friendItem.className = 'friend-checkbox-item';
friendItem.innerHTML = `
<input type="checkbox" value="${friend.id}" id="friend-${friend.id}">
<label for="friend-${friend.id}">${displayName}${friend.note ? ` (${friend.username})` : ''}</label>
`;
friendsListForGroup.appendChild(friendItem);
});
} else {
friendsListForGroup.innerHTML = '<div style="padding: 10px; color: #999;">暂无好友</div>';
}
});
// 显示模态框
createGroupModal.classList.add('active');
// 关闭模态框
function closeCreateGroupModal() {
createGroupModal.classList.remove('active');
}
closeGroupModalBtn.addEventListener('click', closeCreateGroupModal);
cancelCreateGroupBtn.addEventListener('click', closeCreateGroupModal);
// 确认创建群组
confirmCreateGroupBtn.addEventListener('click', function() {
const groupName = groupNameInput.value.trim();
if (!groupName) {
createGroupResult.innerHTML = '<div class="error">群组名称不能为空</div>';
return;
}
// 获取选中的好友
const selectedFriends = [];
const checkboxes = friendsListForGroup.querySelectorAll('input[type="checkbox"]:checked');
checkboxes.forEach(checkbox => {
selectedFriends.push(checkbox.value);
});
// 发送创建群组请求
fetch('api.php?action=createGroup', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `groupName=${encodeURIComponent(groupName)}&memberIds=${encodeURIComponent(JSON.stringify(selectedFriends))}`
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 关闭模态框
closeCreateGroupModal();
// 切换到群组标签并加载群组列表
// 直接加载群组列表,不需要标签切换
loadGroupsList();
// 显示成功通知
showNotification('群组创建成功', 'success');
} else {
createGroupResult.innerHTML = '<div class="error">' + (data.message || '创建群组失败') + '</div>';
}
})
.catch(error => {
console.error('创建群组失败:', error);
createGroupResult.innerHTML = '<div class="error">创建群组失败,请稍后重试</div>';
});
});
}
// 点击页面其他地方关闭设置菜单
document.addEventListener('click', function() {
settingsMenu.classList.remove('active');
});
// 设置菜单点击事件
document.getElementById('menu-change-username').addEventListener('click', function() {
settingsMenu.classList.remove('active');
changeUsernameModal.classList.add('active');
newUsernameInput.value = '';
usernameError.textContent = '';
});
document.getElementById('menu-change-bio').addEventListener('click', function() {
settingsMenu.classList.remove('active');
changeBioModal.classList.add('active');
newBioInput.value = '';
bioError.textContent = '';
// 可以在这里加载当前简介
});
document.getElementById('menu-change-avatar').addEventListener('click', function() {
settingsMenu.classList.remove('active');
changeAvatarModal.classList.add('active');
previewAvatarImg.src = '';
currentAvatarFile = null;
avatarError.textContent = '';
// 加载当前头像
loadCurrentAvatar();
});
// 为状态设置菜单项添加点击事件
const menuChangeStatus = document.getElementById('menu-change-status');
if (menuChangeStatus) {
menuChangeStatus.addEventListener('click', function() {
settingsMenu.classList.remove('active');
statusSettingsModal.classList.add('active');
statusSettingsError.textContent = '';
statusSettingsSuccess.textContent = '';
// 加载当前状态设置
loadCurrentStatus();
});
}
// 为更改位置菜单项添加点击事件
const menuChangeLocation = document.getElementById('menu-change-location');
if (menuChangeLocation) {
menuChangeLocation.addEventListener('click', function() {
settingsMenu.classList.remove('active');
changeLocationModal.classList.add('active');
newLocationInput.value = '';
locationError.textContent = '';
});
}
// 为用户协议设置菜单项添加点击事件
const menuTermsSettings = document.getElementById('menu-terms-settings');
if (menuTermsSettings) {
menuTermsSettings.addEventListener('click', function() {
settingsMenu.classList.remove('active');
termsSettingsModal.classList.add('active');
termsSettingsError.textContent = '';
termsSettingsSuccess.textContent = '';
// 加载用户当前设置
fetch('api.php?action=getTermsSettings')
.then(response => response.json())
.then(data => {
if (data.success) {
getip.checked = data.getip === 1;
}
})
.catch(error => {
console.error('加载设置失败:', error);
getip.checked = true; // 默认开启
});
});
}
// 关闭更改用户名模态框
function closeUsernameModalFunc() {
changeUsernameModal.classList.remove('active');
newUsernameInput.value = '';
usernameError.textContent = '';
}
closeUsernameModal.addEventListener('click', closeUsernameModalFunc);
cancelUsernameChange.addEventListener('click', closeUsernameModalFunc);
// 点击模态框外部关闭
window.addEventListener('click', function(e) {
if (e.target === changeUsernameModal) {
closeUsernameModalFunc();
}
if (e.target === changeAvatarModal) {
closeAvatarModalFunc();
}
if (e.target === changeBioModal) {
closeBioModalFunc();
}
});
// 关闭更改头像模态框
function closeAvatarModalFunc() {
changeAvatarModal.classList.remove('active');
previewAvatarImg.src = '';
currentAvatarFile = null;
avatarError.textContent = '';
}
closeAvatarModal.addEventListener('click', closeAvatarModalFunc);
cancelAvatarChange.addEventListener('click', closeAvatarModalFunc);
// 关闭更改简介模态框
function closeBioModalFunc() {
changeBioModal.classList.remove('active');
newBioInput.value = '';
bioError.textContent = '';
}
closeBioModal.addEventListener('click', closeBioModalFunc);
cancelBioChange.addEventListener('click', closeBioModalFunc);
// 关闭用户协议设置模态框
function closeTermsSettingsModalFunc() {
termsSettingsModal.classList.remove('active');
termsSettingsError.textContent = '';
termsSettingsSuccess.textContent = '';
}
closeTermsSettingsModal.addEventListener('click', closeTermsSettingsModalFunc);
cancelTermsSettings.addEventListener('click', closeTermsSettingsModalFunc);
// 点击模态框外部关闭
window.addEventListener('click', function(e) {
if (e.target === termsSettingsModal) {
closeTermsSettingsModalFunc();
}
});
// 保存用户协议设置
saveTermsSettings.addEventListener('click', function() {
const getipValue = getip.checked ? 1 : 0;
// 发送请求保存设置
fetch('api.php?action=updateTermsSettings', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'require_terms_agreement=' + getipValue
})
.then(response => response.json())
.then(data => {
if (data.success) {
termsSettingsSuccess.textContent = '设置保存成功';
setTimeout(() => {
closeTermsSettingsModalFunc();
}, 1500);
} else {
termsSettingsError.textContent = data.message || '设置保存失败';
}
})
.catch(error => {
console.error('保存设置失败:', error);
termsSettingsError.textContent = '网络错误,请稍后重试';
});
});
// 选择头像按钮点击事件
chooseAvatarBtn.addEventListener('click', function() {
avatarFile.click();
});
// 文件输入变化事件
avatarFile.addEventListener('change', function(e) {
if (e.target.files.length > 0) {
const file = e.target.files[0];
if (file.type.startsWith('image/')) {
currentAvatarFile = file;
// 预览头像
const reader = new FileReader();
reader.onload = function(e) {
previewAvatarImg.src = e.target.result;
};
reader.readAsDataURL(file);
} else {
avatarError.textContent = '请选择图片文件';
}
}
});
// 加载当前头像
function loadCurrentAvatar() {
fetch('api.php?action=getCurrentAvatar')
.then(response => response.json())
.then(data => {
if (data.success && data.avatar) {
previewAvatarImg.src = data.avatar;
}
})
.catch(error => {
console.error('加载头像失败:', error);
});
}
// 加载当前状态设置
function loadCurrentStatus() {
fetch('api.php?action=getCurrentStatus')
.then(response => response.json())
.then(data => {
if (data.success && data.status) {
// 根据当前状态设置单选按钮
switch (data.status) {
case 'online':
statusOnline.checked = true;
break;
case 'dnd':
statusDnd.checked = true;
break;
case 'invisible':
statusInvisible.checked = true;
break;
}
}
})
.catch(error => {
console.error('加载当前状态设置失败:', error);
});
}
// 保存状态设置
function saveStatus() {
let selectedStatus;
if (statusOnline.checked) {
selectedStatus = 'online';
} else if (statusDnd.checked) {
selectedStatus = 'dnd';
} else if (statusInvisible.checked) {
selectedStatus = 'invisible';
}
fetch('api.php?action=updateStatus', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'status=' + encodeURIComponent(selectedStatus)
})
.then(response => response.json())
.then(data => {
if (data.success) {
statusSettingsSuccess.textContent = '状态设置已保存';
setTimeout(() => {
statusSettingsModal.classList.remove('active');
}, 1000);
} else {
statusSettingsError.textContent = data.message || '保存状态设置失败';
}
})
.catch(error => {
console.error('保存状态设置失败:', error);
statusSettingsError.textContent = '保存状态设置失败,请稍后重试';
});
}
// 为状态设置模态框的关闭按钮添加点击事件
if (closeStatusSettingsModal) {
closeStatusSettingsModal.addEventListener('click', function() {
statusSettingsModal.classList.remove('active');
});
}
// 为状态设置模态框的取消按钮添加点击事件
if (cancelStatusSettings) {
cancelStatusSettings.addEventListener('click', function() {
statusSettingsModal.classList.remove('active');
});
}
// 为状态设置模态框的保存按钮添加点击事件
if (saveStatusSettings) {
saveStatusSettings.addEventListener('click', function() {
statusSettingsError.textContent = '';
statusSettingsSuccess.textContent = '';
saveStatus();
});
}
// 确认更改头像
confirmAvatarChange.addEventListener('click', function() {
if (!currentAvatarFile) {
avatarError.textContent = '请选择头像图片';
return;
}
const formData = new FormData();
formData.append('avatar', currentAvatarFile);
// 发送头像到服务器
fetch('api.php?action=changeAvatar', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 关闭模态框
closeAvatarModalFunc();
// 更新页面上的头像
updateUserAvatar(data.avatar);
// 显示成功通知
showNotification('头像更改成功', 'success');
} else {
avatarError.textContent = data.message || '更改头像失败';
}
})
.catch(error => {
console.error('更改头像失败:', error);
avatarError.textContent = '网络错误,请稍后重试';
});
});
// 确认更改简介
confirmBioChange.addEventListener('click', function() {
const newBio = newBioInput.value.trim();
if (newBio.length > 200) {
bioError.textContent = '简介长度不能超过200个字符';
return;
}
// 发送请求更改简介
fetch('api.php?action=changeBio', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'newBio=' + encodeURIComponent(newBio)
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 关闭模态框
closeBioModalFunc();
// 显示成功通知
showNotification('简介更改成功', 'success');
} else {
bioError.textContent = data.message || '更改简介失败';
}
})
.catch(error => {
console.error('更改简介失败:', error);
bioError.textContent = '网络错误,请稍后重试';
});
});
// 更新页面上的头像
function updateUserAvatar(avatarUrl) {
// 更新侧边栏头像
const sidebarAvatar = document.querySelector('.sidebar-header .avatar');
if (sidebarAvatar) {
sidebarAvatar.innerHTML = `<img src="${avatarUrl}" alt="头像" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover;">`;
}
}
// 确认更改用户名
confirmUsernameChange.addEventListener('click', function() {
const newUsername = newUsernameInput.value.trim();
if (!newUsername) {
usernameError.textContent = '用户名不能为空';
return;
}
if (newUsername.length < 2 || newUsername.length > 20) {
usernameError.textContent = '用户名长度应在2-20个字符之间';
return;
}
// 发送更改用户名请求
fetch('api.php?action=changeUsername', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `newUsername=${encodeURIComponent(newUsername)}`
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 更新页面上的用户名
const userInfoUsername = document.querySelector('.sidebar-header .username');
if (userInfoUsername) {
userInfoUsername.textContent = newUsername;
}
// 关闭模态框
closeUsernameModalFunc();
// 显示成功通知
showNotification('用户名更改成功', 'success');
} else {
usernameError.textContent = data.message || '更改用户名失败';
}
})
.catch(error => {
console.error('更改用户名失败:', error);
usernameError.textContent = '网络错误,请稍后重试';
});
});
// 文件输入变化事件
fileInput.addEventListener('change', function(e) {
if (e.target.files.length > 0) {
const file = e.target.files[0];
if (file.type.startsWith('image/')) {
currentImageFile = file;
// 预览图片
const reader = new FileReader();
reader.onload = function(e) {
previewImage.src = e.target.result;
imagePreviewModal.classList.add('active');
};
reader.readAsDataURL(file);
} else {
showNotification('请选择图片文件', 'error');
}
}
});
// 关闭图片预览
function closeImagePreviewModal() {
imagePreviewModal.classList.remove('active');
currentImageFile = null;
fileInput.value = '';
}
closeImagePreview.addEventListener('click', closeImagePreviewModal);
cancelImageSend.addEventListener('click', closeImagePreviewModal);
// 点击模态框外部关闭
window.addEventListener('click', function(e) {
if (e.target === imagePreviewModal) {
closeImagePreviewModal();
}
});
// 确认更改位置
confirmLocationChange.addEventListener('click', function() {
const newLocation = newLocationInput.value.trim();
if (!newLocation) {
locationError.textContent = '位置不能为空';
return;
}
// 发送更改位置请求
fetch('api.php?action=changeLocation', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'newLocation=' + encodeURIComponent(newLocation)
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 关闭模态框
closeLocationModalFunc();
// 显示成功通知
showNotification('位置更改成功', 'success');
} else {
locationError.textContent = data.message || '更改位置失败';
}
})
.catch(error => {
console.error('更改位置失败:', error);
locationError.textContent = '网络错误,请稍后重试';
});
});
// 发送图片
sendImageBtn.addEventListener('click', function() {
if (!currentImageFile || (!currentFriend && !currentGroup)) return;
const isGroup = !!currentGroup;
const targetId = isGroup ? currentGroup.id : currentFriend.id;
const formData = new FormData();
formData.append('targetId', targetId);
formData.append('image', currentImageFile);
formData.append('isGroup', isGroup);
// 发送图片到服务器
fetch('api.php?action=sendImage', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 关闭预览
closeImagePreviewModal();
// 更新聊天历史 - 发送图片时不滚动到底部
loadChatHistory(targetId, false, false, isGroup);
// 更新列表
if (isGroup) {
loadGroupsList();
} else {
loadFriendsList();
}
// 清除消息状态缓存,避免自己发送的图片触发新消息提示
const cacheKey = `${targetId}_${isGroup ? 'group' : 'friend'}`;
messageStatusCache[cacheKey] = null;
showNotification('图片发送成功', 'success');
} else {
showNotification('图片发送失败: ' + (data.message || '未知错误'), 'error');
}
})
.catch(error => {
console.error('发送图片失败:', error);
showNotification('图片发送失败', 'error');
});
});
// 打开聊天窗口
function openChatWindow(friend) {
// 清除当前群组
currentGroup = null;
// 设置当前好友
currentFriend = friend;
// 更新URL参数
const url = new URL(window.location.href);
url.searchParams.set('chatid', friend.id);
url.searchParams.set('chattype', 'friend');
window.history.pushState({}, '', url.toString());
const chatHeader = document.getElementById('chat-header');
const chatUsername = document.getElementById('chat-username');
const chatContent = document.getElementById('chat-content');
const chatInputContainer = document.getElementById('chat-input-container');
const chatUserInfo = document.querySelector('.chat-user-info');
const chatActions = document.querySelector('.chat-actions');
// 构建头像HTML
let avatarHtml = '';
if (friend.avatar) {
avatarHtml = `<img src="${friend.avatar}" alt="头像" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover;">`;
} else {
avatarHtml = '<i class="fas fa-user-circle"></i>';
}
// 更新聊天头部信息
const displayName = friend.note || friend.username;
chatUserInfo.innerHTML = `
<div class="avatar" style="cursor: pointer;">
${avatarHtml}
</div>
<div class="username" id="chat-username">
${displayName}
${friend.note ? `<span style="font-size: 12px; color: var(--text-tertiary);">(${friend.username})</span>` : ''}
${friend.online ? '<span class="online-status-text">在线</span>' : ''}
</div>
`;
// 为聊天头部头像添加点击事件,跳转到个人主页
const chatHeaderAvatar = chatUserInfo.querySelector('.avatar');
if (chatHeaderAvatar) {
chatHeaderAvatar.addEventListener('click', function() {
window.location.href = `profile.php?name=${encodeURIComponent(friend.username)}`;
});
}
// 重置聊天头部操作按钮
chatActions.innerHTML = `
<button class="action-btn" id="search-chat-btn">
<i class="fas fa-search"></i>
</button>
<div class="dropdown-menu-container">
<button class="action-btn" id="friend-menu-btn">
<i class="fas fa-ellipsis-v"></i>
</button>
<div class="dropdown-menu" id="friend-menu">
<ul>
<li id="friend-menu-set-note">
<i class="fas fa-sticky-note"></i>
<span>设置备注</span>
</li>
</ul>
</div>
</div>
`;
// 为搜索按钮重新添加点击事件
const searchChatBtn = document.getElementById('search-chat-btn');
if (searchChatBtn) {
searchChatBtn.addEventListener('click', function() {
if (!currentFriend && !currentGroup) {
showNotification('请先选择一个好友或群组', 'warning');
return;
}
// 显示/隐藏搜索栏
if (chatSearch.style.display === 'none') {
chatSearch.style.display = 'block';
searchChatInput.focus();
} else {
chatSearch.style.display = 'none';
searchResults.style.display = 'none';
searchChatInput.value = '';
}
});
}
// 为好友菜单按钮添加点击事件
const friendMenuBtn = document.getElementById('friend-menu-btn');
const friendMenu = document.getElementById('friend-menu');
if (friendMenuBtn && friendMenu) {
friendMenuBtn.addEventListener('click', function(e) {
e.stopPropagation();
friendMenu.classList.toggle('active');
// 点击其他地方关闭菜单
document.addEventListener('click', function closeMenu() {
friendMenu.classList.remove('active');
document.removeEventListener('click', closeMenu);
});
});
// 为设置备注菜单项添加点击事件
const friendMenuSetNote = document.getElementById('friend-menu-set-note');
if (friendMenuSetNote) {
friendMenuSetNote.addEventListener('click', function() {
openSetNoteModal(friend.id, friend.username, 'friend');
friendMenu.classList.remove('active');
});
}
}
// 显示聊天输入框
chatInputContainer.style.display = 'block';
// 绑定工具按钮事件
bindToolButtonEvents();
// 清空聊天内容并加载历史消息
chatContent.innerHTML = '';
loadChatHistory(friend.id, true, false, false);
// 初始化消息状态缓存 - 首次加载后会在checkForNewMessages中更新
const cacheKey = `${friend.id}_friend`;
messageStatusCache[cacheKey] = null;
}
// 打开群组聊天窗口
function openGroupChatWindow(group) {
// 清除当前好友
currentFriend = null;
// 设置当前群组
currentGroup = group;
// 获取群成员信息
fetch('api.php?action=getGroupMembers&groupId=' + group.id)
.then(response => response.json())
.then(data => {
if (data.success && data.members) {
// 存储群成员信息到group对象
group.members = data.members;
// 同时更新groups数组中的对应群组
const groupIndex = groups.findIndex(g => g.id == group.id);
if (groupIndex !== -1) {
groups[groupIndex].members = data.members;
}
}
})
.catch(error => {
console.error('获取群成员失败:', error);
});
// 更新URL参数
const url = new URL(window.location.href);
url.searchParams.set('chatid', group.id);
url.searchParams.set('chattype', 'group');
window.history.pushState({}, '', url.toString());
const chatHeader = document.getElementById('chat-header');
const chatContent = document.getElementById('chat-content');
const chatInputContainer = document.getElementById('chat-input-container');
const chatUserInfo = document.querySelector('.chat-user-info');
const chatActions = document.querySelector('.chat-actions');
// 构建头像HTML
let avatarHtml = '';
if (group.avatar) {
avatarHtml = `<img src="${group.avatar}" alt="群组头像" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover;">`;
} else {
avatarHtml = '<i class="fas fa-users"></i>';
}
// 更新聊天头部信息
chatUserInfo.innerHTML = `
<div class="avatar">
${avatarHtml}
</div>
<div class="username" id="chat-username">
${group.name}
<span style="font-size: 12px; color: #999; font-weight: normal;">(${group.role})</span>
<span style="font-size: 11px; color: #999; font-weight: normal; display: block;">群号: ${group.group_id}</span>
</div>
`;
// 更新聊天头部操作按钮
let actionsHtml = `
<button class="action-btn" id="search-chat-btn">
<i class="fas fa-search"></i>
</button>
<div class="dropdown-menu-container">
<button class="action-btn" id="group-menu-btn">
<i class="fas fa-ellipsis-v"></i>
</button>
<div class="dropdown-menu" id="group-menu">
<ul>
<li id="group-menu-set-note">
<i class="fas fa-sticky-note"></i>
<span>设置备注</span>
</li>
<li id="group-menu-announcement">
<i class="fas fa-bullhorn"></i>
<span>群公告</span>
</li>
<li id="group-menu-invite">
<i class="fas fa-user-plus"></i>
<span>邀请成员</span>
</li>
<li id="group-menu-share">
<i class="fas fa-share-alt"></i>
<span>分享群组</span>
</li>
${group.role === 'admin' ? `
<li id="group-menu-settings">
<i class="fas fa-cog"></i>
<span>群组设置</span>
</li>
` : ''}
<li class="divider"></li>
<li id="group-menu-leave">
<i class="fas fa-sign-out-alt"></i>
<span>退出群组</span>
</li>
</ul>
</div>
</div>
`;
chatActions.innerHTML = actionsHtml;
// 为群组菜单按钮添加点击事件
const groupMenuBtn = document.getElementById('group-menu-btn');
const groupMenu = document.getElementById('group-menu');
if (groupMenuBtn && groupMenu) {
groupMenuBtn.addEventListener('click', function(e) {
e.stopPropagation();
groupMenu.classList.toggle('active');
});
// 为设置备注菜单项添加点击事件
const groupMenuSetNote = document.getElementById('group-menu-set-note');
if (groupMenuSetNote) {
groupMenuSetNote.addEventListener('click', function() {
openSetNoteModal(group.id, group.name, 'group');
groupMenu.classList.remove('active');
});
}
// 为群公告菜单项添加点击事件
const groupMenuAnnouncement = document.getElementById('group-menu-announcement');
if (groupMenuAnnouncement) {
groupMenuAnnouncement.addEventListener('click', function() {
openGroupAnnouncementModal(group);
groupMenu.classList.remove('active');
});
}
// 为邀请成员菜单项添加点击事件
const groupMenuInvite = document.getElementById('group-menu-invite');
if (groupMenuInvite) {
groupMenuInvite.addEventListener('click', function() {
openInviteGroupModal();
groupMenu.classList.remove('active');
});
}
// 为分享群组菜单项添加点击事件
const groupMenuShare = document.getElementById('group-menu-share');
if (groupMenuShare) {
groupMenuShare.addEventListener('click', function() {
openShareGroupModal(group);
groupMenu.classList.remove('active');
});
}
// 为群组设置菜单项添加点击事件(仅管理员可见)
const groupMenuSettings = document.getElementById('group-menu-settings');
if (groupMenuSettings) {
groupMenuSettings.addEventListener('click', function() {
openGroupSettingsModal(group);
groupMenu.classList.remove('active');
});
}
// 为退出群组菜单项添加点击事件
const groupMenuLeave = document.getElementById('group-menu-leave');
if (groupMenuLeave) {
groupMenuLeave.addEventListener('click', function() {
// 检查用户是否是管理员
if (group.role === 'admin') {
showNotification('管理员不可退出群聊', 'warning');
groupMenu.classList.remove('active');
return;
}
if (confirm('确定要退出该群组吗?')) {
fetch('api.php?action=leaveGroup', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'groupId=' + encodeURIComponent(group.id)
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('已成功退出群组', 'success');
// 关闭群组菜单
groupMenu.classList.remove('active');
// 清空当前群组
currentGroup = null;
// 切换到好友标签
// 直接加载好友列表,不需要标签切换
loadFriendsList();
// 清空聊天内容
const chatContent = document.getElementById('chat-content');
const chatInputContainer = document.getElementById('chat-input-container');
chatContent.innerHTML = '<div class="empty-chat"><i class="fas fa-comments"></i><p>选择一个好友开始聊天</p></div>';
chatInputContainer.style.display = 'none';
} else {
showNotification('退出群组失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('退出群组失败:', error);
showNotification('退出群组失败', 'error');
});
}
groupMenu.classList.remove('active');
});
}
// 阻止下拉菜单内部点击事件冒泡
groupMenu.addEventListener('click', function(e) {
e.stopPropagation();
});
}
// 为搜索按钮重新添加点击事件
const searchChatBtn = document.getElementById('search-chat-btn');
if (searchChatBtn) {
searchChatBtn.addEventListener('click', function() {
if (!currentFriend && !currentGroup) {
showNotification('请先选择一个好友或群组', 'warning');
return;
}
// 显示/隐藏搜索栏
if (chatSearch.style.display === 'none') {
chatSearch.style.display = 'block';
searchChatInput.focus();
} else {
chatSearch.style.display = 'none';
searchResults.style.display = 'none';
searchChatInput.value = '';
}
});
}
// 显示聊天输入框
chatInputContainer.style.display = 'block';
// 绑定工具按钮事件
bindToolButtonEvents();
// 清空聊天内容并加载历史消息
chatContent.innerHTML = '';
// 清空已处理链接集合,确保新聊天中的链接能被处理
processedLinks.clear();
loadChatHistory(group.id, true, false, true);
// 加载并显示群公告
loadAndDisplayGroupAnnouncement(group.id);
// 初始化消息状态缓存 - 首次加载后会在checkForNewMessages中更新
const cacheKey = `${group.id}_group`;
messageStatusCache[cacheKey] = null;
}
// 将文本中的URL转换为可点击的链接
function textToLinks(text) {
if (!text) return '';
// 匹配URL的正则表达式
const urlRegex = /(https?:\/\/[\w\-._~:/?#[\]@!$&'()*+,;=.]+)/gi;
// 替换URL为可点击的链接
return text.replace(urlRegex, function(url) {
return '<a href="' + url + '" target="_blank" rel="noopener noreferrer" style="color: #1890ff; text-decoration: underline;">' + url + '</a>';
});
}
// 加载并显示群公告
function loadAndDisplayGroupAnnouncement(groupId) {
// 隐藏公告显示区域
if (groupAnnouncement) {
groupAnnouncement.style.display = 'none';
}
// 获取群组公告
fetch('api.php?action=getGroupAnnouncements&groupId=' + encodeURIComponent(groupId))
.then(response => response.json())
.then(data => {
if (data.success && data.announcements.length > 0) {
// 获取最新的公告
const latestAnnouncement = data.announcements[0];
// 显示公告
if (groupAnnouncement && announcementContent && announcementTime) {
// 将公告内容中的链接转换为可点击的链接
announcementContent.innerHTML = textToLinks(latestAnnouncement.content);
announcementTime.textContent = '发布时间: ' + formatTimestamp(latestAnnouncement.created_at);
groupAnnouncement.style.display = 'block';
// 为关闭公告按钮添加点击事件
if (closeAnnouncementBtn) {
closeAnnouncementBtn.addEventListener('click', function() {
groupAnnouncement.style.display = 'none';
});
}
}
}
})
.catch(error => {
console.error('加载群公告失败:', error);
});
}
// 搜索聊天记录功能
const searchChatBtn = document.getElementById('search-chat-btn');
const chatSearch = document.getElementById('chat-search');
const searchChatInput = document.getElementById('search-chat-input');
const closeSearchBtn = document.querySelector('.close-search-btn');
const searchResults = document.getElementById('search-results');
const searchList = document.getElementById('search-list');
const searchCount = document.getElementById('search-count');
// 聊天记录缓存
let chatHistoryCache = {};
// 搜索按钮点击事件
if (searchChatBtn) {
searchChatBtn.addEventListener('click', function() {
if (!currentFriend && !currentGroup) {
showNotification('请先选择一个好友或群组', 'warning');
return;
}
// 显示/隐藏搜索栏
if (chatSearch.style.display === 'none') {
chatSearch.style.display = 'block';
searchChatInput.focus();
} else {
chatSearch.style.display = 'none';
searchResults.style.display = 'none';
searchChatInput.value = '';
}
});
}
// 关闭搜索按钮点击事件
if (closeSearchBtn) {
closeSearchBtn.addEventListener('click', function() {
chatSearch.style.display = 'none';
searchResults.style.display = 'none';
searchChatInput.value = '';
});
}
// 搜索输入事件
if (searchChatInput) {
searchChatInput.addEventListener('input', function() {
const keyword = this.value.trim();
if (!keyword) {
searchResults.style.display = 'none';
return;
}
if (currentFriend || currentGroup) {
const isGroup = !!currentGroup;
const targetId = isGroup ? currentGroup.id : currentFriend.id;
searchChatHistory(targetId, keyword, isGroup);
}
});
}
// 搜索聊天历史
function searchChatHistory(targetId, keyword, isGroup = false) {
// 检查缓存
const cacheKey = `${targetId}_${isGroup ? 'group' : 'friend'}`;
if (!chatHistoryCache[cacheKey]) {
// 加载聊天历史到缓存
fetch(`api.php?action=getChatHistory&targetId=${targetId}&isGroup=${isGroup}`)
.then(response => response.json())
.then(data => {
if (data.messages) {
chatHistoryCache[cacheKey] = data.messages;
performSearch(cacheKey, keyword, isGroup);
}
})
.catch(error => {
console.error('加载聊天历史失败:', error);
});
} else {
performSearch(cacheKey, keyword, isGroup);
}
}
// 执行搜索
function performSearch(cacheKey, keyword, isGroup = false) {
const messages = chatHistoryCache[cacheKey];
const results = [];
// 搜索消息
messages.forEach(message => {
if (!message.recalled && message.content && message.content.includes(keyword)) {
results.push(message);
}
});
// 显示搜索结果
displaySearchResults(results, keyword, isGroup);
}
// 显示搜索结果
function displaySearchResults(results, keyword, isGroup = false) {
searchList.innerHTML = '';
searchCount.textContent = results.length;
if (results.length === 0) {
searchList.innerHTML = '<div style="padding: 20px; text-align: center; color: #999;">未找到匹配的消息</div>';
} else {
results.forEach(message => {
const searchItem = document.createElement('div');
searchItem.className = 'search-item';
// 高亮关键字
const highlightedContent = message.content.replace(new RegExp(keyword, 'gi'), '<span class="search-highlight">$&</span>');
// 确定发送者名称
let senderName = message.sender === 'me' ? '我' : '';
if (!senderName) {
if (isGroup && message.sender_name) {
senderName = message.sender_name;
} else if (currentFriend) {
senderName = currentFriend.username;
} else {
senderName = '未知';
}
}
searchItem.innerHTML = `
<div class="search-item-content ${message.sender === 'me' ? 'sent' : 'received'}">
${senderName}: ${highlightedContent}
</div>
<div class="search-item-time">${message.time}</div>
`;
// 点击搜索结果定位到消息
searchItem.addEventListener('click', function() {
// 关闭搜索栏
chatSearch.style.display = 'none';
searchResults.style.display = 'none';
searchChatInput.value = '';
// 滚动到消息位置
scrollToMessage(message.id);
});
searchList.appendChild(searchItem);
});
}
searchResults.style.display = 'block';
}
// 滚动到指定消息
function scrollToMessage(messageId) {
// 这里需要根据实际情况实现滚动逻辑
// 简单起见,我们可以重新加载聊天历史并滚动到底部
if (currentFriend) {
loadChatHistory(currentFriend.id);
}
}
// 定期更新好友列表和消息(轮询)
setInterval(function() {
// 只在必要时加载好友列表(每10秒一次)
if (Date.now() % 10000 < 2000) {
loadFriendsList();
}
if (currentFriend) {
const cacheKey = `${currentFriend.id}_friend`;
// 只有当缓存中已经有哈希值时才检查新消息,避免首次加载时误报
if (messageStatusCache[cacheKey] !== undefined && messageStatusCache[cacheKey] !== null) {
// 轮询更新时不滚动到底部,使用消息对比
checkForNewMessages(currentFriend.id, false);
}
} else if (currentGroup) {
const cacheKey = `${currentGroup.id}_group`;
// 只有当缓存中已经有哈希值时才检查新消息,避免首次加载时误报
if (messageStatusCache[cacheKey] !== undefined && messageStatusCache[cacheKey] !== null) {
// 轮询更新群组消息时不滚动到底部,使用消息对比
checkForNewMessages(currentGroup.id, true);
}
}
// 只在必要时更新好友请求徽章(每5秒一次)
if (Date.now() % 5000 < 2000) {
updateRequestBadge();
}
// 只在必要时更新在线状态(每15秒一次)
if (Date.now() % 15000 < 2000) {
updateOnlineStatus(true);
}
}, 60000); // 每3秒检查一次,但根据时间戳决定是否执行具体操作
// 检查是否有新消息
function checkForNewMessages(targetId, isGroup) {
// 构建请求参数 - 只获取最新的10条消息进行对比,减少数据传输
const params = new URLSearchParams({
targetId: targetId,
page: 1,
limit: 10, // 只获取最新的10条消息
isGroup: isGroup
});
// 检查是否正在进行相同的请求
const requestKey = `checkMessages_${targetId}_${isGroup}`;
if (ongoingRequests[requestKey]) {
return; // 避免重复请求
}
ongoingRequests[requestKey] = true;
fetch(`api.php?action=getChatHistory&${params.toString()}`)
.then(response => response.json())
.then(data => {
if (data.messages && data.messages.length > 0) {
// 生成消息状态哈希
const messageHash = generateMessageHash(data.messages);
const cacheKey = `${targetId}_${isGroup ? 'group' : 'friend'}`;
// 检查是否有新消息
// 只有当缓存中已经有哈希值时才进行对比,避免首次加载时误判
if (messageStatusCache[cacheKey] !== undefined && messageStatusCache[cacheKey] !== null && messageStatusCache[cacheKey] !== messageHash) {
// 有新消息,显示提示但不滚动
showNewMessageNotification(isGroup ? '群组' : '好友');
// 自动更新聊天区的内容,但不滚动
if ((currentFriend && currentFriend.id === targetId) || (currentGroup && currentGroup.id === targetId)) {
loadChatHistory(targetId, false, false, isGroup);
}
}
// 无论是否有新消息,都更新消息状态缓存
messageStatusCache[cacheKey] = messageHash;
}
})
.catch(error => {
console.error('检查新消息失败:', error);
})
.finally(() => {
// 标记请求完成
delete ongoingRequests[requestKey];
});
}
// 生成消息状态哈希
function generateMessageHash(messages) {
// 使用消息ID和时间戳生成哈希,忽略状态字段
const messageIds = messages.map(msg => {
return msg.id + msg.timestamp + (msg.content || '') + (msg.is_image ? '1' : '0');
}).join('|');
return btoa(messageIds);
}
// 显示新消息通知
function showNewMessageNotification(senderType) {
// 检查是否已经有通知
if (document.querySelector('.new-message-notification')) {
return;
}
// 创建通知元素
const notification = document.createElement('div');
notification.className = 'new-message-notification';
notification.innerHTML = `
<span>有新消息</span>
<button class="refresh-messages-btn">查看</button>
`;
// 添加样式
notification.style.cssText = `
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: #07c160;
color: white;
padding: 10px 20px;
border-radius: 20px;
display: flex;
align-items: center;
gap: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
z-index: 9999;
animation: slideIn 0.3s ease;
`;
// 添加到页面
document.body.appendChild(notification);
// 查看按钮点击事件
const refreshBtn = notification.querySelector('.refresh-messages-btn');
refreshBtn.style.cssText = `
background: white;
color: #07c160;
border: none;
padding: 5px 10px;
border-radius: 15px;
cursor: pointer;
font-size: 12px;
font-weight: bold;
`;
refreshBtn.addEventListener('click', function() {
// 刷新当前聊天的消息
if (currentFriend) {
loadChatHistory(currentFriend.id, false);
} else if (currentGroup) {
loadChatHistory(currentGroup.id, false, false, true);
}
// 移除通知
notification.remove();
});
// 3秒后自动隐藏
setTimeout(() => {
if (notification.parentNode) {
notification.style.animation = 'slideOut 0.3s ease';
setTimeout(() => {
notification.remove();
}, 300);
}
}, 3000);
}
// 更新在线状态
function updateOnlineStatus(online) {
fetch('api.php?action=updateOnlineStatus', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `online=${online ? '1' : '0'}`
})
.catch(error => {
console.error('更新在线状态失败:', error);
});
}
// 页面加载时更新在线状态
updateOnlineStatus(true);
// 页面加载时加载用户头像
loadUserAvatar();
// 页面加载时更新好友请求徽章
updateRequestBadge();
// 加载用户头像
function loadUserAvatar() {
fetch('api.php?action=getCurrentAvatar')
.then(response => response.json())
.then(data => {
if (data.success && data.avatar) {
updateUserAvatar(data.avatar);
}
})
.catch(error => {
console.error('加载头像失败:', error);
});
}
// 创建右键菜单函数
function createContextMenu() {
let contextMenu = document.getElementById('message-context-menu');
if (!contextMenu) {
contextMenu = document.createElement('div');
contextMenu.id = 'message-context-menu';
contextMenu.className = 'context-menu';
contextMenu.innerHTML = `
<ul>
<li id="menu-mention">@他</li>
<li id="menu-recall">撤回消息</li>
<li id="menu-quote">引用消息</li>
<li id="menu-share">分享消息</li>
<li id="menu-report">举报消息</li>
</ul>
`;
document.body.appendChild(contextMenu);
}
return contextMenu;
}
// 右键菜单
const contextMenu = createContextMenu();
// 右键菜单相关变量
window.currentMessageId = null;
window.currentMessageElement = null;
// 为消息添加右键点击事件
document.addEventListener('contextmenu', function(e) {
// 检查是否点击在消息上
const messageElement = e.target.closest('.message');
if (messageElement) {
e.preventDefault();
// 检查消息是否已经被撤回
const messageText = messageElement.querySelector('.message-text');
if (messageText && messageText.textContent === '消息已撤回') {
return; // 已撤回的消息不显示右键菜单
}
// 存储当前消息信息
window.currentMessageElement = messageElement;
// 显示右键菜单
contextMenu.style.top = e.pageY + 'px';
contextMenu.style.left = e.pageX + 'px';
contextMenu.classList.add('active');
// 根据消息类型显示不同的菜单项
const menuRecall = document.getElementById('menu-recall');
const isSent = messageElement.classList.contains('sent');
menuRecall.style.display = isSent ? 'block' : 'none';
}
});
// 点击页面其他地方关闭右键菜单
document.addEventListener('click', function() {
contextMenu.classList.remove('active');
currentMessageId = null;
currentMessageElement = null;
});
// @他功能
const menuMention = document.getElementById('menu-mention');
if (menuMention) {
menuMention.addEventListener('click', function() {
if (window.currentMessageElement && messageInput) {
// 检查是否是群组消息
const isGroup = !!currentGroup;
if (isGroup) {
// 群组消息:获取发送者名称
const messageSender = window.currentMessageElement.querySelector('.message-sender');
if (messageSender) {
const senderName = messageSender.textContent.trim();
if (senderName) {
messageInput.value += '@' + senderName + ' ';
messageInput.focus();
}
}
} else if (currentFriend) {
// 私聊消息:获取好友名称
const friendName = currentFriend.username;
if (friendName) {
messageInput.value += '@' + friendName + ' ';
messageInput.focus();
}
}
// 关闭右键菜单
contextMenu.classList.remove('active');
}
});
}
// 撤回消息
const menuRecall = document.getElementById('menu-recall');
if (menuRecall) {
menuRecall.addEventListener('click', function() {
if (window.currentMessageElement && (currentFriend || currentGroup)) {
// 获取消息内容
const messageText = window.currentMessageElement.querySelector('.message-text');
const messageImage = window.currentMessageElement.querySelector('.message-image');
if (messageText || messageImage) {
// 构建消息内容
let content = '';
if (messageText) {
content = messageText.textContent;
} else if (messageImage) {
// 提取图片的相对路径
const imgSrc = messageImage.querySelector('img').src;
// 提取uploads/开头的部分
const relativePathMatch = imgSrc.match(/(uploads\/[^\/]+\.[^\/]+)$/);
if (relativePathMatch) {
content = relativePathMatch[1];
} else {
content = imgSrc;
}
}
// 确定是好友还是群组
const isGroup = !!currentGroup;
const targetId = isGroup ? currentGroup.id : currentFriend.id;
// 获取消息ID
const messageId = window.currentMessageElement.getAttribute('data-message-id');
// 发送撤回请求
fetch('api.php?action=recallMessage', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `targetId=${targetId}&content=${encodeURIComponent(content)}&messageId=${messageId}&isGroup=${isGroup}`
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 更新消息显示为已撤回
if (messageText) {
messageText.textContent = '消息已撤回';
messageText.style.color = '#999';
messageText.style.fontStyle = 'italic';
} else if (messageImage) {
messageImage.innerHTML = '<div style="color: #999; font-style: italic; padding: 10px; text-align: center;">图片已撤回</div>';
}
// 关闭右键菜单
contextMenu.classList.remove('active');
// 显示成功通知
showNotification('消息已撤回', 'success');
} else {
showNotification('撤回消息失败: ' + (data.message || '未知错误'), 'error');
}
})
.catch(error => {
console.error('撤回消息失败:', error);
showNotification('撤回消息失败', 'error');
});
}
}
});
}
// 引用消息
const menuQuote = document.getElementById('menu-quote');
if (menuQuote) {
menuQuote.addEventListener('click', function() {
if (window.currentMessageElement && (currentFriend || currentGroup)) {
// 获取消息内容
const messageText = window.currentMessageElement.querySelector('.message-text');
const messageImage = window.currentMessageElement.querySelector('.message-image');
if (messageText || messageImage) {
// 构建引用内容
let quoteContent = '';
if (messageText) {
quoteContent = messageText.textContent;
} else if (messageImage) {
quoteContent = '[图片]';
}
// 获取消息发送者
const isSent = window.currentMessageElement.classList.contains('sent');
let senderName = '';
if (isSent) {
senderName = '我';
} else if (currentGroup) {
// 在群组中,获取消息发送者的名称
const messageSenderElement = window.currentMessageElement.querySelector('.message-sender');
senderName = messageSenderElement ? messageSenderElement.textContent : '群成员';
} else {
senderName = currentFriend.username;
}
// 获取原消息的id
const messageId = window.currentMessageElement.getAttribute('data-message-id');
// 显示引用输入框
showQuoteInput(senderName, quoteContent, messageId);
// 关闭右键菜单
contextMenu.classList.remove('active');
}
}
});
}
// 分享消息
const menuShare = document.getElementById('menu-share');
if (menuShare) {
menuShare.addEventListener('click', function() {
if (window.currentMessageElement && (currentFriend || currentGroup)) {
// 获取消息内容
const messageText = window.currentMessageElement.querySelector('.message-text');
const messageImage = window.currentMessageElement.querySelector('.message-image');
const messageTime = window.currentMessageElement.querySelector('.message-time');
if (messageText || messageImage) {
// 构建分享内容
let shareContent = '';
// 获取消息发送者
const isSent = window.currentMessageElement.classList.contains('sent');
let senderName = '';
if (isSent) {
senderName = '我';
} else if (currentGroup) {
// 在群组中,获取消息发送者的名称
const messageSenderElement = window.currentMessageElement.querySelector('.message-sender');
senderName = messageSenderElement ? messageSenderElement.textContent : '群成员';
} else {
senderName = currentFriend.username;
}
// 获取消息内容
let messageContent = '';
if (messageText) {
messageContent = messageText.textContent;
} else if (messageImage) {
messageContent = '[图片]';
}
// 获取消息时间
const time = messageTime ? messageTime.textContent : '';
// 构建分享文本
if (currentGroup) {
shareContent = `【${currentGroup.name}】\n${senderName}: ${messageContent}\n${time}`;
} else {
shareContent = `【${currentFriend.username}】\n${senderName}: ${messageContent}\n${time}`;
}
// 复制到剪贴板
navigator.clipboard.writeText(shareContent)
.then(() => {
showNotification('消息已复制到剪贴板', 'success');
})
.catch(err => {
console.error('复制失败:', err);
showNotification('复制失败,请手动复制', 'error');
});
// 关闭右键菜单
contextMenu.classList.remove('active');
}
}
});
}
// 显示引用输入框
function showQuoteInput(senderName, quoteContent, messageId = null) {
// 创建引用输入容器
const quoteInputContainer = document.createElement('div');
quoteInputContainer.className = 'quote-input-container';
if (messageId) {
quoteInputContainer.setAttribute('data-message-id', messageId);
}
quoteInputContainer.innerHTML = `
<div class="quote-header">
<span>${senderName}</span>
<button class="close-quote">&times;</button>
</div>
<div class="quote-content">${quoteContent}</div>
`;
// 获取输入框容器
const inputWrapper = document.querySelector('.input-wrapper');
if (inputWrapper) {
// 检查是否已经有引用输入框
const existingQuote = inputWrapper.querySelector('.quote-input-container');
if (existingQuote) {
existingQuote.remove();
}
// 插入引用输入框
inputWrapper.insertBefore(quoteInputContainer, messageInput);
// 聚焦输入框
messageInput.focus();
// 关闭引用按钮
const closeQuote = quoteInputContainer.querySelector('.close-quote');
closeQuote.addEventListener('click', function() {
quoteInputContainer.remove();
});
}
}
// 页面关闭时更新在线状态为离线
window.addEventListener('beforeunload', function() {
updateOnlineStatus(false);
});
// 检查URL参数,自动打开聊天窗口
function checkUrlParams() {
const url = new URL(window.location.href);
const chatId = url.searchParams.get('chatid');
const chatType = url.searchParams.get('chattype');
if (chatId && chatType) {
// 立即尝试打开聊天窗口
function tryOpenChat() {
if (chatType === 'friend') {
// 查找对应的好友
const friend = friends.find(f => f.id == chatId);
if (friend) {
openChatWindow(friend);
return true;
}
} else if (chatType === 'group') {
// 查找对应的群组
const group = groups.find(g => g.id == chatId);
if (group) {
openGroupChatWindow(group);
return true;
}
}
return false;
}
// 立即尝试
if (!tryOpenChat()) {
// 如果失败,等待一段时间后再次尝试
let attempts = 0;
const maxAttempts = 10;
const interval = 200;
const timer = setInterval(() => {
attempts++;
if (tryOpenChat() || attempts >= maxAttempts) {
clearInterval(timer);
}
}, interval);
}
}
}
// 初始化主题切换功能
initThemeToggle();
}
// API请求封装
function apiRequest(action, data = {}, method = 'GET') {
const url = new URL('api.php', window.location.href);
url.searchParams.append('action', action);
const options = {
method: method,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}
};
if (method === 'POST') {
const formData = new URLSearchParams();
for (const [key, value] of Object.entries(data)) {
formData.append(key, value);
}
options.body = formData.toString();
} else if (method === 'GET') {
for (const [key, value] of Object.entries(data)) {
url.searchParams.append(key, value);
}
}
return fetch(url.toString(), options)
.then(response => response.json())
.catch(error => {
console.error('API请求失败:', error);
return { success: false, message: '请求失败' };
});
}
// Steam登录处理
const steamLoginBtn = document.getElementById('steam-login-btn');
if (steamLoginBtn) {
steamLoginBtn.addEventListener('click', function(e) {
e.preventDefault();
console.log('Steam登录按钮被点击');
// 从API获取Steam配置
fetch('api.php?action=getSteamConfig')
.then(response => response.json())
.then(data => {
console.log('获取Steam配置:', data);
if (data.success && data.config) {
const steamConfig = data.config;
const redirectUri = encodeURIComponent(steamConfig.callback || 'https://www.parlz.com/callback/steam.php');
const state = Math.random().toString(36).substring(2, 15);
console.log('生成Steam登录URL:', {
redirectUri,
state
});
const authUrl = `https://steamcommunity.com/openid/login?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=checkid_setup&openid.return_to=${redirectUri}&openid.realm=${encodeURIComponent('https://www.parlz.com')}&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select`;
console.log('最终Steam登录URL:', authUrl);
window.location.href = authUrl;
} else {
console.error('获取Steam配置失败');
showNotification('Steam登录未配置,请联系管理员', 'error');
}
})
.catch(error => {
console.error('获取Steam配置时发生错误:', error);
showNotification('Steam登录未配置,请联系管理员', 'error');
});
});
}
// Discord登录处理
const discordLoginBtn = document.getElementById('discord-login-btn');
if (discordLoginBtn) {
discordLoginBtn.addEventListener('click', function(e) {
e.preventDefault();
console.log('Discord登录按钮被点击');
// 从API获取Discord配置
fetch('api.php?action=getDiscordConfig')
.then(response => response.json())
.then(data => {
console.log('获取Discord配置:', data);
if (data.success && data.config) {
const discordConfig = data.config;
const clientId = discordConfig.client_id || '';
const redirectUri = encodeURIComponent(discordConfig.callback || 'https://www.parlz.com/callback/discord.php');
const scope = discordConfig.scope || 'identify email';
const state = Math.random().toString(36).substring(2, 15);
if (!clientId) {
console.error('Discord client_id未配置');
showNotification('Discord登录未配置,请联系管理员', 'error');
return;
}
console.log('生成Discord登录URL:', {
clientId,
redirectUri,
scope,
state
});
const authUrl = `https://discord.com/api/oauth2/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
console.log('最终Discord登录URL:', authUrl);
window.location.href = authUrl;
} else {
console.error('获取Discord配置失败');
showNotification('Discord登录未配置,请联系管理员', 'error');
}
})
.catch(error => {
console.error('获取Discord配置时发生错误:', error);
showNotification('Discord登录未配置,请联系管理员', 'error');
});
});
}
// 微软登录处理
const microsoftLoginBtn = document.getElementById('microsoft-login-btn');
if (microsoftLoginBtn) {
microsoftLoginBtn.addEventListener('click', function(e) {
e.preventDefault();
console.log('微软登录按钮被点击');
// 从API获取微软配置
fetch('api.php?action=getMicrosoftConfig')
.then(response => response.json())
.then(data => {
console.log('获取微软配置:', data);
if (data.success && data.config) {
const microsoftConfig = data.config;
const clientId = microsoftConfig.client_id || '';
const redirectUri = encodeURIComponent(microsoftConfig.callback || 'https://www.parlz.com/callback/microsoft.php');
const scope = microsoftConfig.scope || 'user.read';
const state = Math.random().toString(36).substring(2, 15);
if (!clientId) {
console.error('微软 client_id未配置');
showNotification('微软登录未配置,请联系管理员', 'error');
return;
}
console.log('生成微软登录URL:', {
clientId,
redirectUri,
scope,
state
});
const authUrl = `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
console.log('最终微软登录URL:', authUrl);
window.location.href = authUrl;
} else {
console.error('获取微软配置失败');
showNotification('微软登录未配置,请联系管理员', 'error');
}
})
.catch(error => {
console.error('获取微软配置时发生错误:', error);
showNotification('微软登录未配置,请联系管理员', 'error');
});
});
}
// 格式化时间
function formatTime(timestamp) {
const date = new Date(timestamp);
const now = new Date();
// 今天的消息只显示时间
if (date.toDateString() === now.toDateString()) {
return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
}
// 昨天的消息显示"昨天"
const yesterday = new Date(now);
yesterday.setDate(now.getDate() - 1);
if (date.toDateString() === yesterday.toDateString()) {
return '昨天 ' + date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
}
// 今年的消息显示月日
if (date.getFullYear() === now.getFullYear()) {
return (date.getMonth() + 1) + '月' + date.getDate() + '日 ' +
date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
}
// 更早的消息显示完整日期
return date.getFullYear() + '年' + (date.getMonth() + 1) + '月' + date.getDate() + '日 ' +
date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
}
// 显示通知
function showNotification(message, type = 'info') {
// 创建通知元素
const notification = document.createElement('div');
notification.className = `notification notification-${type}`;
notification.textContent = message;
// 添加样式
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 12px 20px;
border-radius: 5px;
color: white;
font-size: 14px;
z-index: 9999;
animation: slideIn 0.3s ease;
`;
// 根据类型设置背景色
switch(type) {
case 'success':
notification.style.background = '#07c160';
break;
case 'error':
notification.style.background = '#ff4d4f';
break;
case 'warning':
notification.style.background = '#faad14';
break;
default:
notification.style.background = '#1890ff';
}
// 添加到页面
document.body.appendChild(notification);
// 3秒后自动移除
setTimeout(() => {
notification.style.animation = 'slideOut 0.3s ease';
setTimeout(() => {
notification.remove();
}, 300);
}, 3000);
}
// 添加动画样式
const style = document.createElement('style');
style.textContent = `
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOut {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}
`;
document.head.appendChild(style);
// 重写群头像上传事件监听器,避免使用未定义的函数
setTimeout(function() {
// 移除旧的事件监听器(如果存在)
const oldChangeAvatarBtn = document.getElementById('change-group-avatar-btn');
if (oldChangeAvatarBtn) {
oldChangeAvatarBtn.onclick = function() {
const groupAvatarInput = document.getElementById('group-avatar-input');
if (groupAvatarInput) {
groupAvatarInput.click();
}
};
}
// 为群头像输入添加新的事件监听器
const groupAvatarInput = document.getElementById('group-avatar-input');
if (groupAvatarInput) {
groupAvatarInput.onchange = function(e) {
const file = e.target.files[0];
if (file) {
console.log('选择了文件:', file);
// 直接在事件监听器中实现上传逻辑
if (!window.currentGroup) {
// 简单的通知实现
alert('群组信息未加载');
return;
}
const groupId = window.currentGroup.id;
const formData = new FormData();
formData.append('avatar', file);
formData.append('groupId', groupId);
// 移除对 showLoading 的调用
fetch('api.php?action=changeGroupAvatar', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 更新本地群组信息
window.currentGroup.avatar = data.avatar;
// 更新群头像预览
const groupAvatarPreview = document.getElementById('group-avatar-preview');
const groupAvatarImg = groupAvatarPreview.querySelector('img');
groupAvatarImg.src = data.avatar;
// 更新群组列表中的头像
const groupElement = document.querySelector(`.chat-item[data-id="${groupId}"]`);
if (groupElement) {
const groupAvatarElement = groupElement.querySelector('.avatar');
if (groupAvatarElement) {
groupAvatarElement.innerHTML = `<img src="${data.avatar}" alt="群组头像" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover;">`;
}
}
// 简单的通知实现
alert('群头像更新成功');
} else {
// 简单的通知实现
alert('群头像更新失败: ' + data.message);
}
})
.catch(error => {
console.error('上传群头像失败:', error);
// 简单的通知实现
alert('上传群头像失败');
});
}
};
}
}, 2000);
// 使用事件委托为群头像上传按钮添加事件监听
document.addEventListener('click', function(e) {
// 检查是否点击了群头像上传按钮
if (e.target.id === 'change-group-avatar-btn' || (e.target.closest && e.target.closest('#change-group-avatar-btn'))) {
console.log('点击了群头像上传按钮');
const groupAvatarInput = document.getElementById('group-avatar-input');
if (groupAvatarInput) {
groupAvatarInput.click();
} else {
console.log('群头像输入元素不存在');
}
}
});
// 为群头像输入添加事件监听
document.addEventListener('change', function(e) {
if (e.target.id === 'group-avatar-input') {
const file = e.target.files[0];
if (file) {
console.log('选择了文件:', file);
// 直接在事件监听器中实现上传逻辑
if (!window.currentGroup) {
showNotification('群组信息未加载', 'error');
return;
}
const groupId = window.currentGroup.id;
const formData = new FormData();
formData.append('avatar', file);
formData.append('groupId', groupId);
showLoading();
fetch('api.php?action=changeGroupAvatar', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 更新本地群组信息
window.currentGroup.avatar = data.avatar;
// 更新群头像预览
const groupAvatarPreview = document.getElementById('group-avatar-preview');
const groupAvatarImg = groupAvatarPreview.querySelector('img');
groupAvatarImg.src = data.avatar;
// 更新群组列表中的头像
const groupElement = document.querySelector(`.chat-item[data-id="${groupId}"]`);
if (groupElement) {
const groupAvatarElement = groupElement.querySelector('.avatar');
if (groupAvatarElement) {
groupAvatarElement.innerHTML = `<img src="${data.avatar}" alt="群组头像" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover;">`;
}
}
showNotification('群头像更新成功', 'success');
} else {
showNotification('群头像更新失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('上传群头像失败:', error);
showNotification('上传群头像失败', 'error');
})
.finally(() => {
hideLoading();
});
}
}
});
// QQ登录处理
const qqLoginBtn = document.getElementById('qq-login-btn');
if (qqLoginBtn) {
qqLoginBtn.addEventListener('click', function(e) {
e.preventDefault();
console.log('QQ登录按钮被点击');
// 从API获取QQ配置
fetch('api.php?action=getQQConfig')
.then(response => response.json())
.then(data => {
console.log('获取QQ配置:', data);
if (data.success && data.config) {
const qqConfig = data.config;
const appId = qqConfig.app_id || '102830836';
const redirectUri = encodeURIComponent(qqConfig.callback || 'https://www.parlz.com/callback/qq.php');
const scope = qqConfig.scope || 'get_user_info';
const state = Math.random().toString(36).substring(2, 15);
console.log('生成QQ登录URL:', {
appId,
redirectUri,
scope,
state
});
const authUrl = `https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=${appId}&redirect_uri=${redirectUri}&scope=${scope}&state=${state}`;
console.log('最终QQ登录URL:', authUrl);
window.location.href = authUrl;
} else {
console.error('获取QQ配置失败,使用默认值');
// fallback到默认值
const appId = '102830836';
const redirectUri = encodeURIComponent('https://www.parlz.com/callback/qq.php');
const scope = 'get_user_info';
const state = Math.random().toString(36).substring(2, 15);
const authUrl = `https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=${appId}&redirect_uri=${redirectUri}&scope=${scope}&state=${state}`;
console.log('使用默认配置的QQ登录URL:', authUrl);
window.location.href = authUrl;
}
})
.catch(error => {
console.error('获取QQ配置时发生错误:', error);
// 发生错误时使用默认值
const appId = '102830836';
const redirectUri = encodeURIComponent('https://www.parlz.com/callback/qq.php');
const scope = 'get_user_info';
const state = Math.random().toString(36).substring(2, 15);
const authUrl = `https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=${appId}&redirect_uri=${redirectUri}&scope=${scope}&state=${state}`;
console.log('发生错误时使用默认配置的QQ登录URL:', authUrl);
window.location.href = authUrl;
});
});
}
// GitHub登录处理
const githubLoginBtn = document.getElementById('github-login-btn');
if (githubLoginBtn) {
githubLoginBtn.addEventListener('click', function(e) {
e.preventDefault();
console.log('GitHub登录按钮被点击');
// 从API获取GitHub配置
fetch('api.php?action=getGitHubConfig')
.then(response => response.json())
.then(data => {
console.log('获取GitHub配置:', data);
if (data.success && data.config) {
const githubConfig = data.config;
const clientId = githubConfig.client_id || '';
const redirectUri = encodeURIComponent(githubConfig.callback || 'https://www.parlz.com/callback/github.php');
const scope = githubConfig.scope || 'user:email';
const state = Math.random().toString(36).substring(2, 15);
if (!clientId) {
console.error('GitHub client_id未配置');
showNotification('GitHub登录未配置,请联系管理员', 'error');
return;
}
console.log('生成GitHub登录URL:', {
clientId,
redirectUri,
scope,
state
});
const authUrl = `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&state=${state}`;
console.log('最终GitHub登录URL:', authUrl);
window.location.href = authUrl;
} else {
console.error('获取GitHub配置失败');
showNotification('GitHub登录未配置,请联系管理员', 'error');
}
})
.catch(error => {
console.error('获取GitHub配置时发生错误:', error);
showNotification('GitHub登录未配置,请联系管理员', 'error');
});
});
}