chore(addon/aikefu): 增加可以选择消息排序

This commit is contained in:
2025-12-09 10:12:29 +08:00
parent cd37019675
commit 717481dcba
3 changed files with 173 additions and 56 deletions

View File

@@ -271,6 +271,8 @@ class Kefu extends BaseShop
$limit = input("limit/d", 50);
$conversation_id = input("conversation_id/s", "");
$user_id = input("user_id/s", "");
$sort_field = input("sort_field/s", "create_time"); // 排序字段
$sort_order = input("sort_order/s", "desc"); // 排序方式asc或desc
$kefu_message_model = new KefuMessageModel();
$condition = [
@@ -287,7 +289,9 @@ class Kefu extends BaseShop
$condition[] = ['user_id', '=', $user_id];
}
$message_list = $kefu_message_model->getMessageList($condition, '*', 'create_time asc', $page, $limit);
// 构建排序字符串
$order = $sort_field . ' ' . $sort_order;
$message_list = $kefu_message_model->getMessageList($condition, '*', $order, $page, $limit);
// 适配layui table的返回格式同时保持与Dify API风格一致
$result = [
'code' => 0, // layui table要求成功状态码为0

View File

@@ -20,48 +20,110 @@
color: #faad14;
}
/* 简单消息列表样式 */
.simple-message-list {
max-height: 500px;
/* 消息列表样式优化 - 从message.html复制 */
.message-list {
max-height: 650px;
overflow-y: auto;
padding: 15px;
padding: 25px;
background-color: #fafbfc;
border-radius: 8px;
margin-bottom: 20px;
border: 1px solid #e8e8e8;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08);
}
/* 消息项样式 */
.message-item {
margin-bottom: 15px;
padding: 10px;
border-radius: 4px;
}
.message-item.user {
background-color: #e6f7ff;
border-left: 3px solid #1890ff;
}
.message-item.assistant {
background-color: #f6ffed;
border-left: 3px solid #52c41a;
}
.message-header {
margin-bottom: 25px;
display: flex;
justify-content: space-between;
margin-bottom: 5px;
font-size: 12px;
align-items: flex-start;
}
.message-role {
font-weight: bold;
color: #333;
.message-item.user {
justify-content: flex-end;
}
.message-time {
color: #999;
.message-item.assistant {
justify-content: flex-start;
}
/* 头像样式 */
.message-avatar {
width: 48px;
height: 48px;
border-radius: 50%;
margin: 0 12px;
border: 2px solid #fff;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* 消息内容样式 */
.message-content {
font-size: 14px;
line-height: 1.4;
color: #666;
word-break: break-word;
max-width: 70%;
padding: 16px 20px;
border-radius: 18px;
word-wrap: break-word;
line-height: 1.6;
position: relative;
}
.empty-message {
text-align: center;
.message-item.user .message-content {
background-color: #1E9FFF;
color: white;
border-bottom-right-radius: 4px;
box-shadow: 0 2px 8px rgba(30, 159, 255, 0.3);
}
.message-item.assistant .message-content {
background-color: white;
color: #333;
border-bottom-left-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
/* 消息时间样式 */
.message-time {
font-size: 12px;
color: #999;
padding: 30px;
margin-top: 8px;
text-align: center;
}
/* 消息角色样式 */
.message-role {
font-size: 13px;
font-weight: 500;
margin-bottom: 6px;
}
.message-item.user .message-role {
color: rgba(106, 38, 38, 0.9);
text-align: right;
}
.message-item.assistant .message-role {
color: #666;
text-align: left;
}
/* 空状态样式 */
.empty-state {
text-align: center;
padding: 60px 20px;
color: #999;
}
.empty-state i {
font-size: 48px;
margin-bottom: 15px;
display: block;
}
/* 响应式设计 */
@media (max-width: 768px) {
.message-content {
max-width: 85%;
}
}
</style>
@@ -118,25 +180,35 @@
}}}
</script>
<!-- 简单消息列表模板 -->
<!-- 消息列表模板 - 与message.html一致 -->
<script type="text/html" id="messageListTpl">
<div class="simple-message-list">
<div class="message-list">
{{# if(d.length > 0) {
d.forEach(function(item) {
var role = item.role === 'user' ? (item.user_id ? '用户-' + item.user_id : '用户') : '机器人';
var roleClass = item.role === 'user' ? 'user' : 'assistant';
var roleText = item.role === 'user' ? '用户' : '机器人';
var avatar = item.role === 'user' ? '/addon/aikefu/static/images/user.svg' : '/addon/aikefu/static/images/robot.svg';
}}}
<div class="message-item {{roleClass}}">
<div class="message-header">
<span class="message-role">{{roleText}}</span>
<span class="message-time">{{item.create_time}}</span>
{{# if(item.role === 'assistant') {
<img src="{{avatar}}" class="message-avatar">
}}}
<div>
<div class="message-role">{{role}}</div>
<div class="message-content">{{item.content}}</div>
<div class="message-time">{{item.create_time}}</div>
</div>
<div class="message-content">{{item.content}}</div>
{{# if(item.role === 'user') {
<img src="{{avatar}}" class="message-avatar">
}}}
</div>
{{# });
} else {
}}}
<div class="empty-message">暂无消息记录</div>
<div class="empty-state">
<i class="layui-icon layui-icon-message"></i>
<p>暂无消息记录</p>
</div>
{{# }
}}
</div>
@@ -243,7 +315,7 @@
layer.open({
type: 1,
title: '消息记录',
content: '<div class="simple-message-list" style="padding: 15px; max-height: 500px; overflow-y: auto;">加载中...</div>',
content: '<div class="message-list" style="padding: 25px; max-height: 650px; overflow-y: auto;">加载中...</div>',
area: ['80%', '70%'],
success: function(layero, index) {
// 获取消息列表
@@ -253,34 +325,48 @@
data: {
conversation_id: data.conversation_id,
page: 1,
limit: 100 // 加载最多100条消息
limit: 100, // 加载最多100条消息
sort_field: 'create_time',
sort_order: 'desc' // 默认最新消息在前面
},
dataType: 'json',
success: function(res) {
var messageList = res.data.messages || [];
var messageList = res.data?.messages || [];
var html = '';
if (messageList.length > 0) {
messageList.forEach(function(item) {
var role = item.role === 'user' ? (item.user_id ? '用户-' + item.user_id : '用户') : '机器人';
var roleClass = item.role === 'user' ? 'user' : 'assistant';
var roleText = item.role === 'user' ? '用户' : '机器人';
var avatar = item.role === 'user' ? '/addon/aikefu/static/images/user.svg' : '/addon/aikefu/static/images/robot.svg';
html += '<div class="message-item ' + roleClass + '">';
html += '<div class="message-header">';
html += '<span class="message-role">' + roleText + '</span>';
html += '<span class="message-time">' + item.create_time + '</span>';
html += '</div>';
if (item.role === 'assistant') {
html += '<img src="' + avatar + '" class="message-avatar">';
}
html += '<div>';
html += '<div class="message-role">' + role + '</div>';
html += '<div class="message-content">' + item.content + '</div>';
html += '<div class="message-time">' + item.create_time + '</div>';
html += '</div>';
if (item.role === 'user') {
html += '<img src="' + avatar + '" class="message-avatar">';
}
html += '</div>';
});
} else {
html += '<div class="empty-message">暂无消息记录</div>';
html += '<div class="empty-state">';
html += '<i class="layui-icon layui-icon-message"></i>';
html += '<p>暂无消息记录</p>';
html += '</div>';
}
layero.find('.simple-message-list').html(html);
layero.find('.message-list').html(html);
// 默认滚动到底部(最新消息)
layero.find('.message-list').scrollTop(layero.find('.message-list')[0].scrollHeight);
},
error: function() {
layero.find('.simple-message-list').html('<div class="empty-message">加载消息失败</div>');
layero.find('.message-list').html('<div class="empty-state">加载消息失败</div>');
}
});
}

View File

@@ -220,6 +220,13 @@
<label for="user_id">用户ID</label>
<input type="text" name="user_id" id="msg_user_id" placeholder="请输入用户ID进行过滤" value="{$user_id ?? ''}" class="layui-input" style="width: 200px; display: inline-block;">
</div>
<div class="search-item">
<label for="sort_order">排序方式</label>
<select name="sort_order" id="msg_sortOrder" class="layui-select" style="width: 200px; display: inline-block;">
<option value="desc">最新消息在前面</option>
<option value="asc">最新消息在后面</option>
</select>
</div>
<div class="search-item">
<button type="button" class="layui-btn layui-btn-primary" id="msg_searchBtn">搜索</button>
<button type="button" class="layui-btn" id="msg_resetBtn">重置</button>
@@ -247,12 +254,14 @@
var laypage = layui.laypage;
var layer = layui.layer;
// 分页参数
// 分页和排序参数
var page = 1;
var limit = 50;
var total = 0;
var conversation_id = $('#msg_conversation_id').val();
var user_id = $('#msg_user_id').val();
var sortField = 'create_time'; // 排序字段:创建时间
var sortOrder = 'desc'; // 默认倒序(最新消息在前面)
// 加载会话信息当指定会话ID时显示
function loadConversationInfo() {
@@ -294,7 +303,9 @@
// 构建请求数据
var requestData = {
page: page,
limit: limit
limit: limit,
sort_field: sortField, // 排序字段:创建时间
sort_order: sortOrder // 排序方式:倒序/正序
};
// 如果有会话ID则添加到请求数据中
@@ -376,8 +387,14 @@
}
$('#msg_messageList').html(html);
// 滚动到底部
$('#msg_messageList').scrollTop($('#msg_messageList')[0].scrollHeight);
// 根据排序方式调整滚动位置
if (sortOrder === 'asc') {
// 正序时滚动到顶部
$('#msg_messageList').scrollTop(0);
} else {
// 倒序时滚动到底部
$('#msg_messageList').scrollTop($('#msg_messageList')[0].scrollHeight);
}
// 渲染分页
renderPagination();
} else {
@@ -434,6 +451,7 @@
$('#msg_searchBtn').click(function() {
conversation_id = $('#msg_conversation_id').val().trim();
user_id = $('#msg_user_id').val().trim();
sortOrder = $('#msg_sortOrder').val(); // 获取当前选择的排序方式
page = 1;
loadConversationInfo();
loadMessageList();
@@ -443,13 +461,22 @@
$('#msg_resetBtn').click(function() {
$('#msg_conversation_id').val('');
$('#msg_user_id').val('');
$('#msg_sortOrder').val('desc'); // 重置为默认排序
conversation_id = '';
user_id = '';
sortOrder = 'desc'; // 重置为默认排序
page = 1;
loadConversationInfo();
loadMessageList();
});
// 排序方式变更事件
$('#msg_sortOrder').change(function() {
sortOrder = $(this).val();
page = 1; // 切换排序时重置到第一页
loadMessageList();
});
// 初始化加载(默认显示所有消息)
loadConversationInfo();
loadMessageList();