6868 行
288 KiB
JavaScript
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">×</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">×</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 = {
|
|
'&': '&',
|
|
'<': '<',
|
|
'>': '>',
|
|
'"': '"',
|
|
"'": '''
|
|
};
|
|
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)}"e=${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">×</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">×</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">×</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">×</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">×</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">×</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">×</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)}¬e=${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">×</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');
|
|
});
|
|
});
|
|
} |