-
-
-
- 用户名称
-UID: --- | IP: ---
-
-
- 正在对话
+
+
+
-
+
-
+
+
+ 用户名称
+IP: ---
+
+
+ 在线
+
-
-
-
- 从左侧选择一个会话开始实时对话
+
+
+
+
+
+
+ 请从左侧选择一个会话
+
+
-
+
+
@@ -92,20 +179,31 @@ async function refreshUsers() {
const r = await fetch('/api/chat.php?action=admin_get_all');
const users = await r.json();
const list = document.getElementById('user-list');
+ const search = document.getElementById('user-search').value.toLowerCase();
let html = '';
users.forEach(u => {
- const isActive = (selectedIp === u.ip_address && selectedUser == u.user_id);
+ const username = u.username || '匿名用户';
+ const uid = u.uid || '---';
+ const ip = u.ip_address;
+ const remark = u.remark || '';
+
+ if (search && !username.toLowerCase().includes(search) && !ip.includes(search) && !uid.toString().includes(search)) {
+ return;
+ }
+
+ const isActive = (selectedIp === ip && selectedUser == u.user_id);
html += `
-
+
+
用户备注
+
+
+
+
+
+
+ +
+
UID: ---
+ 当前IP: ---
+ 最近活跃: ---
+
+
= $title ?> - = $site_name ?>
-
-
+
+
- ${u.username || '匿名用户'}
- ${new Date(u.created_at).toLocaleTimeString()}
+ ${username}
+ ${new Date(u.created_at).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}
- ${u.message}
-
- UID: ${u.uid || '---'}
- ${u.ip_address}
+ ${remark ? `
`;
@@ -113,15 +211,22 @@ async function refreshUsers() {
list.innerHTML = html;
}
-function openChat(userId, ip, name, uid) {
+function openChat(userId, ip, name, uid, remark) {
selectedUser = userId;
selectedIp = ip;
document.getElementById('header-name').innerText = name;
- document.getElementById('header-meta').innerText = `UID: ${uid} | IP: ${ip}`;
+ document.getElementById('info-ip-header').innerText = ip;
document.getElementById('chat-header').style.display = 'block';
document.getElementById('input-area').style.display = 'block';
+ document.getElementById('remark-area').style.display = 'block';
+
+ document.getElementById('remark-text').value = remark;
+ document.getElementById('info-uid').innerText = uid;
+ document.getElementById('info-ip').innerText = ip;
+
lastMsgId = 0;
fetchMessages();
+ refreshUsers(); // Refresh list to update active state
}
async function fetchMessages() {
@@ -130,7 +235,6 @@ async function fetchMessages() {
const msgs = await r.json();
const area = document.getElementById('messages-area');
- // Simple filter for the specific conversation
const filtered = msgs.filter(m => m.ip_address === selectedIp && (m.user_id == selectedUser || m.user_id == 0));
if (filtered.length > lastMsgId) {
@@ -143,9 +247,39 @@ async function fetchMessages() {
});
area.scrollTop = area.scrollHeight;
lastMsgId = filtered.length;
+
+ if (filtered.length > 0) {
+ document.getElementById('info-time').innerText = new Date(filtered[filtered.length-1].created_at).toLocaleString();
+ }
}
}
+document.getElementById('plus-btn').addEventListener('click', () => {
+ document.getElementById('image-input').click();
+});
+
+document.getElementById('image-input').addEventListener('change', async (e) => {
+ const file = e.target.files[0];
+ if (!file) return;
+
+ const formData = new FormData();
+ formData.append('file', file);
+ formData.append('user_id', selectedUser);
+ formData.append('ip_address', selectedIp);
+
+ const r = await fetch('/api/chat.php?action=upload_image', {
+ method: 'POST',
+ body: formData
+ });
+ const res = await r.json();
+ if (res.success) {
+ fetchMessages();
+ } else {
+ alert('上传失败: ' + res.error);
+ }
+ e.target.value = ''; // Reset
+});
+
document.getElementById('chat-form').addEventListener('submit', async (e) => {
e.preventDefault();
const input = document.getElementById('msg-input');
@@ -162,16 +296,29 @@ document.getElementById('chat-form').addEventListener('submit', async (e) => {
fetchMessages();
});
-// Clear notifications when on this page
-function clearNotifications() {
- fetch('/api/admin_notifications.php?action=clear&type=messages');
-}
-setInterval(clearNotifications, 5000);
-clearNotifications();
+document.getElementById('save-remark-btn').addEventListener('click', async () => {
+ const remark = document.getElementById('remark-text').value;
+ const fd = new URLSearchParams();
+ fd.append('user_id', selectedUser);
+ fd.append('ip_address', selectedIp);
+ fd.append('remark', remark);
-setInterval(refreshUsers, 3000);
-setInterval(fetchMessages, 1000);
+ const r = await fetch('/api/chat.php?action=save_remark', { method: 'POST', body: fd });
+ const res = await r.json();
+ if (res.success) {
+ alert('备注已保存');
+ refreshUsers();
+ }
+});
+
+document.getElementById('user-search').addEventListener('input', refreshUsers);
+
+setInterval(refreshUsers, 5000);
+setInterval(fetchMessages, 2000);
refreshUsers();
-
-
+
+
diff --git a/admin/layout.php b/admin/layout.php
index fb18bb6..9ef20e2 100644
--- a/admin/layout.php
+++ b/admin/layout.php
@@ -33,12 +33,19 @@ function renderAdminPage($content, $title = '后台管理') {
global $admin, $lang;
$current_page = basename($_SERVER['PHP_SELF']);
- $site_logo = '';
+ $site_logo = '/assets/images/logo.png';
+ $site_favicon = '';
$site_name = 'Byro';
try {
$stmt = db()->prepare("SELECT setting_value FROM system_settings WHERE setting_key = 'site_logo'");
$stmt->execute();
- $site_logo = $stmt->fetchColumn() ?: '';
+ $val = $stmt->fetchColumn();
+ if ($val) $site_logo = $val;
+
+ $stmt = db()->prepare("SELECT setting_value FROM system_settings WHERE setting_key = 'site_favicon'");
+ $stmt->execute();
+ $val = $stmt->fetchColumn();
+ $site_favicon = $val ?: $site_logo;
$stmt = db()->prepare("SELECT setting_value FROM system_settings WHERE setting_key = 'site_name'");
$stmt->execute();
@@ -53,8 +60,8 @@ function renderAdminPage($content, $title = '后台管理') {
[备注: ${remark}]
` : ''}
+ ${u.message}
+
+ UID: ${uid}
+ ${ip}