diff --git a/med.php b/med.php index f12fc52..f43b79e 100644 --- a/med.php +++ b/med.php @@ -119,7 +119,7 @@ function formatDate($timestamp) {
-
+ '; + }); + isThrottled = true; + setTimeout(() => { + isThrottled = false; + }, 500); + } + + function onReachTop() { + if (isThrottled) return; + fetch('/comment/?category=&id=') + .then(response => response.text()) + .then(html => { + document.getElementById('comments').innerHTML = html; + }) + .catch(error => { + document.getElementById('comments').innerHTML = '
加载评论失败
'; + }); + isThrottled = true; + setTimeout(() => { + isThrottled = false; + }, 500); + } + +// 全局变量跟踪当前卡片 +var currentCard = null; +// 防止重复调用的标志位 +var isOpeningCard = false; +// 存储最后一次调用的参数 +var lastUid = null; + +// 修改后的 getUserCard 函数,添加防抖处理 +function getUserCard(uid) { + // 如果正在打开卡片,直接返回 + if (isOpeningCard) { + console.log('卡片正在打开中,忽略重复调用'); + return; + } + + // 如果已有卡片,先移除 + if (currentCard) { + currentCard.remove(); + currentCard = null; + } + + // 设置正在打开标志 + isOpeningCard = true; + lastUid = uid; + + console.log('显示用户卡片,ID:', uid); + + fetch('/user/card.php?id=' + uid) + .then(r => r.text()) + .then(html => { + // 创建遮罩层 + const overlay = document.createElement('div'); + overlay.id = 'userCardOverlay'; + overlay.style.cssText = ` + position:fixed; + top:0;left:0;right:0;bottom:0; + background:rgba(0,0,0,0.5); + display:flex; + justify-content:center; + align-items:center; + z-index:9999; + `; + + // 提取卡片内容 + const temp = document.createElement('div'); + temp.innerHTML = html; + let cardHTML = temp.innerHTML; + + // 如果是完整HTML,提取body内容 + if (html.includes(']*>([\s\S]*?)<\/body>/i); + if (bodyMatch) { + const temp2 = document.createElement('div'); + temp2.innerHTML = bodyMatch[1]; + cardHTML = temp2.innerHTML; + } + } + + // 插入卡片 + overlay.innerHTML = `
${cardHTML}
`; + + // 添加到页面并保存引用 + document.body.appendChild(overlay); + currentCard = overlay; + + // 重置打开标志 + isOpeningCard = false; + + // 事件监听器1:点击遮罩层关闭(使用once确保只绑定一次) + overlay.addEventListener('click', function closeOnOverlayClick(e) { + if (e.target === overlay) { + // 立即移除事件监听器,防止重复触发 + overlay.removeEventListener('click', closeOnOverlayClick); + overlay.remove(); + currentCard = null; + } + }, { once: true }); + + // 事件监听器2:查找并绑定关闭按钮 + setTimeout(() => { + // 查找所有关闭按钮 + const closeBtns = overlay.querySelectorAll('.close-btn, button[onclick*="close"]'); + + closeBtns.forEach(btn => { + // 移除原有的click事件监听器,防止重复绑定 + const newBtn = btn.cloneNode(true); + btn.parentNode.replaceChild(newBtn, btn); + + // 为新按钮绑定事件 + newBtn.addEventListener('click', function closeBtnClick(e) { + e.stopPropagation(); + e.preventDefault(); + + if (overlay.parentNode) { + overlay.remove(); + currentCard = null; + } + }, { once: true }); // 使用once确保只触发一次 + }); + + // 阻止卡片内部点击事件冒泡 + const cardContent = overlay.querySelector('.user-card') || + overlay.querySelector('.centered-container') || + overlay.querySelector('div > div'); + if (cardContent) { + cardContent.addEventListener('click', function(e) { + e.stopPropagation(); + }); + } + + // 绑定关注按钮功能(可选) + setupUserCardEvents(); + }, 0); + }) + .catch(e => { + console.error('加载失败:', e); + isOpeningCard = false; // 出错时也要重置标志 + }); +} + +// 防抖函数,防止快速多次点击 +function debounce(func, wait) { + let timeout; + return function(...args) { + clearTimeout(timeout); + timeout = setTimeout(() => func.apply(this, args), wait); + }; +} + +// 使用防抖包装的getUserCard函数 +var debouncedGetUserCard = debounce(getUserCard, 300); + +// 如果您需要替换原来的调用,可以使用这个防抖版本 +// 示例:debouncedGetUserCard(uid); + +// 其他函数保持不变 +function setupUserCardEvents() { + const overlay = document.getElementById('userCardOverlay'); + if (!overlay) return; + + // 关注按钮功能 + const followBtn = overlay.querySelector('#followBtn'); + const unfollowBtn = overlay.querySelector('#unfollowBtn'); + + if (followBtn) { + // 克隆并替换按钮,移除旧的事件监听器 + const newFollowBtn = followBtn.cloneNode(true); + followBtn.parentNode.replaceChild(newFollowBtn, followBtn); + + newFollowBtn.addEventListener('click', function(e) { + e.stopPropagation(); // 防止事件冒泡 + console.log('关注用户'); + + // 模拟API请求 + setTimeout(() => { + newFollowBtn.style.display = 'none'; + if (unfollowBtn) { + const newUnfollowBtn = unfollowBtn.cloneNode(true); + unfollowBtn.parentNode.replaceChild(newUnfollowBtn, unfollowBtn); + newUnfollowBtn.style.display = 'block'; + } + updateFollowersCount(1); + }, 300); + }); + } + + if (unfollowBtn) { + // 克隆并替换按钮,移除旧的事件监听器 + const newUnfollowBtn = unfollowBtn.cloneNode(true); + unfollowBtn.parentNode.replaceChild(newUnfollowBtn, unfollowBtn); + + newUnfollowBtn.addEventListener('click', function(e) { + e.stopPropagation(); // 防止事件冒泡 + console.log('取消关注用户'); + + // 模拟API请求 + setTimeout(() => { + newUnfollowBtn.style.display = 'none'; + if (followBtn) { + const newFollowBtn = followBtn.cloneNode(true); + followBtn.parentNode.replaceChild(newFollowBtn, followBtn); + newFollowBtn.style.display = 'block'; + } + updateFollowersCount(-1); + }, 300); + }); + } +} + +function updateFollowersCount(change) { + const overlay = document.getElementById('userCardOverlay'); + if (!overlay) return; + + const statItems = overlay.querySelectorAll('.stat-item'); + if (statItems.length > 1) { + const followersElement = statItems[1].querySelector('.text'); + if (followersElement) { + const text = followersElement.textContent; + const current = parseInt(text.replace(/[^\d]/g, '')) || 0; + const newCount = current + change; + followersElement.textContent = '粉丝' + newCount.toLocaleString(); + } + } +} + +function handleEscapeKey(e) { + if (e.key === 'Escape') { + const overlay = document.getElementById('userCardOverlay'); + if (overlay) { + overlay.remove(); + currentCard = null; + isOpeningCard = false; // 重置打开标志 + } + } +} + +// 添加ESC键监听 +document.addEventListener('keydown', handleEscapeKey); + +// 如果需要修复页面中的点击事件,可以添加以下代码 +document.addEventListener('DOMContentLoaded', function() { + // 查找所有可能触发getUserCard的元素 + const triggerElements = document.querySelectorAll('[onclick*="getUserCard"], [data-user-id]'); + + triggerElements.forEach(element => { + // 移除原有的onclick事件 + const originalOnClick = element.getAttribute('onclick'); + if (originalOnClick && originalOnClick.includes('getUserCard')) { + element.removeAttribute('onclick'); + + // 获取用户ID + const uid = element.dataset.userId || + originalOnClick.match(/getUserCard\(['"]([^'"]+)['"]\)/)?.[1] || + originalOnClick.match(/getUserCard\(([^)]+)\)/)?.[1]; + + if (uid) { + // 添加新的点击事件,使用防抖版本 + element.addEventListener('click', function(e) { + e.preventDefault(); + e.stopPropagation(); + debouncedGetUserCard(uid.trim()); + }); + } + } + }); +}); diff --git a/styles/comment.css b/styles/comment.css index d938a86..a047e4b 100644 --- a/styles/comment.css +++ b/styles/comment.css @@ -164,3 +164,224 @@ dialog::backdrop { height: 45px; } } + + .user-card-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 9999; + opacity: 0; + visibility: hidden; + transition: opacity 0.3s ease, visibility 0.3s ease; + } + + .user-card-overlay.active { + opacity: 1; + visibility: visible; + } + + .user-card { + background: white; + border-radius: 15px; + padding: 25px; + width: 100%; + max-width: 400px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + box-sizing: border-box; + position: relative; + transform: translateY(20px); + transition: transform 0.3s ease; + margin: 20px; + } + + .user-card-overlay.active .user-card { + transform: translateY(0); + } + + .close-btn { + position: absolute; + top: 15px; + right: 15px; + width: 30px; + height: 30px; + background: #f5f5f5; + border: none; + border-radius: 50%; + font-size: 16px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + color: #666; + z-index: 1000; + } + + .close-btn:hover { + background: #e5e5e5; + } + + .avatar-container { + text-align: center; + margin-bottom: 15px; + } + + .avatar { + width: 100px; + height: 100px; + border-radius: 50%; + object-fit: cover; + border: 4px solid #f0f0f0; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); + } + + .user-details { + text-align: center; + margin-bottom: 20px; + } + + .username { + font-size: 22px; + font-weight: 700; + color: #1a1a1a; + margin-bottom: 8px; + line-height: 1.2; + } + + .signature { + font-size: 14px; + color: #666; + line-height: 1.4; + opacity: 0.9; + max-width: 80%; + margin: 0 auto; + } + + .stats-row { + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 25px; + padding: 15px 0; + border-top: 1px solid #f0f0f0; + border-bottom: 1px solid #f0f0f0; + } + + .stat-item { + display: flex; + align-items: center; + font-size: 15px; + font-weight: 600; + color: #333; + margin: 0 20px; + } + + .stat-item .number { + color: #1890ff; + margin-right: 4px; + } + + .stat-item .text { + color: #666; + } + + .data-section { + margin-bottom: 25px; + } + + .numbers-row { + display: flex; + justify-content: space-around; + margin-bottom: 10px; + } + + .data-number { + font-size: 20px; + font-weight: 700; + color: #1890ff; + text-align: center; + flex: 1; + } + + .data-icons { + display: flex; + justify-content: space-around; + margin-bottom: 10px; + } + + .data-icons img { + height: 25px; + filter: brightness(0.9); + opacity: 0.8; + } + + .verification-badge { + display: inline-block; + background: #ffd700; + color: #333; + padding: 2px 8px; + border-radius: 12px; + font-size: 10px; + font-weight: 600; + margin-left: 8px; + vertical-align: middle; + } + + .follow-button, .unfollow-button { + width: 100%; + padding: 14px; + border: none; + border-radius: 10px; + font-size: 16px; + font-weight: 600; + cursor: pointer; + font-family: inherit; + box-sizing: border-box; + } + + .follow-button { + background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); + color: white; + } + + .follow-button:hover { + background: linear-gradient(135deg, #096dd9 0%, #1890ff 100%); + } + + .unfollow-button { + background: #f5f5f5; + color: #666; + border: 1px solid #d9d9d9; + } + + .unfollow-button:hover { + background: #fff2f0; + color: #ff4d4f; + border-color: #ffccc7; + } + + @media (max-width: 480px) { + .user-card { + padding: 20px; + margin: 15px; + } + + .avatar { + width: 90px; + height: 90px; + } + + .username { + font-size: 20px; + } + + .stat-item { + margin: 0 15px; + } + } diff --git a/user/card.php b/user/card.php new file mode 100644 index 0000000..92e6dd7 --- /dev/null +++ b/user/card.php @@ -0,0 +1,297 @@ + $id]; + +$headers = [ + 'Content-Type: application/json', + 'Accept: application/json', + 'Accept-Language: zh-CN', + 'x-API-Token: ' . ($_SESSION['token'] ?? ''), + 'x-API-AuthCode: ' . ($_SESSION['authCode'] ?? '') +]; + +$url = $baseUrl . 'Users/GetUser'; + +$ch = curl_init(); +curl_setopt_array($ch, [ + CURLOPT_URL => $url, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => json_encode($requestData, JSON_UNESCAPED_UNICODE), + CURLOPT_HTTPHEADER => $headers, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_TIMEOUT => 30, +]); + +$response = curl_exec($ch); +curl_close($ch); +$data = json_decode($response, true); +$userData = $data['Data'] ?? []; + +if (empty($userData)) { + echo '
用户数据加载失败
'; + exit; +} + +// 生成头像URL +function getAvatarUrl($userID, $avatarNumber) { + $part1 = substr($userID, 0, 4); + $part2 = substr($userID, 4, 2); + $part3 = substr($userID, 6, 2); + $part4 = substr($userID, 8, 16); + return "http://netlogo-cn.oss-cn-hongkong.aliyuncs.com/users/avatars/{$part1}/{$part2}/{$part3}/{$part4}/{$avatarNumber}.jpg!full"; +} + +$avatarUrl = getAvatarUrl($userData['User']['ID'], $userData['User']['Avatar'] ?? 0); +$vers = [ + 'Banned' => '已被封禁', + 'Oldtimer' => '老用户', + 'Volunteer' => '志愿者', + 'Junior' => '见习编辑', + 'Editor' => '认证编辑', + 'Administrator' => '认证管理员' +]; +?> + +
+
+ 用户头像 +
+ +
+
+ + +
+
+
+
+ +
+
+ 关注 +
+
+ 粉丝 +
+
+ +
+
+
+
+
+
+
+ 实验 + 收藏 + 精选 +
+
+ + + +
+ +