171 lines
7.1 KiB
PHP
171 lines
7.1 KiB
PHP
<?php
|
|
session_start();
|
|
require_once __DIR__ . '/../db/config.php';
|
|
|
|
// Check if admin
|
|
if (!isset($_SESSION['admin_id'])) {
|
|
header("Location: /admin/login.php");
|
|
exit;
|
|
}
|
|
|
|
// In a real app, check for admin role here
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="zh">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Byro Admin | 客服系统</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
|
<style>
|
|
body { background-color: #f0f2f5; font-family: 'Noto Sans SC', sans-serif; height: 100vh; margin: 0; }
|
|
.sidebar { width: 350px; background: #fff; border-right: 1px solid #ddd; display: flex; flex-direction: column; }
|
|
.main-chat { flex: 1; display: flex; flex-direction: column; background: #fff; }
|
|
.user-list { flex: 1; overflow-y: auto; }
|
|
.user-card { padding: 15px; border-bottom: 1px solid #f0f0f0; cursor: pointer; transition: all 0.2s; }
|
|
.user-card:hover { background: #f8f9fa; }
|
|
.user-card.active { background: #e7f3ff; border-left: 5px solid #007bff; }
|
|
.chat-header { padding: 15px 25px; border-bottom: 1px solid #ddd; background: #fff; z-index: 10; }
|
|
.messages-area { flex: 1; padding: 25px; background: #f9f9f9; overflow-y: auto; display: flex; flex-direction: column; gap: 10px; }
|
|
.msg { max-width: 75%; padding: 10px 15px; border-radius: 18px; font-size: 14px; line-height: 1.5; }
|
|
.msg-admin { align-self: flex-end; background: #007bff; color: #fff; border-bottom-right-radius: 2px; }
|
|
.msg-user { align-self: flex-start; background: #fff; color: #333; border: 1px solid #eee; border-bottom-left-radius: 2px; box-shadow: 0 2px 5px rgba(0,0,0,0.05); }
|
|
.chat-input-area { padding: 20px; border-top: 1px solid #ddd; background: #fff; }
|
|
.status-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; margin-right: 5px; }
|
|
.status-online { background: #28a745; }
|
|
</style>
|
|
</head>
|
|
<body class="d-flex">
|
|
|
|
<div class="sidebar">
|
|
<div class="p-3 bg-primary text-white d-flex justify-content-between align-items-center">
|
|
<div class="d-flex align-items-center gap-2">
|
|
<a href="/admin/index.php" class="text-white"><i class="bi bi-arrow-left"></i></a>
|
|
<h5 class="m-0 fw-bold">客服中心</h5>
|
|
</div>
|
|
<span class="badge bg-light text-primary">Live</span>
|
|
</div>
|
|
<div class="p-2">
|
|
<input type="text" class="form-control form-control-sm" placeholder="搜索用户 UID / IP">
|
|
</div>
|
|
<div class="user-list" id="user-list">
|
|
<!-- User cards -->
|
|
</div>
|
|
</div>
|
|
|
|
<div class="main-chat">
|
|
<div class="chat-header" id="chat-header" style="display: none;">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h5 class="m-0 fw-bold" id="header-name">用户名称</h5>
|
|
<div class="small text-muted" id="header-meta">UID: --- | IP: ---</div>
|
|
</div>
|
|
<div class="text-end">
|
|
<span class="status-dot status-online"></span>
|
|
<span class="small text-success fw-bold">正在对话</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="messages-area" id="messages-area">
|
|
<div class="m-auto text-center text-muted">
|
|
<i class="bi bi-chat-left-dots fs-1 d-block mb-3"></i>
|
|
<p>从左侧选择一个会话开始实时对话</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="chat-input-area" id="input-area" style="display: none;">
|
|
<form id="chat-form" class="d-flex gap-2">
|
|
<input type="text" id="msg-input" class="form-control" placeholder="输入消息回复用户..." autocomplete="off">
|
|
<button type="submit" class="btn btn-primary px-4 fw-bold">发送回复</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let selectedUser = null;
|
|
let selectedIp = null;
|
|
let lastMsgId = 0;
|
|
|
|
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');
|
|
|
|
let html = '';
|
|
users.forEach(u => {
|
|
const isActive = (selectedIp === u.ip_address && selectedUser == u.user_id);
|
|
html += `
|
|
<div class="user-card ${isActive ? 'active' : ''}" onclick="openChat('${u.user_id}', '${u.ip_address}', '${u.username || '匿名用户'}', '${u.uid || '---'}')">
|
|
<div class="d-flex justify-content-between mb-1">
|
|
<span class="fw-bold">${u.username || '匿名用户'}</span>
|
|
<span class="small text-muted">${new Date(u.created_at).toLocaleTimeString()}</span>
|
|
</div>
|
|
<div class="small text-truncate text-muted mb-1">${u.message}</div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span class="badge bg-light text-dark border" style="font-size: 10px;">UID: ${u.uid || '---'}</span>
|
|
<span class="text-primary" style="font-size: 10px;">${u.ip_address}</span>
|
|
</div>
|
|
</div>
|
|
`;
|
|
});
|
|
list.innerHTML = html;
|
|
}
|
|
|
|
function openChat(userId, ip, name, uid) {
|
|
selectedUser = userId;
|
|
selectedIp = ip;
|
|
document.getElementById('header-name').innerText = name;
|
|
document.getElementById('header-meta').innerText = `UID: ${uid} | IP: ${ip}`;
|
|
document.getElementById('chat-header').style.display = 'block';
|
|
document.getElementById('input-area').style.display = 'block';
|
|
lastMsgId = 0;
|
|
fetchMessages();
|
|
}
|
|
|
|
async function fetchMessages() {
|
|
if (!selectedIp) return;
|
|
const r = await fetch(`/api/chat.php?action=get_messages&user_id=${selectedUser}&ip=${selectedIp}`);
|
|
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) {
|
|
area.innerHTML = '';
|
|
filtered.forEach(m => {
|
|
const div = document.createElement('div');
|
|
div.className = `msg ${m.sender === 'admin' ? 'msg-admin' : 'msg-user'}`;
|
|
div.innerText = m.message;
|
|
area.appendChild(div);
|
|
});
|
|
area.scrollTop = area.scrollHeight;
|
|
lastMsgId = filtered.length;
|
|
}
|
|
}
|
|
|
|
document.getElementById('chat-form').addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
const input = document.getElementById('msg-input');
|
|
const msg = input.value.trim();
|
|
if (!msg) return;
|
|
|
|
const fd = new URLSearchParams();
|
|
fd.append('message', msg);
|
|
fd.append('user_id', selectedUser);
|
|
fd.append('ip_address', selectedIp);
|
|
|
|
await fetch('/api/chat.php?action=admin_send', { method: 'POST', body: fd });
|
|
input.value = '';
|
|
fetchMessages();
|
|
});
|
|
|
|
setInterval(refreshUsers, 3000);
|
|
setInterval(fetchMessages, 1000);
|
|
refreshUsers();
|
|
</script>
|
|
</body>
|
|
</html>
|