websocket:集成通知测试功能到WebSocket测试页面
- 添加通知模式切换功能,支持聊天和通知两种测试模式 - 实现通知WebSocket连接和用户认证 - 添加通知发送界面,支持API和WebSocket两种发送方式 - 集成通知管理功能,支持列表查看和已读标记 - 修复HTML结构,确保通知模式与聊天模式平级显示 - 更新页面标题和功能描述
This commit is contained in:
@@ -19,9 +19,9 @@ export class WebSocketTestController {
|
||||
|
||||
@Get()
|
||||
@ApiOperation({
|
||||
summary: '🔌 WebSocket 测试页面 - 一键测试工具 + API监控',
|
||||
summary: '🔌 WebSocket 测试页面 + 通知系统 - 一键测试工具 + API监控',
|
||||
description: `
|
||||
**🚀 功能强大的WebSocket测试工具**
|
||||
**🚀 功能强大的WebSocket测试工具 + 通知系统**
|
||||
|
||||
提供完整的WebSocket测试功能:
|
||||
- ✅ 自动获取JWT Token
|
||||
@@ -30,7 +30,16 @@ export class WebSocketTestController {
|
||||
- ✅ 聊天消息发送测试
|
||||
- ✅ 位置更新测试
|
||||
- ✅ 实时消息日志
|
||||
- 📡 **新增:API调用监控** - 实时显示所有HTTP请求
|
||||
- 📡 **API调用监控** - 实时显示所有HTTP请求
|
||||
- 🔔 **通知系统测试** - 完整的通知功能测试
|
||||
|
||||
**新增通知系统功能**:
|
||||
- 🔔 实时通知WebSocket连接
|
||||
- 📢 通知发送和接收测试
|
||||
- 📋 通知列表管理
|
||||
- 🔢 未读通知统计
|
||||
- 🎯 支持系统、用户、广播通知
|
||||
- ⏰ 定时通知功能
|
||||
|
||||
**使用方法**:
|
||||
1. 点击下方"Execute"按钮
|
||||
@@ -58,7 +67,7 @@ export class WebSocketTestController {
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>WebSocket 测试工具 + API监控 - Pixel Game Server</title>
|
||||
<title>WebSocket 测试工具 + 通知系统 - Pixel Game Server</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
@@ -73,6 +82,7 @@ export class WebSocketTestController {
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
margin-bottom: 20px;
|
||||
min-height: 200px; /* 确保容器有最小高度 */
|
||||
}
|
||||
.status {
|
||||
padding: 10px;
|
||||
@@ -234,6 +244,7 @@ export class WebSocketTestController {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
min-height: 400px; /* 确保两列布局有最小高度 */
|
||||
}
|
||||
|
||||
.logs-section {
|
||||
@@ -262,10 +273,27 @@ export class WebSocketTestController {
|
||||
max-width: calc(100vw - 60px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 通知模式特定样式 */
|
||||
#noticeMode {
|
||||
width: 100%;
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
#noticeMode .container {
|
||||
background: white;
|
||||
border: 1px solid #ddd;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
#noticeMode h2, #noticeMode h3 {
|
||||
color: #1976d2;
|
||||
margin-top: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🎮 Pixel Game Server - WebSocket 测试工具 + API监控</h1>
|
||||
<h1>🎮 Pixel Game Server - WebSocket 测试工具 + 通知系统</h1>
|
||||
|
||||
<div class="info-panel">
|
||||
<h3>📋 使用说明</h3>
|
||||
@@ -286,7 +314,8 @@ export class WebSocketTestController {
|
||||
• 💾 本地存储:自动保存Token,下次访问无需重新获取<br>
|
||||
• 📧 真实邮箱支持:检测到真实邮箱时使用真实验证码发送<br>
|
||||
• 📡 **API调用监控**:实时显示所有HTTP请求和响应,方便调试<br>
|
||||
• 🔍 详细日志:支持显示请求体、响应体和调用统计
|
||||
• 🔍 详细日志:支持显示请求体、响应体和调用统计<br>
|
||||
• 🔔 **通知系统测试**:完整的通知功能测试,支持实时推送和API调用
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 15px; padding: 10px; background-color: #fff3e0; border-radius: 4px;">
|
||||
@@ -319,6 +348,17 @@ export class WebSocketTestController {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 功能切换按钮 -->
|
||||
<div class="container" style="text-align: center; margin-bottom: 20px;">
|
||||
<h2>🎛️ 功能选择</h2>
|
||||
<div style="display: flex; gap: 10px; justify-content: center;">
|
||||
<button id="chatModeBtn" onclick="switchMode('chat')" style="flex: 1; max-width: 200px; background-color: #1976d2;">💬 聊天测试</button>
|
||||
<button id="noticeModeBtn" onclick="switchMode('notice')" style="flex: 1; max-width: 200px; background-color: #666;">🔔 通知测试</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 聊天模式 -->
|
||||
<div id="chatMode">
|
||||
<div class="two-column">
|
||||
<div class="container">
|
||||
<h2>🔌 连接控制</h2>
|
||||
@@ -458,18 +498,110 @@ export class WebSocketTestController {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 通知模式 -->
|
||||
<div id="noticeMode" style="display: none; background-color: #f0f8ff; border: 2px solid red; padding: 20px;">
|
||||
<h1 style="color: red; font-size: 24px;">🔔 通知测试模式</h1>
|
||||
<p style="color: blue; font-size: 18px;">如果你能看到这个文字,说明通知模式正常显示了!</p>
|
||||
|
||||
<div class="two-column">
|
||||
<div class="container">
|
||||
<h2>🔔 通知系统控制</h2>
|
||||
<div id="noticeConnectionStatus" class="status disconnected">未连接</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="noticeWsUrl">通知WebSocket地址:</label>
|
||||
<input type="text" id="noticeWsUrl" value="ws://localhost:3000/ws/notice" />
|
||||
</div>
|
||||
|
||||
<button id="noticeConnectBtn" onclick="toggleNoticeConnection()">连接通知系统</button>
|
||||
|
||||
<h3>🔐 用户认证</h3>
|
||||
<div class="form-group">
|
||||
<label for="noticeUserId">用户ID:</label>
|
||||
<input type="number" id="noticeUserId" value="1" placeholder="输入用户ID" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="noticeJwtToken">JWT Token:</label>
|
||||
<textarea id="noticeJwtToken" rows="3" placeholder="输入JWT Token (可从聊天模式复制)"></textarea>
|
||||
<button onclick="copyTokenFromChat()" style="margin-top: 5px; background-color: #4caf50;">从聊天模式复制Token</button>
|
||||
</div>
|
||||
|
||||
<button onclick="authenticateNotice()">认证通知连接</button>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<h2>📢 发送通知</h2>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="noticeTitle">通知标题:</label>
|
||||
<input type="text" id="noticeTitle" placeholder="输入通知标题" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="noticeContent">通知内容:</label>
|
||||
<textarea id="noticeContent" rows="3" placeholder="输入通知内容"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="noticeType">通知类型:</label>
|
||||
<select id="noticeType">
|
||||
<option value="system">系统通知</option>
|
||||
<option value="user">用户通知</option>
|
||||
<option value="broadcast">广播通知</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="targetUserId">目标用户ID (留空为广播):</label>
|
||||
<input type="number" id="targetUserId" placeholder="目标用户ID" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="scheduledTime">定时发送 (可选):</label>
|
||||
<input type="datetime-local" id="scheduledTime" />
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
|
||||
<button onclick="sendNoticeViaAPI()" style="background-color: #4caf50;"><3E> 过通过API发送</button>
|
||||
<button onclick="sendNoticeViaWS()" style="background-color: #ff9800;">🔌 通过WebSocket发送</button>
|
||||
</div>
|
||||
|
||||
<div class="quick-actions" style="margin-top: 15px;">
|
||||
<div class="quick-action" onclick="sendQuickNotice('系统维护', '系统将在今晚进行维护')">快速: 系统维护</div>
|
||||
<div class="quick-action" onclick="sendQuickNotice('新功能上线', '我们上线了新的通知功能!')">快速: 新功能</div>
|
||||
<div class="quick-action" onclick="sendQuickNotice('测试通知', '这是一条测试通知')">快速: 测试通知</div>
|
||||
<div class="quick-action" onclick="loadNoticeList()"><3E> 刷取新通知列表</div>
|
||||
<div class="quick-action" onclick="getUnreadCount()"><3E> 获取未读数日</div>
|
||||
<div class="quick-action" onclick="clearNoticeLog()">🗑️ 清空通知日志</div>
|
||||
</div>
|
||||
|
||||
<h3>📋 通知管理</h3>
|
||||
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
|
||||
<button onclick="loadNoticeList()" style="flex: 1;">获取通知列表</button>
|
||||
<button onclick="getUnreadCount()" style="flex: 1; background-color: #ff9800;">获取未读数量</button>
|
||||
</div>
|
||||
|
||||
<div id="noticeList" style="max-height: 200px; overflow-y: auto; border: 1px solid #ddd; padding: 10px; background-color: #fafafa;">
|
||||
<div style="text-align: center; color: #666;">点击"获取通知列表"加载通知</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 日志区域 - 消息日志和API监控并排显示 -->
|
||||
<div class="logs-section">
|
||||
<div class="container">
|
||||
<h2>📋 消息日志</h2>
|
||||
<h2 id="logTitle">📋 消息日志</h2>
|
||||
<div id="messageLog" class="message-log"></div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<h2>📡 API 调用日志</h2>
|
||||
<h2 id="apiLogTitle">📡 API 调用日志</h2>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||||
<span style="font-size: 14px; color: #666;">实时监控前端API调用</span>
|
||||
<span style="font-size: 14px; color: #666;" id="apiLogSubtitle">实时监控前端API调用</span>
|
||||
<div>
|
||||
<button onclick="clearApiLog()" style="padding: 4px 8px; font-size: 12px; background-color: #ff9800;">清空日志</button>
|
||||
<button onclick="toggleApiLogDetails()" id="toggleDetailsBtn" style="padding: 4px 8px; font-size: 12px; background-color: #9c27b0; margin-left: 5px;">显示详情</button>
|
||||
@@ -502,6 +634,11 @@ export class WebSocketTestController {
|
||||
let apiSuccessCount = 0;
|
||||
let apiErrorCount = 0;
|
||||
let showApiDetails = false;
|
||||
let currentMode = 'chat'; // 当前模式:chat 或 notice
|
||||
|
||||
// 通知系统相关变量
|
||||
let noticeWs = null;
|
||||
let noticeAuthenticated = false;
|
||||
|
||||
// API监控相关变量
|
||||
const originalFetch = window.fetch;
|
||||
@@ -1869,6 +2006,16 @@ export class WebSocketTestController {
|
||||
addMessage('system', '🎲 一键测试会自动生成随机测试账号,方便多用户测试');
|
||||
addMessage('system', '📡 新功能: API调用监控已启用,可实时查看所有HTTP请求');
|
||||
addMessage('system', '🔍 API日志支持显示请求体、响应体和详细统计信息');
|
||||
addMessage('system', '🔔 新增: 通知系统测试功能,可切换到通知模式进行测试');
|
||||
|
||||
// 请求通知权限
|
||||
if (Notification.permission === 'default') {
|
||||
Notification.requestPermission().then(permission => {
|
||||
if (permission === 'granted') {
|
||||
addMessage('system', '✅ 浏览器通知权限已获取');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 检查URL参数,看是否从API文档跳转过来
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
@@ -1951,6 +2098,396 @@ export class WebSocketTestController {
|
||||
window.addEventListener('error', function(event) {
|
||||
addMessage('error', '❌ 页面错误: ' + event.error.message);
|
||||
});
|
||||
|
||||
// ==================== 通知系统功能 ====================
|
||||
|
||||
// 切换功能模式
|
||||
function switchMode(mode) {
|
||||
console.log('switchMode called with:', mode);
|
||||
currentMode = mode;
|
||||
const chatMode = document.getElementById('chatMode');
|
||||
const noticeMode = document.getElementById('noticeMode');
|
||||
const chatBtn = document.getElementById('chatModeBtn');
|
||||
const noticeBtn = document.getElementById('noticeModeBtn');
|
||||
const logTitle = document.getElementById('logTitle');
|
||||
const apiLogTitle = document.getElementById('apiLogTitle');
|
||||
const apiLogSubtitle = document.getElementById('apiLogSubtitle');
|
||||
|
||||
console.log('Elements found:', {
|
||||
chatMode: !!chatMode,
|
||||
noticeMode: !!noticeMode,
|
||||
chatBtn: !!chatBtn,
|
||||
noticeBtn: !!noticeBtn
|
||||
});
|
||||
|
||||
if (mode === 'chat') {
|
||||
if (chatMode) chatMode.style.display = 'block';
|
||||
if (noticeMode) noticeMode.style.display = 'none';
|
||||
if (chatBtn) chatBtn.style.backgroundColor = '#1976d2';
|
||||
if (noticeBtn) noticeBtn.style.backgroundColor = '#666';
|
||||
if (logTitle) logTitle.textContent = '📋 消息日志';
|
||||
if (apiLogTitle) apiLogTitle.textContent = '📡 API 调用日志';
|
||||
if (apiLogSubtitle) apiLogSubtitle.textContent = '实时监控前端API调用';
|
||||
addMessage('system', '🔄 已切换到聊天测试模式');
|
||||
console.log('Switched to chat mode');
|
||||
} else {
|
||||
console.log('Switching to notice mode...');
|
||||
if (chatMode) {
|
||||
chatMode.style.display = 'none';
|
||||
console.log('Chat mode hidden');
|
||||
}
|
||||
if (noticeMode) {
|
||||
noticeMode.style.display = 'block';
|
||||
console.log('Notice mode shown, display style:', noticeMode.style.display);
|
||||
console.log('Notice mode computed style:', window.getComputedStyle(noticeMode).display);
|
||||
}
|
||||
if (chatBtn) chatBtn.style.backgroundColor = '#666';
|
||||
if (noticeBtn) noticeBtn.style.backgroundColor = '#1976d2';
|
||||
if (logTitle) logTitle.textContent = '🔔 通知日志';
|
||||
if (apiLogTitle) apiLogTitle.textContent = '📡 通知API日志';
|
||||
if (apiLogSubtitle) apiLogSubtitle.textContent = '实时监控通知API调用';
|
||||
addMessage('system', '🔄 已切换到通知测试模式');
|
||||
console.log('Notice mode switch completed');
|
||||
|
||||
// 自动复制Token
|
||||
copyTokenFromChat();
|
||||
}
|
||||
}
|
||||
|
||||
// 从聊天模式复制Token
|
||||
function copyTokenFromChat() {
|
||||
const chatToken = document.getElementById('jwtToken').value.trim();
|
||||
if (chatToken) {
|
||||
document.getElementById('noticeJwtToken').value = chatToken;
|
||||
addMessage('system', '✅ 已从聊天模式复制Token');
|
||||
} else {
|
||||
addMessage('system', '💡 聊天模式中没有Token,请先在聊天模式获取Token');
|
||||
}
|
||||
}
|
||||
|
||||
// 切换通知WebSocket连接
|
||||
function toggleNoticeConnection() {
|
||||
if (noticeWs && noticeWs.readyState === WebSocket.OPEN) {
|
||||
noticeWs.close();
|
||||
} else {
|
||||
connectNotice();
|
||||
}
|
||||
}
|
||||
|
||||
// 连接通知WebSocket
|
||||
function connectNotice() {
|
||||
const url = document.getElementById('noticeWsUrl').value;
|
||||
updateNoticeStatus('connecting', '连接中...');
|
||||
|
||||
try {
|
||||
noticeWs = new WebSocket(url);
|
||||
|
||||
noticeWs.onopen = function() {
|
||||
updateNoticeStatus('connected', '已连接');
|
||||
document.getElementById('noticeConnectBtn').textContent = '断开连接';
|
||||
addMessage('system', '✅ 通知WebSocket连接成功');
|
||||
};
|
||||
|
||||
noticeWs.onmessage = function(event) {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
addMessage('received', '🔔 收到通知: ' + JSON.stringify(data, null, 2));
|
||||
|
||||
if (data.type === 'notice') {
|
||||
showNotificationPopup(data.data);
|
||||
} else if (data.type === 'authenticated') {
|
||||
noticeAuthenticated = true;
|
||||
addMessage('system', '✅ 通知系统认证成功');
|
||||
} else if (data.type === 'pong') {
|
||||
addMessage('system', '🏓 收到pong响应');
|
||||
}
|
||||
} catch (e) {
|
||||
addMessage('received', '🔔 ' + event.data);
|
||||
}
|
||||
};
|
||||
|
||||
noticeWs.onclose = function() {
|
||||
updateNoticeStatus('disconnected', '未连接');
|
||||
document.getElementById('noticeConnectBtn').textContent = '连接通知系统';
|
||||
noticeAuthenticated = false;
|
||||
addMessage('system', '🔌 通知WebSocket连接已关闭');
|
||||
};
|
||||
|
||||
noticeWs.onerror = function(error) {
|
||||
addMessage('error', '❌ 通知连接错误: ' + error);
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
updateNoticeStatus('disconnected', '连接失败');
|
||||
addMessage('error', '❌ 通知连接失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新通知连接状态
|
||||
function updateNoticeStatus(status, message) {
|
||||
const statusEl = document.getElementById('noticeConnectionStatus');
|
||||
statusEl.className = 'status ' + status;
|
||||
statusEl.textContent = message;
|
||||
}
|
||||
|
||||
// 认证通知连接
|
||||
function authenticateNotice() {
|
||||
if (!noticeWs || noticeWs.readyState !== WebSocket.OPEN) {
|
||||
addMessage('error', '❌ 请先建立通知WebSocket连接');
|
||||
return;
|
||||
}
|
||||
|
||||
const userId = document.getElementById('noticeUserId').value.trim();
|
||||
if (!userId) {
|
||||
addMessage('error', '❌ 请输入用户ID');
|
||||
return;
|
||||
}
|
||||
|
||||
const message = {
|
||||
event: 'authenticate',
|
||||
data: { userId: parseInt(userId) }
|
||||
};
|
||||
|
||||
noticeWs.send(JSON.stringify(message));
|
||||
addMessage('sent', '📤 发送认证: ' + JSON.stringify(message, null, 2));
|
||||
}
|
||||
|
||||
// 通过API发送通知
|
||||
async function sendNoticeViaAPI() {
|
||||
const title = document.getElementById('noticeTitle').value.trim();
|
||||
const content = document.getElementById('noticeContent').value.trim();
|
||||
const type = document.getElementById('noticeType').value;
|
||||
const targetUserId = document.getElementById('targetUserId').value.trim();
|
||||
const scheduledTime = document.getElementById('scheduledTime').value;
|
||||
const token = document.getElementById('noticeJwtToken').value.trim();
|
||||
|
||||
if (!title || !content) {
|
||||
addMessage('error', '❌ 请输入通知标题和内容');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
addMessage('error', '❌ 请输入JWT Token');
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
title,
|
||||
content,
|
||||
type,
|
||||
userId: targetUserId ? parseInt(targetUserId) : undefined,
|
||||
scheduledAt: scheduledTime || undefined
|
||||
};
|
||||
|
||||
try {
|
||||
let endpoint = '/api/notices';
|
||||
if (type === 'system') {
|
||||
endpoint = '/api/notices/system';
|
||||
} else if (type === 'broadcast') {
|
||||
endpoint = '/api/notices/broadcast';
|
||||
}
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + token
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
addMessage('system', '✅ 通知发送成功: ' + JSON.stringify(result, null, 2));
|
||||
clearNoticeForm();
|
||||
} else {
|
||||
addMessage('error', '❌ 通知发送失败: ' + (result.message || '未知错误'));
|
||||
}
|
||||
} catch (error) {
|
||||
addMessage('error', '❌ 请求失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 通过WebSocket发送通知 (这里只是演示,实际通知应该通过API发送)
|
||||
function sendNoticeViaWS() {
|
||||
if (!noticeWs || noticeWs.readyState !== WebSocket.OPEN) {
|
||||
addMessage('error', '❌ 请先建立通知WebSocket连接');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!noticeAuthenticated) {
|
||||
addMessage('error', '❌ 请先认证通知连接');
|
||||
return;
|
||||
}
|
||||
|
||||
const message = {
|
||||
event: 'ping'
|
||||
};
|
||||
|
||||
noticeWs.send(JSON.stringify(message));
|
||||
addMessage('sent', '📤 发送ping: ' + JSON.stringify(message, null, 2));
|
||||
}
|
||||
|
||||
// 发送快速通知
|
||||
function sendQuickNotice(title, content) {
|
||||
document.getElementById('noticeTitle').value = title;
|
||||
document.getElementById('noticeContent').value = content;
|
||||
document.getElementById('noticeType').value = 'system';
|
||||
sendNoticeViaAPI();
|
||||
}
|
||||
|
||||
// 加载通知列表
|
||||
async function loadNoticeList() {
|
||||
const token = document.getElementById('noticeJwtToken').value.trim();
|
||||
|
||||
if (!token) {
|
||||
addMessage('error', '❌ 请输入JWT Token');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/notices', {
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + token
|
||||
}
|
||||
});
|
||||
|
||||
const notices = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
displayNoticeList(notices);
|
||||
addMessage('system', '✅ 通知列表加载成功,共 ' + notices.length + ' 条');
|
||||
} else {
|
||||
addMessage('error', '❌ 加载通知列表失败: ' + (notices.message || '未知错误'));
|
||||
}
|
||||
} catch (error) {
|
||||
addMessage('error', '❌ 请求失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取未读通知数量
|
||||
async function getUnreadCount() {
|
||||
const token = document.getElementById('noticeJwtToken').value.trim();
|
||||
|
||||
if (!token) {
|
||||
addMessage('error', '❌ 请输入JWT Token');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/notices/unread-count', {
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + token
|
||||
}
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
addMessage('system', '📊 未读通知数量: ' + result.count);
|
||||
} else {
|
||||
addMessage('error', '❌ 获取未读数量失败: ' + (result.message || '未知错误'));
|
||||
}
|
||||
} catch (error) {
|
||||
addMessage('error', '❌ 请求失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示通知列表
|
||||
function displayNoticeList(notices) {
|
||||
const listEl = document.getElementById('noticeList');
|
||||
listEl.innerHTML = '';
|
||||
|
||||
if (notices.length === 0) {
|
||||
listEl.innerHTML = '<div style="text-align: center; color: #666;">暂无通知</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
notices.forEach(notice => {
|
||||
const noticeEl = document.createElement('div');
|
||||
noticeEl.style.cssText = 'border: 1px solid #ddd; padding: 8px; margin-bottom: 8px; border-radius: 4px; background: white;';
|
||||
|
||||
const statusColor = notice.status === 'read' ? '#666' : '#1976d2';
|
||||
const statusText = notice.status === 'read' ? '已读' : '未读';
|
||||
|
||||
noticeEl.innerHTML = \`
|
||||
<div style="font-weight: bold; margin-bottom: 4px;">\${notice.title}</div>
|
||||
<div style="margin-bottom: 4px;">\${notice.content}</div>
|
||||
<div style="font-size: 12px; color: #666;">
|
||||
类型: \${notice.type} |
|
||||
状态: <span style="color: \${statusColor};">\${statusText}</span> |
|
||||
时间: \${new Date(notice.createdAt).toLocaleString()}
|
||||
\${notice.status !== 'read' ?
|
||||
\`<button onclick="markNoticeAsRead(\${notice.id})" style="margin-left: 10px; padding: 2px 6px; font-size: 11px; background-color: #4caf50; color: white; border: none; border-radius: 2px; cursor: pointer;">标记已读</button>\` :
|
||||
''
|
||||
}
|
||||
</div>
|
||||
\`;
|
||||
|
||||
listEl.appendChild(noticeEl);
|
||||
});
|
||||
}
|
||||
|
||||
// 标记通知为已读
|
||||
async function markNoticeAsRead(noticeId) {
|
||||
const token = document.getElementById('noticeJwtToken').value.trim();
|
||||
|
||||
if (!token) {
|
||||
addMessage('error', '❌ 请输入JWT Token');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(\`/api/notices/\${noticeId}/read\`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + token
|
||||
}
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
addMessage('system', '✅ 通知已标记为已读');
|
||||
loadNoticeList(); // 刷新列表
|
||||
} else {
|
||||
addMessage('error', '❌ 标记失败: ' + (result.message || '未知错误'));
|
||||
}
|
||||
} catch (error) {
|
||||
addMessage('error', '❌ 请求失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示通知弹窗
|
||||
function showNotificationPopup(notice) {
|
||||
// 使用浏览器通知API
|
||||
if (Notification.permission === 'granted') {
|
||||
new Notification(notice.title, {
|
||||
body: notice.content,
|
||||
icon: '/favicon.ico'
|
||||
});
|
||||
}
|
||||
|
||||
// 在页面上显示
|
||||
addMessage('system', \`🔔 新通知: \${notice.title} - \${notice.content}\`);
|
||||
}
|
||||
|
||||
// 清空通知表单
|
||||
function clearNoticeForm() {
|
||||
document.getElementById('noticeTitle').value = '';
|
||||
document.getElementById('noticeContent').value = '';
|
||||
document.getElementById('targetUserId').value = '';
|
||||
document.getElementById('scheduledTime').value = '';
|
||||
}
|
||||
|
||||
// 清空通知日志
|
||||
function clearNoticeLog() {
|
||||
if (currentMode === 'notice') {
|
||||
clearLog();
|
||||
addMessage('system', '🗑️ 通知日志已清空');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user