diff --git a/comment/comment.html b/comment/comment.html new file mode 100644 index 0000000..6fcaf77 --- /dev/null +++ b/comment/comment.html @@ -0,0 +1,3 @@ + +发一条友善的评论吧 + diff --git a/comment/index.php b/comment/index.php new file mode 100644 index 0000000..0f9698e --- /dev/null +++ b/comment/index.php @@ -0,0 +1,388 @@ +token = $token; + $this->authCode = $authCode; + } + + /** + * 获取留言/评论信息 + */ + public function getMessages( + $ID, + $type = "Discussion", + $take = 20, + $from = null, + $skip = 0 + ) { + // 验证必需参数 + if (empty($ID)) { + throw new Exception("ID参数不能为空"); + } + + if (empty($type)) { + throw new Exception("类型参数不能为空"); + } + + if ($take > 100) { + throw new Exception("消息获取数量一次最多为100条"); + } + + $take = -$take; + + $requestData = [ + 'TargetID' => $ID, + 'TargetType' => $type, + 'Take' => $take, + 'Skip' => $skip, + ]; + + // 只有在提供了from参数时才添加CommentID字段 + if ($from !== null) { + $requestData['CommentID'] = $from; + } + + $headers = [ + 'Content-Type: application/json', + 'Accept: application/json', + 'Accept-Language: zh-CN', + ]; + + if ($this->token && !empty($this->token)) { + $headers[] = 'x-API-Token: ' . $this->token; + } + + if ($this->authCode && !empty($this->authCode)) { + $headers[] = 'x-API-AuthCode: ' . $this->authCode; + } + + $url = $this->baseUrl . 'Messages/GetComments'; + + $ch = curl_init(); + curl_setopt_array($ch, [ + CURLOPT_URL => $url, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => json_encode($requestData), + CURLOPT_HTTPHEADER => $headers, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_TIMEOUT => 30, + ]); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $error = curl_error($ch); + curl_close($ch); + + if ($error) { + throw new Exception("请求失败: " . $error); + } + + if ($httpCode !== 200) { + $errorInfo = ''; + if ($response) { + $responseData = json_decode($response, true); + if (isset($responseData['Message'])) { + $errorInfo = ' - ' . $responseData['Message']; + } + } + throw new Exception("API请求失败,HTTP状态码: " . $httpCode . $errorInfo); + } + + $data = json_decode($response, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw new Exception("JSON解析失败: " . json_last_error_msg()); + } + + // 检查API返回的业务状态码 + if (isset($data['Status']) && $data['Status'] !== 200) { + $errorMsg = $data['Message'] ?? '未知错误'; + throw new Exception("API返回错误: " . $errorMsg); + } + + return $data; + } + + // 设置认证信息 + public function setAuth($token, $authCode) { + $this->token = $token; + $this->authCode = $authCode; + return $this; + } +} + +// 类形式的使用示例: +$messageService = new MessageService($_SESSION['token'], $_SESSION['authCode']); +try { + $messages = $messageService->getMessages($contentId, $category, 20); + $target = $messages['Data']['Target'] ?? []; + $comments = $messages['Data']['Comments'] ?? []; + $totalComments = $messages['Data']['Count'] ?? 0; +} catch (Exception $e) { + echo "错误: " . $e->getMessage(); +} + +// 生成头像URL函数 +function getAvatarUrl($userID, $avatarNumber) { + // 根据用户ID构建头像路径 + $part1 = substr($userID, 0, 4); + $part2 = substr($userID, 4, 2); + $part3 = substr($userID, 6, 2); + $part4 = substr($userID, 8, 16); + + return "http://netlogo-static-cn.turtlesim.com/users/avatars/{$part1}/{$part2}/{$part3}/{$part4}/{$avatarNumber}.jpg!full"; +} + +// 格式化时间函数 +function formatCommentTime($timestamp) { + if (!$timestamp) return '未知时间'; + + // 如果是毫秒时间戳,转换为秒 + if ($timestamp > 9999999999) { + $timestamp = $timestamp / 1000; + } + + $date = date('n/j', $timestamp); + $hour = date('G', $timestamp); + $minute = date('i', $timestamp); + + // 转换为12小时制并添加上午/下午 + if ($hour < 12) { + $period = '上午'; + $displayHour = $hour == 0 ? 12 : $hour; + } else { + $period = '下午'; + $displayHour = $hour > 12 ? $hour - 12 : $hour; + } + + return "{$date} {$period}{$displayHour}:{$minute}"; +} + +// 处理回复内容中的@用户标签 +function processReplyContent($content) { + // 匹配 @用户名 格式 + $pattern = '/@([^<]+)/'; + $replacement = '@$2'; + + return preg_replace($pattern, $replacement, $content); +} +?> + +
+ +
+ 加载失败: +
+ + + +
+

暂无评论

+
+ + $comment): ?> +
+
+
+ <?= htmlspecialchars($comment['Nickname'] ?? '用户') ?> +
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + +
+ + +
+ +
+ + diff --git a/getv.php b/getv.php new file mode 100644 index 0000000..bc7fde4 --- /dev/null +++ b/getv.php @@ -0,0 +1,133 @@ + '', + 'Password' => '', + 'Version' => 2411, + 'Device' => [ + 'Identifier' => $identifier, + 'Language' => "Chinese", + ], + 'Statistic' => null, + ]; + + $headers = [ + "Content-Type: application/json", + "Accept: application/json", + "Accept-Language: zh-CN", + ]; + + $response = $this->postRequest("Users/Authenticate", $requestData, $headers); + $statusCode = $response['status']; + $this->responseBody = $response['body']; + + $responseData = json_decode($this->responseBody, true); + + // 检查JSON解码是否成功 + if (json_last_error() !== JSON_ERROR_NONE) { + throw new Exception("API响应格式错误: " . json_last_error_msg()); + } + + // 优先检查业务状态码 + if (isset($responseData['Status'])) { + if ($responseData['Status'] === 200) { + $this->token = $responseData['Token'] ?? ''; + $this->authCode = $responseData['AuthCode'] ?? ''; + return $responseData; + } else { + $errorMsg = $responseData['Message'] ?? '登录失败'; + throw new Exception($errorMsg); + } + } + // 处理 HTTP 错误 + elseif ($statusCode >= 400) { + $errorMap = [ + 403 => '访问被拒绝(403)', + 404 => '资源不存在(404)', + 500 => '服务器错误(500)' + ]; + $errorMsg = $errorMap[$statusCode] ?? "HTTP 错误: $statusCode"; + throw new Exception($errorMsg); + } + // 完全无效的响应 + else { + throw new Exception("无效的API响应"); + } + } + + private function postRequest($endpoint, $data, $headers = []) { + $url = $this->baseUrl . $endpoint; + + $ch = curl_init($url); + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADER => true, + CURLOPT_POST => true, + CURLOPT_HTTPHEADER => $headers, + CURLOPT_POSTFIELDS => json_encode($data), + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_TIMEOUT => 15, + CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', + CURLOPT_SSL_VERIFYPEER => true, + ]); + + $response = curl_exec($ch); + + if ($response === false) { + $error = curl_error($ch); + curl_close($ch); + throw new Exception("请求失败: $error"); + } + + $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); + + $body = substr($response, $headerSize); + + curl_close($ch); + + return [ + 'status' => $statusCode, + 'body' => $body + ]; + } +} + + try { + $authService = new AuthService(); + $loginData = $authService->login(); + $_SESSION['token'] = $authService->token; + $_SESSION['authCode'] = $authService->authCode; + $_SESSION['responseBody'] = $authService->responseBody; + + // 重定向到首页 + header('Location: /' . $r); + exit; + } catch (Exception $e) { + $errorMsg = $e->getMessage(); + echo $errorMsg; + } +?> diff --git a/index.php b/index.php index eae1fef..60a1648 100644 --- a/index.php +++ b/index.php @@ -3,6 +3,16 @@ $dt = null; if (isset($_SESSION['responseBody'])) { $dt = is_string($_SESSION['responseBody']) ? json_decode($_SESSION['responseBody'], true) : $_SESSION['responseBody']; +} else { + $redirectUrl = '/getv.php'; + if (!headers_sent()) { + header('Location: ' . $redirectUrl); + exit; + } else { + // 如果已经有输出,使用JavaScript重定向作为备用 + echo ''; + exit; + } } // 检查登录状态 diff --git a/med.php b/med.php index 66dc68a..d27151b 100644 --- a/med.php +++ b/med.php @@ -10,6 +10,10 @@ $dt = null; if (isset($_SESSION['responseBody'])) { $dt = is_string($_SESSION['responseBody']) ? json_decode($_SESSION['responseBody'], true) : $_SESSION['responseBody']; +} else { + $redirectUrl = '/getv.php?r=' . urlencode('med.php?category=' . $category . '&id=' . $contentId . '&type=' . $type); + header('Location: ' . $redirectUrl); + exit; } // 获取作品详情的函数 @@ -66,13 +70,12 @@ function getContentSummary($contentId, $category, $token, $authCode) { $token = $_SESSION['token'] ?? ''; $authCode = $_SESSION['authCode'] ?? ''; - if (!empty($token) && !empty($authCode)) { + if (isset($token) && isset($authCode)) { $contentData = getContentSummary($contentId, $category, $token, $authCode); } } $content = $contentData['Data'] ?? null; - // 处理日期格式 function formatDate($timestamp) { return date('Y年m月d日', $timestamp / 1000); @@ -99,7 +102,7 @@ function formatDate($timestamp) { <?= htmlspecialchars($content['LocalizedSubject']['Chinese'] ?? $pageTitle) ?> - Turtle Universe Web - + @@ -126,7 +129,7 @@ function formatDate($timestamp) { } ?>');">
-
+
@@ -151,7 +154,18 @@ function formatDate($timestamp) {
- + +
+
+
简介
+
评论()
+
+ +
+
+ +
+
- - -
-
-
-
访问量
-
-
-
-
收藏
-
-
-
-
支持
-
-
-
-
评论
-
-
- - -
-
-
简介
-
详细信息
-
- -
-
- -
-
+

作品介绍

-

暂无作品介绍

+ ', $content['Description']) ?>
- - - -
- - - -
@@ -272,7 +215,7 @@ function formatDate($timestamp) { // 标签页切换 function switchTab(tabName) { // 隐藏所有标签内容 - document.querySelectorAll('.tab-content').forEach(tab => { + document.querySelectorAll('.qh').forEach(tab => { tab.style.display = 'none'; }); @@ -317,6 +260,15 @@ function shareExperiment() { function remixExperiment() { alert('改编功能即将开放'); } - +// 使用JavaScript动态加载评论 +fetch('/comment/?category=&id=') + .then(response => response.text()) + .then(html => { + document.getElementById('comments').innerHTML = html; + }) + .catch(error => { + document.getElementById('comments').innerHTML = '
加载评论失败
'; + }); + diff --git a/scripts/comment.js b/scripts/comment.js new file mode 100644 index 0000000..fa43547 --- /dev/null +++ b/scripts/comment.js @@ -0,0 +1,61 @@ + const dialog = document.querySelector('dialog'); + const originalText = dialog.innerHTML.trim(); // 获取原始文本 "编辑评论内容" + + // 解析属性 + const types = dialog.dataset.type.split(','); + const inputPlaceholder = dialog.dataset.ed; + const [methodStr, btnClass, btnText] = dialog.dataset.eb.split(','); + const methodName = methodStr.replace('()', ''); // 提取方法名 "sm" + + // 清空dialog原有内容 + dialog.innerHTML = ''; + + // 创建输入框 + const input = document.createElement('textarea'); + input.id='dt'; + input.placeholder = inputPlaceholder; + dialog.appendChild(input); + + const header = document.createElement('div'); + header.id='dh'; + header.innerHTML='

'+inputPlaceholder+'

'; + dialog.appendChild(header); + + // 创建提交按钮 + const submitBtn = document.createElement('button'); + submitBtn.className = btnClass; + submitBtn.textContent = btnText; + header.appendChild(submitBtn); + + // 提交按钮点击事件 + submitBtn.addEventListener('click', () => { + if (typeof window[methodName] === 'function') { + window[methodName](input.value); // 调用sm函数并传入输入值 + } + dialog.close(); + }); + + // 点击外部关闭对话框 + dialog.addEventListener('click', (e) => { + const rect = dialog.getBoundingClientRect(); + if ( + e.clientX < rect.left || + e.clientX > rect.right || + e.clientY < rect.top || + e.clientY > rect.bottom + ) { + dialog.close(); + } + }); + + // 点击触发按钮打开对话框 + document.getElementById('start').addEventListener('click', () => { + dialog.showModal(); + input.focus(); // 自动聚焦输入框 + }); + +/* 示例sm函数 */ +function sm(value) { + //向服务器通信 + document.querySelector('dialog').close(); +} diff --git a/styles/comment.css b/styles/comment.css new file mode 100644 index 0000000..d938a86 --- /dev/null +++ b/styles/comment.css @@ -0,0 +1,166 @@ +.red { + background-color: #e00; + position: absolute; + top: 20%; + left: 88%; + height: 60%; + width: 10%; + color: white; + font-size: 2.5vh; + border-radius: 10px; +} + +textarea:focus { + border-color: rgba(0,0,0,0); + outline: none; + box-shadow: 0 0 0.1px rgba(255,255,255,0); +} + +/* 评论框样式修复 */ +dialog { + position: fixed; + top: 5%; + left: 5%; + z-index: 80; + height: 90%; + width: 90%; + border: none; + border-radius: 12px; + box-shadow: 0 10px 30px rgba(0,0,0,0.3); + background: white; + overflow: hidden; +} + +#dt { + position: absolute; + top: 10%; + left: 0%; + height: 90%; + width: 100%; + border: none; + font-size: 16px; + padding: 20px; + resize: none; + font-family: inherit; + line-height: 1.5; + background: #fafafa; +} + +#dh { + position: absolute; + top: 0%; + left: 0%; + height: 10%; + width: 100%; + background-color: #f8f9fa; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px; + border-bottom: 1px solid #e9ecef; + } + +#dh img { + cursor: pointer; + padding: 8px; + border-radius: 50%; + transition: background-color 0.2s; +} + +#dh img:hover { + background-color: rgba(0,0,0,0.1); +} + +.mid { + font-size: 18px; + font-weight: 600; + color: #333; + margin: 0; +} + +.red { + background-color: red; + position: static; + height: auto; + width: auto; + color: white; + font-size: 14px; + border-radius: 10px; + border: none; + padding: 8px 20px; + cursor: pointer; + transition: background-color 0.3s; + margin: 0; +} + +.red:hover { + background-color: #1060c0; +} + +/* 移除原有的绝对定位样式 */ +.red, +.mid { + position: static; + top: auto; + left: auto; +} + +/* 移动端适配 */ +@media (max-width: 768px) { + dialog { + top: 2%; + left: 2%; + height: 96%; + width: 96%; + } + + #dt { + font-size: 14px; + padding: 15px; + } + + .mid { + font-size: 16px; + } + + .red { + padding: 6px 16px; + font-size: 13px; + } + + #dh { + padding: 0 15px; + } +} + +/* 对话框背景遮罩 */ +dialog::backdrop { + background: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(2px); +} + +/* #start 按钮位置和尺寸修复 */ +#start { + position: fixed; + bottom: 0px; + right: 0%; + transform: translateY(-10%); + height: 40px; + width: 48%; + z-index: 70; + border: 1px solid #111; + border-radius: none; + text-align: left; + color: gray; + background-color: #f6f6f6; +} + +/* 移动端适配 */ +@media (max-width: 768px) { + #start { + bottom: 0px; + width: 98%; + max-width: 700px; + height: 45px; + } +}