no message

This commit is contained in:
2025-10-18 14:46:52 +08:00
commit 7a84025b05
387 changed files with 75711 additions and 0 deletions

234
user/admin_reply_ticket.php Normal file
View File

@@ -0,0 +1,234 @@
<?php
include './admin_head.php';
$a_user = $_GET['user'];
$a_pass = $_GET['pass'];
?>
<main class="h-full overflow-y-auto">
<div class="container px-6 mx-auto grid">
<h2 class="my-6 text-2xl font-semibold text-gray-700 dark:text-gray-200">
工单对话
</h2>
<div class="w-full overflow-hidden rounded-lg shadow-xl">
<div class="w-full overflow-x-auto">
<table class="w-full whitespace-no-wrap">
<thead>
<tr class="text-xs font-semibold tracking-wide text-left text-gray-500 uppercase border-b dark:border-gray-700 bg-gray-50 dark:text-gray-400 dark:bg-gray-800">
<th class="px-4 py-3">工单信息</th>
<th class="px-4 py-3">紧急程度</th>
<th class="px-4 py-3">工单状态</th>
<th class="px-4 py-3">回复时间</th>
</tr>
</thead>
<tbody id="active_ticket" class="bg-white divide-y dark:divide-gray-700 dark:bg-gray-800">
<tr class="text-gray-700 dark:text-gray-400">
<td class="px-4 py-3">
<div class="flex items-center text-sm">
<!-- Avatar with inset shadow -->
<div class="relative hidden md:block rounded-full p-3 mr-4 text-teal-500 bg-teal-100 rounded-full dark:text-teal-100 dark:bg-teal-500">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 5v8a2 2 0 01-2 2h-5l-5 4v-4H4a2 2 0 01-2-2V5a2 2 0 012-2h12a2 2 0 012 2zM7 8H5v2h2V8zm2 0h2v2H9V8zm6 0h-2v2h2V8z" clip-rule="evenodd"></path>
</svg>
</div>
<div>
<p id="title" class="font-semibold">载入中...</p>
<p id="related_product" class="text-xs text-gray-600 dark:text-gray-400">
载入中...
</p>
</div>
</div>
</td>
<td id="priority" class="px-4 py-3 text-sm">
载入中...
</td>
<td class="px-4 py-3 text-xs">
<span id="status" class="px-2 py-1 font-semibold leading-tight text-green-700 bg-green-100 rounded-full dark:bg-green-700 dark:text-green-100">
载入中...
</span>
</td>
<td id="last_reply_time" class="px-4 py-3 text-sm">
载入中...
</td>
</tr>
</tbody>
</table>
</div>
<div class="grid px-4 py-3 text-xs font-semibold tracking-wide text-gray-500 uppercase border-t dark:border-gray-700 bg-gray-50 sm:grid-cols-9 dark:text-gray-400 dark:bg-gray-800">
<span class="flex items-center col-span-3">
FurryBOX Studio
</span>
<span class="col-span-2"></span>
</div>
</div>
<br>
<div id="chat">
</div>
<br>
<form action="./admin_api_reply_ticket.php?user=<?php echo $a_user;?>&pass=<?php echo $a_pass;?>" method="POST">
<div class="px-4 py-3 mb-8 bg-white rounded-lg shadow-md dark:bg-gray-800">
<label class="block text-sm">
<span class="text-gray-700 dark:text-gray-400">昵称</span>
<input name="name" class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 form-textarea focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray" rows="3" placeholder="客户端显示的昵称"></input>
</label>
<label class="block text-sm">
<span class="text-gray-700 dark:text-gray-400">回复</span>
<textarea name="msg" class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 form-textarea focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray" rows="3" placeholder="回复的内容"></textarea>
</label>
<button type="submit" class="block w-full px-4 py-2 mt-4 text-sm font-medium leading-5 text-center text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-lg active:bg-purple-600 hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple">
回复
</button>
</form>
<form action="./admin_end_ticket.php?user=<?php echo $a_user;?>&pass=<?php echo $a_pass;?>" method="POST">
<input
name="不用在意这些乱七八糟的东西"
type="text"
style="position: absolute; width: 0; height: 0; opacity: 0; pointer-events: none;"
readonly>
<button type="submit" class="block w-full px-4 py-2 mt-4 text-sm font-medium leading-5 text-center text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-lg active:bg-purple-600 hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple">
结单
</button>
</form>
</div>
<br><br><br><br><br>
<script>
let cache = null;
let lastAudioPlayTime = 0; // 记录上一次播放音频的时间
const audioInterval = 5000; // 设置音频播放的间隔,单位为毫秒
function fetchTicket() {
fetch('./admin_get_ticket.php?user=<?php echo $a_user;?>&pass=<?php echo $a_pass;?>')
.then(response => response.text())
.then(data => {
if (data === '无') return; // 如果返回无,则不进行任何操作
const ticketData = JSON.parse(data);
const { ticket } = ticketData;
// 缓存第一次请求的数据
if (!cache) {
cache = ticket;
updatePage(ticket);
// 在第一次加载时,如果最后一条记录是客服回复,则进行提示
const lastConversation = ticket.conversation[ticket.conversation.length - 1];
if (lastConversation.role === 'admin') {
handleCustomerServiceReply();
}
} else {
// 对比缓存和新数据
if (JSON.stringify(cache) !== JSON.stringify(ticket)) {
handleUpdates(ticket);
}
}
})
.catch(err => console.error('Error fetching ticket:', err));
}
function updatePage(ticket) {
document.getElementById('title').innerText = ticket.title;
document.getElementById('last_reply_time').innerText = ticket.last_reply_time;
document.getElementById('status').innerText = ticket.status;
document.getElementById('priority').innerText = ticket.priority;
document.getElementById('related_product').innerText = ticket.related_product;
const chatElement = document.getElementById('chat');
chatElement.innerHTML = ''; // 清空之前的聊天记录
ticket.conversation.forEach(item => {
const isAdmin = item.role === 'admin';
const html = `
<br>
<div class="rk-tip-in bg-white p-4 rounded-lg dark:bg-gray-800 shadow-xl">
<div class="flex items-center">
<div class="p-3 mr-4 ${isAdmin ? 'text-orange-500 bg-orange-100' : 'text-blue-500 bg-blue-100'} rounded-full dark:text-${isAdmin ? 'orange-100' : 'blue-100'} dark:bg-${isAdmin ? 'orange-500' : 'blue-500'}">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">${isAdmin ? `<path d="M13 6a3 3 0 11-6 0 3 3 0 016 0zM18 8a2 2 0 11-4 0 2 2 0 014 0zM14 15a4 4 0 00-8 0v3h8v-3zM6 8a2 2 0 11-4 0 2 2 0 014 0zM16 18v-3a5.972 5.972 0 00-.75-2.906A3.005 3.005 0 0119 15v3h-3zM4.75 12.094A5.973 5.973 0 004 15v3H1v-3a3 3 0 013.75-2.906z"></path>` : `<path fill-rule="evenodd" d="M18 5v8a2 2 0 01-2 2h-5l-5 4v-4H4a2 2 0 01-2-2V5a2 2 0 012-2h12a2 2 0 012 2zM7 8H5v2h2V8zm2 0h2v2H9V8zm6 0h-2v2h2V8z" clip-rule="evenodd"></path>`}</svg>
</div>
<div>
<p class="mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">${item.reply_time}</p>
<p class="text-lg font-semibold text-gray-700 dark:text-gray-200">${item.responder}</p>
</div>
</div>
<div style="padding-top:10px">
<p class="text-gray-600 dark:text-gray-400 shadow-xs p-4 rounded-lg">${item.reply_content}</p>
</div>
</div>`;
chatElement.insertAdjacentHTML('beforeend', html);
});
}
function handleUpdates(ticket) {
// 检查是否有新的对话记录且为客服回复
const lastConversation = ticket.conversation[ticket.conversation.length - 1];
const isNewCustomerReply = lastConversation.role === 'user' && ticket.conversation.length > cache.conversation.length;
if (isNewCustomerReply) {
handleCustomerServiceReply();
}
// 更新页面
updatePage(ticket);
cache = ticket; // 更新缓存
}
function handleCustomerServiceReply() {
rk.tip('叮叮叮~~', '收到客户回复啦~~', 5);
// 检查时间间隔,避免频繁播放音频
const currentTime = Date.now();
if (currentTime - lastAudioPlayTime >= audioInterval) {
const audio = new Audio('./tip.mp3');
audio.play().catch(error => console.error('Audio play error:', error)); // 捕获播放失败的错误
lastAudioPlayTime = currentTime; // 更新播放时间
}
}
// 启动定时器,每秒钟调用一次 fetchTicket
setInterval(fetchTicket, 1000);
// 页面加载完成后启动 Ticket 查询
document.addEventListener('DOMContentLoaded', (event) => {
fetchTicket(); // 初次获取工单信息
});
</script>