Pro/application/admin/view/chat/index.html
2026-02-25 01:50:31 +08:00

1191 lines
33 KiB
HTML
Executable File

{include file="public/header" /}
<style>
/* 客服工作台整体样式 */
.chat-workbench {
display: flex;
height: calc(100vh - 60px);
background: #f5f5f5;
gap: 10px;
padding: 10px;
}
/* 左侧会话列表 */
.chat-sessions {
width: 280px;
min-width: 280px;
background: #fff;
border-radius: 4px;
display: flex;
flex-direction: column;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
.sessions-header {
padding: 15px;
border-bottom: 1px solid #e8e8e8;
display: flex;
justify-content: space-between;
align-items: center;
}
.sessions-header h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
color: #333;
}
.sessions-tabs {
display: flex;
border-bottom: 1px solid #e8e8e8;
}
.sessions-tabs .tab-item {
flex: 1;
padding: 12px 0;
text-align: center;
cursor: pointer;
font-size: 13px;
color: #666;
border-bottom: 2px solid transparent;
transition: all 0.3s;
}
.sessions-tabs .tab-item:hover {
color: #009688;
}
.sessions-tabs .tab-item.active {
color: #009688;
border-bottom-color: #009688;
}
.sessions-tabs .tab-item .badge {
display: inline-block;
min-width: 18px;
height: 18px;
line-height: 18px;
padding: 0 5px;
font-size: 11px;
background: #ff5722;
color: #fff;
border-radius: 9px;
margin-left: 4px;
}
.sessions-list {
flex: 1;
overflow-y: auto;
}
.session-item {
display: flex;
align-items: center;
padding: 12px 15px;
cursor: pointer;
border-bottom: 1px solid #f5f5f5;
transition: background 0.2s;
}
.session-item:hover {
background: #f8f9fa;
}
.session-item.active {
background: #e8f5e9;
border-left: 3px solid #009688;
}
.session-avatar {
width: 44px;
height: 44px;
border-radius: 50%;
background: linear-gradient(135deg, #009688, #4db6ac);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 16px;
font-weight: 600;
margin-right: 12px;
flex-shrink: 0;
position: relative;
}
.session-avatar .online-dot {
position: absolute;
bottom: 2px;
right: 2px;
width: 10px;
height: 10px;
background: #4caf50;
border: 2px solid #fff;
border-radius: 50%;
}
.session-info {
flex: 1;
min-width: 0;
}
.session-info .name-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 4px;
}
.session-info .username {
font-size: 14px;
font-weight: 500;
color: #333;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.session-info .time {
font-size: 11px;
color: #999;
flex-shrink: 0;
margin-left: 8px;
}
.session-info .last-msg {
font-size: 12px;
color: #999;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.session-badge {
min-width: 20px;
height: 20px;
line-height: 20px;
padding: 0 6px;
font-size: 11px;
background: #ff5722;
color: #fff;
border-radius: 10px;
text-align: center;
margin-left: 8px;
}
.sessions-empty {
padding: 40px 20px;
text-align: center;
color: #999;
}
.sessions-empty i {
font-size: 48px;
color: #ddd;
margin-bottom: 10px;
}
.accept-btn {
padding: 4px 10px;
background: #009688;
color: #fff;
border: none;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
flex-shrink: 0;
margin-left: 8px;
transition: background 0.2s;
}
.accept-btn:hover {
background: #00796b;
}
/* 中间聊天区域 */
.chat-main {
flex: 1;
background: #fff;
border-radius: 4px;
display: flex;
flex-direction: column;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
min-width: 0;
}
.chat-header {
padding: 15px 20px;
border-bottom: 1px solid #e8e8e8;
display: flex;
justify-content: space-between;
align-items: center;
}
.chat-header .chat-title {
display: flex;
align-items: center;
}
.chat-header .chat-title h3 {
margin: 0;
font-size: 15px;
font-weight: 600;
color: #333;
}
.chat-header .chat-status {
display: flex;
align-items: center;
margin-left: 15px;
font-size: 12px;
}
.chat-header .chat-status .status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 5px;
}
.chat-header .chat-status .status-dot.online {
background: #4caf50;
}
.chat-header .chat-status .status-dot.offline {
background: #9e9e9e;
}
.ws-status {
display: inline-flex;
align-items: center;
font-size: 12px;
padding: 4px 10px;
border-radius: 12px;
background: #ffebee;
color: #e53935;
}
.ws-status.connected {
background: #e8f5e9;
color: #43a047;
}
.ws-status i {
margin-right: 4px;
}
/* 消息区域 */
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 20px;
background: #f5f5f5;
}
.message-placeholder {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #999;
}
.message-placeholder i {
font-size: 64px;
color: #ddd;
margin-bottom: 15px;
}
.message-placeholder p {
font-size: 14px;
}
.message-group {
margin-bottom: 20px;
}
.message-date {
text-align: center;
margin: 15px 0;
}
.message-date span {
display: inline-block;
padding: 4px 12px;
background: rgba(0,0,0,0.1);
border-radius: 12px;
font-size: 11px;
color: #666;
}
.message-row {
display: flex;
margin-bottom: 12px;
}
.message-row.user {
justify-content: flex-start;
}
.message-row.agent {
justify-content: flex-end;
}
.message-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
background: #009688;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 14px;
flex-shrink: 0;
}
.message-row.user .message-avatar {
margin-right: 10px;
background: #607d8b;
}
.message-row.agent .message-avatar {
margin-left: 10px;
order: 2;
}
.message-content {
max-width: 60%;
}
.message-bubble {
padding: 10px 14px;
border-radius: 12px;
font-size: 14px;
line-height: 1.5;
word-break: break-word;
}
.message-row.user .message-bubble {
background: #fff;
color: #333;
border-top-left-radius: 4px;
}
.message-row.agent .message-bubble {
background: #009688;
color: #fff;
border-top-right-radius: 4px;
}
.message-bubble img {
max-width: 200px;
border-radius: 8px;
cursor: pointer;
}
.message-time {
font-size: 11px;
color: #999;
margin-top: 4px;
}
.message-row.agent .message-time {
text-align: right;
}
.typing-indicator {
display: none;
padding: 10px 20px;
color: #999;
font-size: 12px;
}
.typing-indicator.show {
display: block;
}
/* 输入区域 */
.chat-input-area {
border-top: 1px solid #e8e8e8;
background: #fff;
}
.input-toolbar {
padding: 8px 15px;
display: flex;
align-items: center;
gap: 5px;
border-bottom: 1px solid #f0f0f0;
}
.input-toolbar .tool-btn {
height: 28px;
padding: 0 12px;
border: 1px solid #e8e8e8;
background: #fff;
border-radius: 4px;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
color: #666;
font-size: 12px;
transition: all 0.2s;
}
.input-toolbar .tool-btn:hover:not(:disabled) {
background: #f5f5f5;
color: #009688;
border-color: #009688;
}
.input-toolbar .tool-btn:disabled {
color: #ccc;
cursor: not-allowed;
border-color: #eee;
}
.input-wrapper {
padding: 10px 15px;
display: flex;
align-items: flex-end;
gap: 10px;
}
.input-wrapper textarea {
flex: 1;
border: 1px solid #e8e8e8;
border-radius: 8px;
padding: 10px 12px;
font-size: 14px;
resize: none;
outline: none;
min-height: 60px;
max-height: 120px;
transition: border-color 0.2s;
}
.input-wrapper textarea:focus {
border-color: #009688;
}
.input-wrapper textarea:disabled {
background: #f5f5f5;
}
.send-btn {
padding: 10px 24px;
background: #009688;
color: #fff;
border: none;
border-radius: 8px;
font-size: 14px;
cursor: pointer;
transition: background 0.2s;
white-space: nowrap;
}
.send-btn:hover:not(:disabled) {
background: #00796b;
}
.send-btn:disabled {
background: #ccc;
cursor: not-allowed;
}
/* 快捷回复面板 */
.quick-reply-panel {
display: none;
position: absolute;
bottom: 100%;
left: 0;
right: 0;
background: #fff;
border: 1px solid #e8e8e8;
border-radius: 8px 8px 0 0;
box-shadow: 0 -2px 8px rgba(0,0,0,0.1);
max-height: 200px;
overflow-y: auto;
}
.quick-reply-panel.show {
display: block;
}
.quick-reply-item {
padding: 10px 15px;
cursor: pointer;
border-bottom: 1px solid #f5f5f5;
transition: background 0.2s;
}
.quick-reply-item:hover {
background: #f8f9fa;
}
.quick-reply-item .title {
font-size: 13px;
font-weight: 500;
color: #333;
margin-bottom: 3px;
}
.quick-reply-item .content {
font-size: 12px;
color: #999;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* 右侧用户信息面板 */
.chat-user-panel {
width: 260px;
min-width: 260px;
background: #fff;
border-radius: 4px;
display: none;
flex-direction: column;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
.chat-user-panel.show {
display: flex;
}
.user-profile {
padding: 25px 20px;
text-align: center;
border-bottom: 1px solid #e8e8e8;
}
.user-profile .avatar {
width: 72px;
height: 72px;
border-radius: 50%;
background: linear-gradient(135deg, #009688, #4db6ac);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 28px;
font-weight: 600;
margin: 0 auto 12px;
}
.user-profile .username {
font-size: 16px;
font-weight: 600;
color: #333;
margin-bottom: 5px;
}
.user-profile .source-tag {
display: inline-block;
padding: 3px 10px;
background: #e8f5e9;
color: #009688;
border-radius: 12px;
font-size: 12px;
}
.user-details {
flex: 1;
padding: 15px;
overflow-y: auto;
}
.detail-item {
display: flex;
justify-content: space-between;
padding: 10px 0;
border-bottom: 1px solid #f5f5f5;
}
.detail-item .label {
font-size: 13px;
color: #999;
}
.detail-item .value {
font-size: 13px;
color: #333;
font-weight: 500;
}
.detail-item .value.money {
color: #ff9800;
}
.user-actions {
padding: 15px;
border-top: 1px solid #e8e8e8;
}
.user-actions .action-btn {
width: 100%;
padding: 10px;
margin-bottom: 8px;
border: 1px solid #e8e8e8;
background: #fff;
border-radius: 6px;
font-size: 13px;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
.user-actions .action-btn i {
margin-right: 6px;
}
.user-actions .action-btn:hover {
border-color: #009688;
color: #009688;
}
.user-actions .action-btn.danger {
color: #e53935;
}
.user-actions .action-btn.danger:hover {
border-color: #e53935;
background: #ffebee;
}
</style>
<body>
<div class="chat-workbench">
<!-- 左侧会话列表 -->
<div class="chat-sessions">
<div class="sessions-header">
<h3>会话列表</h3>
<button class="layui-btn layui-btn-xs layui-btn-primary" id="refresh-sessions" title="刷新">刷新</button>
</div>
<div class="sessions-tabs">
<div class="tab-item active" data-status="1">
进行中 <span class="badge" id="active-count">0</span>
</div>
<div class="tab-item" data-status="0">
待接入 <span class="badge" id="pending-count">0</span>
</div>
</div>
<div class="sessions-list">
<div id="session-list"></div>
<div id="pending-list" style="display:none;"></div>
</div>
</div>
<!-- 中间聊天窗口 -->
<div class="chat-main">
<div class="chat-header">
<div class="chat-title">
<h3 id="chat-title">请选择会话</h3>
<div class="chat-status" id="user-status" style="display:none;">
<span class="status-dot online"></span>
<span>在线</span>
</div>
</div>
<div class="chat-header-actions">
<span class="ws-status" id="ws-status">未连接</span>
<button class="layui-btn layui-btn-xs layui-btn-danger" id="end-session" style="display:none;margin-left:10px;">结束会话</button>
</div>
</div>
<div class="chat-messages" id="message-area">
<div class="message-placeholder">
<div style="font-size:48px;color:#ddd;margin-bottom:15px;">💬</div>
<p>请从左侧选择一个会话开始聊天</p>
</div>
</div>
<div class="typing-indicator" id="typing-indicator">
用户正在输入...
</div>
<div class="chat-input-area" style="position:relative;">
<div class="quick-reply-panel" id="quick-reply-panel"></div>
<div class="input-toolbar">
<button class="tool-btn" id="show-quick-reply" title="快捷回复" disabled>快捷</button>
</div>
<div class="input-wrapper">
<textarea id="message-input" placeholder="输入消息,按 Enter 发送..." disabled></textarea>
<button class="send-btn" id="send-message" disabled>发送</button>
</div>
</div>
</div>
<!-- 右侧用户信息面板 -->
<div class="chat-user-panel" id="user-panel">
<div class="user-profile">
<div class="avatar" id="user-avatar">U</div>
<div class="username" id="user-username">-</div>
<span class="source-tag" id="user-source">-</span>
</div>
<div class="user-details">
<div class="detail-item">
<span class="label">账户余额</span>
<span class="value money" id="user-money">-</span>
</div>
<div class="detail-item">
<span class="label">会话ID</span>
<span class="value" id="session-id">-</span>
</div>
<div class="detail-item">
<span class="label">会话时长</span>
<span class="value" id="session-duration">-</span>
</div>
</div>
<div class="user-actions">
<button class="action-btn" id="view-user-detail">查看用户详情</button>
<button class="action-btn" id="view-chat-history">历史消息记录</button>
<button class="action-btn danger" id="end-session-panel" style="display:none;">结束会话</button>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.5.0/socket.io.min.js"></script>
<script>
layui.use(['layer', 'upload'], function(){
var layer = layui.layer;
var upload = layui.upload;
var adminId = {$admin_id};
var loginToken = '{$login_token}';
var currentSessionId = null;
var currentSession = null;
var socket = null;
var currentTab = 1; // 1=进行中, 0=待接入
var sessionStartTime = null;
var durationTimer = null;
// WebSocket连接
function connectWebSocket() {
console.log('开始连接WebSocket...');
socket = io('wss://api.g7g7.top', {
transports: ['websocket'],
reconnection: true,
reconnectionDelay: 1000,
reconnectionAttempts: 5
});
socket.on('connect', function() {
console.log('Socket.IO connect 事件触发, socket.id:', socket.id);
updateWsStatus(true);
socket.emit('chat.connect', {
token: loginToken,
role: 'agent',
maxSessions: {$admin_status.max_sessions}
});
console.log('已发送 chat.connect 事件');
});
socket.on('disconnect', function(reason) {
console.log('Socket.IO disconnect 事件触发, 原因:', reason);
updateWsStatus(false);
});
socket.on('connect_error', function(error) {
console.log('Socket.IO connect_error:', error);
updateWsStatus(false);
});
socket.on('chat_connected', function(data) {
if (data.success) {
layer.msg('连接成功', {icon: 1, time: 1500});
loadSessions();
loadPendingSessions();
} else {
layer.msg('连接失败: ' + data.error, {icon: 2});
}
});
socket.on('chat_message_new', function(data) {
if (data.sessionId == currentSessionId) {
appendMessage(data, 'user');
playNotificationSound();
} else {
playNotificationSound();
}
loadSessions();
});
socket.on('chat_session_new', function(data) {
layer.msg('新会话分配', {icon: 1});
playNotificationSound();
loadSessions();
});
socket.on('chat_session_ended', function(data) {
if (data.sessionId == currentSessionId) {
layer.msg('会话已结束', {icon: 0});
closeCurrentSession();
}
loadSessions();
});
socket.on('chat_user_typing', function(data) {
if (data.sessionId == currentSessionId) {
showTypingIndicator();
}
});
// 心跳
setInterval(function() {
if (socket && socket.connected) {
socket.emit('chat.ping', {});
}
}, 25000);
}
// 更新WebSocket状态显示
function updateWsStatus(connected) {
var $status = $('#ws-status');
if (connected) {
$status.addClass('connected').text('已连接');
} else {
$status.removeClass('connected').text('未连接');
}
}
// 播放提示音
function playNotificationSound() {
// 可以添加提示音逻辑
}
// 显示打字指示器
function showTypingIndicator() {
var $indicator = $('#typing-indicator');
$indicator.addClass('show');
setTimeout(function() {
$indicator.removeClass('show');
}, 3000);
}
// 加载会话列表
function loadSessions() {
$.get('/chat/sessions', {status: 1}, function(res) {
if (res.code === 0) {
$('#active-count').text(res.data.length);
renderSessionList(res.data, '#session-list', false);
}
});
}
// 加载待分配会话
function loadPendingSessions() {
$.get('/chat/pending', function(res) {
if (res.code === 0) {
$('#pending-count').text(res.data.length);
renderSessionList(res.data, '#pending-list', true);
}
});
}
// 渲染会话列表
function renderSessionList(sessions, container, isPending) {
var html = '';
if (sessions.length === 0) {
html = '<div class="sessions-empty">';
html += '<div style="font-size:36px;color:#ddd;margin-bottom:10px;">💬</div>';
html += '<p>暂无会话</p>';
html += '</div>';
} else {
sessions.forEach(function(session) {
var isActive = session.id == currentSessionId ? ' active' : '';
var username = session.user_info ? session.user_info.username : '未知用户';
var firstChar = username.charAt(0).toUpperCase();
var unreadBadge = session.unread_count > 0 ? '<span class="session-badge">' + session.unread_count + '</span>' : '';
var lastMsg = session.last_msg || '暂无消息';
if (lastMsg.length > 20) lastMsg = lastMsg.substring(0, 20) + '...';
html += '<div class="session-item' + isActive + '" data-id="' + session.id + '">';
html += ' <div class="session-avatar">' + firstChar + '<span class="online-dot"></span></div>';
html += ' <div class="session-info">';
html += ' <div class="name-row">';
html += ' <span class="username">' + username + '</span>';
html += ' <span class="time">' + (session.last_msg_time_fmt || session.create_time_fmt || '') + '</span>';
html += ' </div>';
html += ' <div class="last-msg">' + lastMsg + '</div>';
html += ' </div>';
if (isPending) {
html += ' <button class="accept-btn" data-id="' + session.id + '">接入</button>';
} else {
html += unreadBadge;
}
html += '</div>';
});
}
$(container).html(html);
// 绑定点击事件
$(container + ' .session-item').click(function(e) {
// 如果点击的是接入按钮,不触发打开会话
if ($(e.target).hasClass('accept-btn')) return;
var sessionId = $(this).data('id');
if (!isPending) {
openSession(sessionId);
}
});
// 绑定接入按钮事件
if (isPending) {
$(container + ' .accept-btn').click(function(e) {
e.stopPropagation();
var sessionId = $(this).data('id');
acceptSession(sessionId);
});
}
}
// 接入会话
function acceptSession(sessionId) {
$.post('/chat/acceptSession', {session_id: sessionId}, function(res) {
if (res.code === 0) {
layer.msg('接入成功', {icon: 1, time: 1500});
loadSessions();
loadPendingSessions();
// 自动打开刚接入的会话
setTimeout(function() {
openSession(sessionId);
// 切换到进行中标签
$('.sessions-tabs .tab-item[data-status="1"]').click();
}, 500);
} else {
layer.msg(res.msg || '接入失败', {icon: 2});
loadPendingSessions();
}
});
}
// 打开会话
function openSession(sessionId) {
// 移除之前的选中状态
$('.session-item').removeClass('active');
$('.session-item[data-id="' + sessionId + '"]').addClass('active');
currentSessionId = sessionId;
// 加载消息
$.get('/chat/messages', {session_id: sessionId}, function(res) {
if (res.code === 0) {
$('#message-area').html('');
res.data.forEach(function(msg) {
appendMessage(msg, msg.sender_type === 1 ? 'user' : 'agent');
});
scrollToBottom();
}
});
// 获取会话详情
$.get('/chat/sessions', {status: 1}, function(res) {
if (res.code === 0) {
var session = res.data.find(function(s) { return s.id == sessionId; });
if (session) {
currentSession = session;
var username = session.user_info ? session.user_info.username : '未知用户';
var money = session.user_info ? session.user_info.money : '-';
var sourceName = session.source_name || '-';
var firstChar = username.charAt(0).toUpperCase();
// 更新聊天头部
$('#chat-title').text(username);
$('#user-status').show();
$('#end-session').show();
// 更新右侧用户面板
$('#user-avatar').text(firstChar);
$('#user-username').text(username);
$('#user-money').text(money);
$('#user-source').text(sourceName);
$('#session-id').text(session.id);
$('#user-panel').addClass('show');
$('#end-session-panel').show();
// 启用输入
$('#message-input, #send-message, #show-quick-reply').prop('disabled', false);
// 开始计时
startDurationTimer(session.create_time);
}
}
});
}
// 会话时长计时器
function startDurationTimer(startTime) {
if (durationTimer) clearInterval(durationTimer);
// startTime 是 Unix 时间戳(秒),需要转换为毫秒
sessionStartTime = startTime * 1000;
function updateDuration() {
var now = Date.now();
var diff = Math.floor((now - sessionStartTime) / 1000);
var hours = Math.floor(diff / 3600);
var minutes = Math.floor((diff % 3600) / 60);
var seconds = diff % 60;
var str = '';
if (hours > 0) str += hours + '时';
str += minutes + '分' + seconds + '秒';
$('#session-duration').text(str);
}
updateDuration();
durationTimer = setInterval(updateDuration, 1000);
}
// 追加消息
function appendMessage(msg, type) {
var content = msg.content || (msg.data ? msg.data.content : '');
var time = msg.create_time_fmt || new Date().toLocaleTimeString();
var isImage = msg.msg_type === 2 || (msg.data && msg.data.msgType === 2);
var username = type === 'user' ? (currentSession && currentSession.user_info ? currentSession.user_info.username : 'U') : '客服';
var firstChar = username.charAt(0).toUpperCase();
var html = '<div class="message-row ' + type + '">';
html += ' <div class="message-avatar">' + firstChar + '</div>';
html += ' <div class="message-content">';
html += ' <div class="message-bubble">';
if (isImage) {
html += '<img src="' + content + '" onclick="layer.photos({photos: {data: [{src: \'' + content + '\'}]}})">';
} else {
html += escapeHtml(content);
}
html += ' </div>';
html += ' <div class="message-time">' + time + '</div>';
html += ' </div>';
html += '</div>';
$('#message-area').append(html);
scrollToBottom();
}
// HTML转义
function escapeHtml(text) {
var div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 滚动到底部
function scrollToBottom() {
var area = document.getElementById('message-area');
area.scrollTop = area.scrollHeight;
}
// 发送消息
$('#send-message').click(function() {
sendMessage();
});
function sendMessage() {
var content = $('#message-input').val().trim();
if (!content) {
layer.msg('请输入消息', {icon: 0});
return;
}
if (!currentSessionId) {
layer.msg('请先选择会话', {icon: 0});
return;
}
socket.emit('chat.message.send', {
sessionId: currentSessionId,
msgType: 1,
content: content,
clientMsgId: Date.now()
});
appendMessage({content: content, create_time_fmt: new Date().toLocaleTimeString()}, 'agent');
$('#message-input').val('');
}
// 回车发送
$('#message-input').keypress(function(e) {
if (e.which === 13 && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
// 结束会话
function endCurrentSession() {
if (!currentSessionId) return;
layer.confirm('确定结束此会话?', {icon: 3, title: '提示'}, function(index) {
$.post('/chat/endSession', {session_id: currentSessionId}, function(res) {
if (res.code === 0) {
layer.msg('会话已结束', {icon: 1});
closeCurrentSession();
loadSessions();
} else {
layer.msg(res.msg || '操作失败', {icon: 2});
}
});
layer.close(index);
});
}
$('#end-session, #end-session-panel').click(endCurrentSession);
// 关闭当前会话
function closeCurrentSession() {
currentSessionId = null;
currentSession = null;
if (durationTimer) clearInterval(durationTimer);
$('#chat-title').text('请选择会话');
$('#user-status').hide();
$('#end-session').hide();
$('#user-panel').removeClass('show');
$('#end-session-panel').hide();
$('#message-input, #send-message, #show-quick-reply').prop('disabled', true);
$('#message-area').html('<div class="message-placeholder"><div style="font-size:48px;color:#ddd;margin-bottom:15px;">💬</div><p>请从左侧选择一个会话开始聊天</p></div>');
$('.session-item').removeClass('active');
}
// 快捷回复
var quickReplyLoaded = false;
var quickReplies = [];
function loadQuickReplies() {
$.get('/chat/quickReplies', function(res) {
if (res.code === 0) {
quickReplies = res.data;
quickReplyLoaded = true;
renderQuickReplyPanel();
}
});
}
function renderQuickReplyPanel() {
var html = '';
quickReplies.forEach(function(item) {
html += '<div class="quick-reply-item" data-content="' + escapeHtml(item.content) + '">';
html += ' <div class="title">' + escapeHtml(item.title) + '</div>';
html += ' <div class="content">' + escapeHtml(item.content.substring(0, 50)) + (item.content.length > 50 ? '...' : '') + '</div>';
html += '</div>';
});
$('#quick-reply-panel').html(html);
$('#quick-reply-panel .quick-reply-item').click(function() {
var content = $(this).data('content');
$('#message-input').val(content);
$('#quick-reply-panel').removeClass('show');
$('#message-input').focus();
});
}
$('#show-quick-reply').click(function() {
if (!quickReplyLoaded) {
loadQuickReplies();
}
$('#quick-reply-panel').toggleClass('show');
});
// 点击其他地方关闭快捷回复面板
$(document).click(function(e) {
if (!$(e.target).closest('#show-quick-reply, #quick-reply-panel').length) {
$('#quick-reply-panel').removeClass('show');
}
});
// 刷新会话
$('#refresh-sessions').click(function() {
loadSessions();
loadPendingSessions();
layer.msg('已刷新', {icon: 1, time: 1000});
});
// 标签切换
$('.sessions-tabs .tab-item').click(function() {
var status = $(this).data('status');
currentTab = status;
$(this).addClass('active').siblings().removeClass('active');
if (status === 0) {
$('#session-list').hide();
$('#pending-list').show();
loadPendingSessions();
} else {
$('#pending-list').hide();
$('#session-list').show();
loadSessions();
}
});
// 图片上传
upload.render({
elem: '#upload-image',
url: '/chat/upload',
accept: 'images',
before: function() {
layer.load(1);
},
done: function(res) {
layer.closeAll('loading');
if (res.code === 0) {
socket.emit('chat.message.send', {
sessionId: currentSessionId,
msgType: 2,
content: res.url,
clientMsgId: Date.now()
});
appendMessage({content: res.url, msg_type: 2, create_time_fmt: new Date().toLocaleTimeString()}, 'agent');
} else {
layer.msg(res.msg || '上传失败', {icon: 2});
}
},
error: function() {
layer.closeAll('loading');
layer.msg('上传失败', {icon: 2});
}
});
// 查看用户详情
$('#view-user-detail').click(function() {
if (currentSession && currentSession.user_id) {
layer.open({
type: 2,
title: '用户详情',
area: ['800px', '600px'],
content: '/player/player_edit?id=' + currentSession.user_id
});
}
});
// 查看历史记录
$('#view-chat-history').click(function() {
if (currentSessionId) {
layer.open({
type: 2,
title: '历史消息记录',
area: ['800px', '600px'],
content: '/chat/record?session_id=' + currentSessionId
});
}
});
// 初始化
connectWebSocket();
loadQuickReplies();
});
</script>
</body>
</html>