fix(ai-chat): 修复历史消息加载逻辑及超时问题

This commit is contained in:
2026-05-13 09:00:20 +08:00
parent d5ca77e2d8
commit b2688dc8a1
2 changed files with 99 additions and 106 deletions

View File

@@ -210,14 +210,6 @@ export default {
}
});
const timeout = setTimeout(() => {
if (!isAuthenticated || content === '') {
console.warn('WebSocket 超时,强制关闭');
socketTask.close();
safeComplete({ error: 'AI服务响应超时' });
safeReject(new Error('AI服务响应超时'));
}
}, 20000);
});
// #endif
},

View File

@@ -534,28 +534,30 @@ export default {
}
},
onShow() {
// 如果不是 AI 客服,立即退出并跳转
if (customerServiceType !== 'ai') {
uni.showToast({ title: '当前客服类型不支持此页面', icon: 'none' });
setTimeout(() => {
uni.navigateBack({ delta: 1 }); // 或 redirectTo 首页
}, 1000);
return; // 关键:阻止后续初始化
}
// 优先读取本地缓存的会话 ID
const localConvId = this.getConversationIdFromLocal();
if (localConvId) {
this.currentConversationId = localConvId;
aiService.setConversationId(localConvId);
} else {
this.currentConversationId = aiService.getConversationId();
}
// 加载初始历史后,同步historyOffset
this.loadChatHistoryIfExist().then(() => {
// 初始历史条数作为偏移量
this.historyOffset = this.messages.length;
});
},
// 如果不是 AI 客服,立即退出并跳转
if (customerServiceType !== 'ai') {
uni.showToast({ title: '当前客服类型不支持此页面', icon: 'none' });
setTimeout(() => {
uni.navigateBack({ delta: 1 });
}, 1000);
return;
}
// 只在初次渲染或会话ID发生变化时才加载历史
const localConvId = this.getConversationIdFromLocal();
const shouldReload = this.currentConversationId !== localConvId;
if (localConvId && shouldReload) {
// 切换到已有的历史会话 → 重新加载历史
this.currentConversationId = localConvId;
aiService.setConversationId(localConvId);
this.loadChatHistoryIfExist();
// 注意: historyOffset 和 hasMoreHistory 在 loadChatHistoryIfExist() 内部设置
} else if (!localConvId && !this.currentConversationId) {
// 全新会话,初始化偏移量
this.historyOffset = this.messages.length;
}
// 会话未变化且已有消息 → 跳过加载,保留当前消息
},
// 新增:组件销毁时清除防抖定时器
beforeDestroy() {
if (this.scrollTimer) clearTimeout(this.scrollTimer);
@@ -631,8 +633,8 @@ export default {
// 防抖处理:50ms内只执行一次,避免频繁触发
if (this.scrollTimer) clearTimeout(this.scrollTimer);
this.scrollTimer = setTimeout(() => {
// 滚动到顶部附近(<20px)、不在加载中、显示加载更多时,触发加载
if (currentScrollTop < 20 && !this.isLoadingHistory && this.showLoadMore) {
// 滚动到顶部附近(<20px)、不在加载中、还有更多历史时,触发加载
if (currentScrollTop < 20 && !this.isLoadingHistory && this.hasMoreHistory) {
this.loadMoreHistory();
}
}, 50);
@@ -682,7 +684,7 @@ async loadChatHistoryIfExist() {
const realTimestamp = msg.created_at || msg.create_time || msg.time;
return {
id: msg.message_id || (Date.now() + Math.random() * 10000),
role: msg.role === 'user' ? 'user' : 'assistant',
role: msg.role === 'user' ? 'user' : 'ai',
content: content,
timestamp: realTimestamp,
actions: msg.role !== 'user' ? [
@@ -691,6 +693,10 @@ async loadChatHistoryIfExist() {
] : []
};
}).filter(msg => msg.content.trim() !== ''); // 过滤掉内容为空的消息
// 根据服务端 total 判断是否还有更多历史可加载
const msgCount = this.messages.length;
this.hasMoreHistory = response.total > msgCount;
this.historyOffset = msgCount;
} catch (error) {
console.error('加载历史记录失败:', error);
// 只需调用服务层的清除方法,它会处理本地存储
@@ -1393,80 +1399,74 @@ async loadChatHistoryIfExist() {
},
// 加载更多历史消息
async loadMoreHistory() {
if (!this.currentConversationId) {
this.currentConversationId = this.getConversationIdFromLocal();
aiService.setConversationId(this.currentConversationId);
}
// 防止重复加载
if (!this.showLoadMore || !this.currentConversationId || this.isLoadingHistory) {
return;
}
this.isFetchingHistory = true;
this.isLoadingHistory = true;
this.loadingText = '加载历史消息中...';
this.hasMoreHistory = true;
try {
// 关键修改:用独立的historyOffset作为偏移量,不再依赖messages.length
const response = await aiService.getConversationHistory({
conversation_id: this.currentConversationId,
limit: 20,
offset: this.historyOffset // 改用独立偏移量
});
async loadMoreHistory() {
if (!this.currentConversationId) {
this.currentConversationId = this.getConversationIdFromLocal();
aiService.setConversationId(this.currentConversationId);
}
// 防止重复加载
if (!this.hasMoreHistory || !this.currentConversationId || this.isLoadingHistory) {
return;
}
this.isFetchingHistory = true;
this.isLoadingHistory = true;
this.loadingText = '加载历史消息中...';
try {
const response = await aiService.getConversationHistory({
conversation_id: this.currentConversationId,
limit: 20,
offset: this.historyOffset
});
if (response.success && Array.isArray(response.messages) && response.messages.length > 0) {
const historyMessages = response.messages.map(msg => {
// 只使用后端返回的真实时间,不使用假时间
const realTimestamp = msg.create_time || msg.created_at || msg.time;
return {
id: msg.id || `history-${Date.now()}-${Math.random().toString(36).slice(2)}`,
role: msg.role === 'user' ? 'user' : 'ai',
type: 'text',
content: msg.content,
timestamp: realTimestamp
};
});
// 1. 记录新增消息数量
const newMsgCount = historyMessages.length;
// 2. 添加到消息列表开头
this.messages = [...historyMessages, ...this.messages];
// 3. 更新历史偏移量(累加,不是覆盖)
this.historyOffset += newMsgCount;
// 4. 等待DOM渲染后,精准计算滚动位置
await this.$nextTick();
// 获取新增消息的实际DOM高度
const query = uni.createSelectorQuery().in(this);
const heightPromises = [];
for (let i = 0; i < newMsgCount; i++) {
heightPromises.push(new Promise(resolve => {
query.select(`.message-item:nth-child(${i + 1})`).boundingClientRect(rect => {
resolve(rect ? rect.height + 32 : 0); // 加上消息间距(32rpx)
}).exec();
}));
}
// 计算总高度
const heights = await Promise.all(heightPromises);
const totalNewHeight = heights.reduce((sum, h) => sum + h, 0);
// 5. 设置scrollTop,保持滚动条在顶部附近
this.scrollTop = totalNewHeight;
} else {
// 无更多历史
this.showLoadMore = false;
}
} catch (error) {
console.error('加载历史失败:', error);
uni.showToast({ title: '加载历史失败', icon: 'none' });
this.showLoadMore = false;
} finally {
this.isLoadingHistory = false;
this.isFetchingHistory = false;
this.hasMoreHistory = false;
this.loadingText = '加载更多历史消息';
}
},
const realTimestamp = msg.create_time || msg.created_at || msg.time;
return {
id: msg.id || `history-${Date.now()}-${Math.random().toString(36).slice(2)}`,
role: msg.role === 'user' ? 'user' : 'ai',
type: 'text',
content: msg.content,
timestamp: realTimestamp
};
});
const newMsgCount = historyMessages.length;
this.messages = [...historyMessages, ...this.messages];
this.historyOffset += newMsgCount;
// 根据服务端 total 判断是否还有更多
if (response.total !== undefined) {
this.hasMoreHistory = this.historyOffset < response.total;
}
await this.$nextTick();
const query = uni.createSelectorQuery().in(this);
const heightPromises = [];
for (let i = 0; i < newMsgCount; i++) {
heightPromises.push(new Promise(resolve => {
query.select(`.message-item:nth-child(${i + 1})`).boundingClientRect(rect => {
resolve(rect ? rect.height + 32 : 0);
}).exec();
}));
}
const heights = await Promise.all(heightPromises);
const totalNewHeight = heights.reduce((sum, h) => sum + h, 0);
this.scrollTop = totalNewHeight;
} else {
// 无更多历史
this.hasMoreHistory = false;
}
} catch (error) {
console.error('加载历史失败:', error);
uni.showToast({ title: '加载历史失败', icon: 'none' });
this.hasMoreHistory = false;
} finally {
this.isLoadingHistory = false;
this.isFetchingHistory = false;
this.loadingText = '加载更多历史消息';
}
},
// 显示更多工具
showMoreTools() {
this.showToolsPanel = true
@@ -1908,8 +1908,9 @@ async loadChatHistoryIfExist() {
this.currentConversationId = null
aiService.clearConversationId()
this.clearConversationIdFromLocal()
// 重置历史偏移量
// 重置历史偏移量和加载状态
this.historyOffset = 0
this.hasMoreHistory = false
// 触发清空事件
this.$emit('messages-cleared')
},