|
@@ -338,11 +338,17 @@
|
|
|
|
|
|
|
|
// 完成流式输出
|
|
// 完成流式输出
|
|
|
const finishStreaming = (id) => {
|
|
const finishStreaming = (id) => {
|
|
|
- console.log("消息id2:",id);
|
|
|
|
|
|
|
+ console.log("完成流式输出,消息id:", id);
|
|
|
stopTypingEffect();
|
|
stopTypingEffect();
|
|
|
|
|
|
|
|
if (currentTypingMessage.value !== null) {
|
|
if (currentTypingMessage.value !== null) {
|
|
|
- chatMessages.value[currentTypingMessage.value].isTyping = false;
|
|
|
|
|
|
|
+ // 如果消息内容为空(可能是被中止了),移除这条消息
|
|
|
|
|
+ if (!chatMessages.value[currentTypingMessage.value]?.content) {
|
|
|
|
|
+ console.log('[完成流式] 消息内容为空,移除消息');
|
|
|
|
|
+ chatMessages.value.splice(currentTypingMessage.value, 1);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ chatMessages.value[currentTypingMessage.value].isTyping = false;
|
|
|
|
|
+ }
|
|
|
currentTypingMessage.value = null;
|
|
currentTypingMessage.value = null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -350,6 +356,11 @@
|
|
|
isInThinkingMode.value = false;
|
|
isInThinkingMode.value = false;
|
|
|
thinkingBuffer.value = '';
|
|
thinkingBuffer.value = '';
|
|
|
|
|
|
|
|
|
|
+ // 重置微信小程序中止标记
|
|
|
|
|
+ // #ifdef MP-WEIXIN
|
|
|
|
|
+ isWeixinRequestAborted = false;
|
|
|
|
|
+ // #endif
|
|
|
|
|
+
|
|
|
isProcessing.value = false;
|
|
isProcessing.value = false;
|
|
|
scrollToBottom();
|
|
scrollToBottom();
|
|
|
|
|
|
|
@@ -360,6 +371,15 @@
|
|
|
// 处理流式错误
|
|
// 处理流式错误
|
|
|
const handleStreamError = (error) => {
|
|
const handleStreamError = (error) => {
|
|
|
console.error('流式错误:', error);
|
|
console.error('流式错误:', error);
|
|
|
|
|
+
|
|
|
|
|
+ // 如果是微信小程序的主动中止,不显示错误
|
|
|
|
|
+ // #ifdef MP-WEIXIN
|
|
|
|
|
+ if (isWeixinRequestAborted) {
|
|
|
|
|
+ console.log('[微信小程序] 忽略主动中止的错误');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ // #endif
|
|
|
|
|
+
|
|
|
stopTypingEffect();
|
|
stopTypingEffect();
|
|
|
|
|
|
|
|
// 移除正在输入的消息
|
|
// 移除正在输入的消息
|
|
@@ -448,8 +468,13 @@
|
|
|
};
|
|
};
|
|
|
// #endif
|
|
// #endif
|
|
|
|
|
|
|
|
- // #ifdef APP-HARMONY || APP-PLUS
|
|
|
|
|
- // 鸿蒙等不支持 renderjs 的平台,使用原生方式
|
|
|
|
|
|
|
+ // #ifdef MP-WEIXIN
|
|
|
|
|
+ // 微信小程序使用普通请求(不支持 SSE)
|
|
|
|
|
+ startWeixinRequest(url, requestData);
|
|
|
|
|
+ // #endif
|
|
|
|
|
+
|
|
|
|
|
+ // #ifdef APP-HARMONY
|
|
|
|
|
+ // 鸿蒙平台使用原生方式
|
|
|
startSSERequestNative(url, requestData);
|
|
startSSERequestNative(url, requestData);
|
|
|
// #endif
|
|
// #endif
|
|
|
}
|
|
}
|
|
@@ -457,6 +482,104 @@
|
|
|
// 鸿蒙端 SSE 请求任务引用
|
|
// 鸿蒙端 SSE 请求任务引用
|
|
|
let sseRequestTask = null;
|
|
let sseRequestTask = null;
|
|
|
|
|
|
|
|
|
|
+ // 微信小程序请求任务引用
|
|
|
|
|
+ let weixinRequestTask = null;
|
|
|
|
|
+ let isWeixinRequestAborted = false; // 标记请求是否被主动中止
|
|
|
|
|
+
|
|
|
|
|
+ // 微信小程序请求(不支持 SSE,使用普通请求)
|
|
|
|
|
+ const startWeixinRequest = (url, data) => {
|
|
|
|
|
+ console.log('[微信小程序] 开始请求:', url);
|
|
|
|
|
+
|
|
|
|
|
+ // 重置中止标记
|
|
|
|
|
+ isWeixinRequestAborted = false;
|
|
|
|
|
+
|
|
|
|
|
+ // 创建请求任务
|
|
|
|
|
+ weixinRequestTask = uni.request({
|
|
|
|
|
+ url: url,
|
|
|
|
|
+ method: 'POST',
|
|
|
|
|
+ header: {
|
|
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
|
|
+ 'Authorization': `Bearer ${storage.getAccessToken()}`
|
|
|
|
|
+ },
|
|
|
|
|
+ data: data,
|
|
|
|
|
+ timeout: 60000, // 60秒超时
|
|
|
|
|
+ success: (res) => {
|
|
|
|
|
+ // 如果请求被主动中止,不处理响应
|
|
|
|
|
+ if (isWeixinRequestAborted) {
|
|
|
|
|
+ console.log('[微信小程序] 请求已被中止,忽略响应');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.log('[微信小程序] 请求成功,状态码:', res.statusCode);
|
|
|
|
|
+
|
|
|
|
|
+ if (res.statusCode === 200 && res.data) {
|
|
|
|
|
+ // 检查返回数据格式
|
|
|
|
|
+ if (typeof res.data === 'string') {
|
|
|
|
|
+ console.log('[微信小程序] 收到文本格式数据,长度:', res.data.length);
|
|
|
|
|
+ // SSE 格式数据
|
|
|
|
|
+ parseSSEResponse(res.data);
|
|
|
|
|
+ } else if (res.data.answer || res.data.content) {
|
|
|
|
|
+ console.log('[微信小程序] 收到 JSON 格式数据');
|
|
|
|
|
+ // JSON 格式数据,直接显示
|
|
|
|
|
+ const content = res.data.answer || res.data.content || '';
|
|
|
|
|
+ handleMessageData(content);
|
|
|
|
|
+ finishStreaming(res.data.id || null);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.error('[微信小程序] 未知的响应格式:', res.data);
|
|
|
|
|
+ handleStreamError('响应格式错误');
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.error('[微信小程序] 请求失败,状态码:', res.statusCode);
|
|
|
|
|
+ handleStreamError('请求失败,状态码: ' + res.statusCode);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ fail: (err) => {
|
|
|
|
|
+ // 如果是主动中止的请求,不显示错误
|
|
|
|
|
+ if (isWeixinRequestAborted) {
|
|
|
|
|
+ console.log('[微信小程序] 请求已被主动中止');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.error('[微信小程序] 请求失败:', err);
|
|
|
|
|
+
|
|
|
|
|
+ // 过滤掉 abort 错误(用户主动中止)
|
|
|
|
|
+ if (err.errMsg && err.errMsg.includes('abort')) {
|
|
|
|
|
+ console.log('[微信小程序] 请求被中止');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 过滤掉 WebSocket 相关错误(可能是其他组件导致的)
|
|
|
|
|
+ if (err.errMsg && err.errMsg.includes('WebSocket')) {
|
|
|
|
|
+ console.warn('[微信小程序] 忽略 WebSocket 错误,这可能来自其他组件');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ handleStreamError(err.errMsg || '网络异常');
|
|
|
|
|
+ },
|
|
|
|
|
+ complete: () => {
|
|
|
|
|
+ weixinRequestTask = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 停止微信小程序请求
|
|
|
|
|
+ const stopWeixinRequest = () => {
|
|
|
|
|
+ if (weixinRequestTask) {
|
|
|
|
|
+ console.log('[微信小程序] 准备中止请求');
|
|
|
|
|
+ // 设置中止标记
|
|
|
|
|
+ isWeixinRequestAborted = true;
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ weixinRequestTask.abort();
|
|
|
|
|
+ console.log('[微信小程序] 请求已中止');
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('[微信小程序] 中止请求失败:', e);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ weixinRequestTask = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 原生方式处理 SSE 请求(用于鸿蒙等平台)
|
|
// 原生方式处理 SSE 请求(用于鸿蒙等平台)
|
|
|
const startSSERequestNative = (url, data) => {
|
|
const startSSERequestNative = (url, data) => {
|
|
|
console.log('[鸿蒙端] 开始 SSE 请求:', url);
|
|
console.log('[鸿蒙端] 开始 SSE 请求:', url);
|
|
@@ -658,14 +781,16 @@
|
|
|
stopSSERequestNative();
|
|
stopSSERequestNative();
|
|
|
// #endif
|
|
// #endif
|
|
|
|
|
|
|
|
|
|
+ // #ifdef MP-WEIXIN
|
|
|
|
|
+ // 停止微信小程序请求
|
|
|
|
|
+ stopWeixinRequest();
|
|
|
|
|
+ // #endif
|
|
|
|
|
+
|
|
|
// 立即停止打字机效果并完成
|
|
// 立即停止打字机效果并完成
|
|
|
finishStreaming();
|
|
finishStreaming();
|
|
|
|
|
|
|
|
- uni.showToast({
|
|
|
|
|
- title: '已中止',
|
|
|
|
|
- icon: 'none',
|
|
|
|
|
- duration: 1500
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ // 不显示 Toast,避免干扰用户
|
|
|
|
|
+ console.log('[用户操作] 已中止流式输出');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 重新生成回复
|
|
// 重新生成回复
|
|
@@ -866,18 +991,40 @@
|
|
|
|
|
|
|
|
// 生命周期钩子
|
|
// 生命周期钩子
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
|
|
|
+ console.log('[生命周期] 组件挂载');
|
|
|
|
|
+
|
|
|
|
|
+ // 捕获全局错误,防止 WebSocket 等错误影响功能
|
|
|
|
|
+ // #ifdef MP-WEIXIN
|
|
|
|
|
+ const originalError = console.error;
|
|
|
|
|
+ console.error = function(...args) {
|
|
|
|
|
+ // 过滤掉 WebSocket 相关错误(可能来自其他组件或框架)
|
|
|
|
|
+ const errorMsg = args.join(' ');
|
|
|
|
|
+ if (errorMsg.includes('WebSocket') || errorMsg.includes('closeSocket')) {
|
|
|
|
|
+ console.warn('[已过滤] WebSocket 错误:', ...args);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ originalError.apply(console, args);
|
|
|
|
|
+ };
|
|
|
|
|
+ // #endif
|
|
|
|
|
+
|
|
|
// 初始化防抖函数
|
|
// 初始化防抖函数
|
|
|
debouncedSubmitQuestion = debounce(submitQuestion, 300)
|
|
debouncedSubmitQuestion = debounce(submitQuestion, 300)
|
|
|
|
|
|
|
|
// 获取系统信息
|
|
// 获取系统信息
|
|
|
- const systemInfo = uni.getSystemInfoSync();
|
|
|
|
|
- statusBarHeight.value = systemInfo.statusBarHeight || 20;
|
|
|
|
|
- isIOS.value = systemInfo.platform === 'ios';
|
|
|
|
|
-
|
|
|
|
|
- // 计算底部安全区域(包含 tabbar 高度)
|
|
|
|
|
- const safeAreaInsetsBottom = systemInfo.safeAreaInsets ? (systemInfo.safeAreaInsets.bottom || 0) : 0;
|
|
|
|
|
- // 转换为 rpx,并确保至少有基础间距
|
|
|
|
|
- safeAreaBottom.value = Math.max(safeAreaInsetsBottom * 2, 0);
|
|
|
|
|
|
|
+ try {
|
|
|
|
|
+ const systemInfo = uni.getSystemInfoSync();
|
|
|
|
|
+ statusBarHeight.value = systemInfo.statusBarHeight || 20;
|
|
|
|
|
+ isIOS.value = systemInfo.platform === 'ios';
|
|
|
|
|
+
|
|
|
|
|
+ // 计算底部安全区域(包含 tabbar 高度)
|
|
|
|
|
+ const safeAreaInsetsBottom = systemInfo.safeAreaInsets ? (systemInfo.safeAreaInsets.bottom || 0) : 0;
|
|
|
|
|
+ // 转换为 rpx,并确保至少有基础间距
|
|
|
|
|
+ safeAreaBottom.value = Math.max(safeAreaInsetsBottom * 2, 0);
|
|
|
|
|
+
|
|
|
|
|
+ console.log('[系统信息] 平台:', systemInfo.platform, '状态栏高度:', statusBarHeight.value);
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('[系统信息] 获取失败:', e);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// 初始化消息
|
|
// 初始化消息
|
|
|
initMessages();
|
|
initMessages();
|
|
@@ -887,44 +1034,79 @@
|
|
|
scrollToBottom();
|
|
scrollToBottom();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- // 监听来自 renderjs 的自定义事件(仅在支持的平台)
|
|
|
|
|
- // #ifdef H5 || APP-PLUS
|
|
|
|
|
|
|
+ // 监听来自 renderjs 的自定义事件(仅在 H5 平台)
|
|
|
|
|
+ // #ifdef H5
|
|
|
if (typeof window !== 'undefined') {
|
|
if (typeof window !== 'undefined') {
|
|
|
window.addEventListener('renderjs-stream-data', (event) => {
|
|
window.addEventListener('renderjs-stream-data', (event) => {
|
|
|
- console.log('通过事件接收到数据:', event.detail);
|
|
|
|
|
|
|
+ console.log('[H5] 通过事件接收到数据:', event.detail);
|
|
|
onStreamData(event.detail);
|
|
onStreamData(event.detail);
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
// #endif
|
|
// #endif
|
|
|
|
|
+
|
|
|
|
|
+ console.log('[生命周期] 组件挂载完成');
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
// 组件销毁时清理资源
|
|
// 组件销毁时清理资源
|
|
|
onBeforeUnmount(() => {
|
|
onBeforeUnmount(() => {
|
|
|
|
|
+ console.log('[生命周期] 组件即将卸载,清理资源');
|
|
|
|
|
+
|
|
|
// 停止打字机效果
|
|
// 停止打字机效果
|
|
|
stopTypingEffect();
|
|
stopTypingEffect();
|
|
|
|
|
|
|
|
|
|
+ // #ifdef H5
|
|
|
// 通知 renderjs 停止连接
|
|
// 通知 renderjs 停止连接
|
|
|
if (isProcessing.value) {
|
|
if (isProcessing.value) {
|
|
|
- renderjsData.value = {
|
|
|
|
|
- action: 'stop',
|
|
|
|
|
- timestamp: Date.now()
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ try {
|
|
|
|
|
+ renderjsData.value = {
|
|
|
|
|
+ action: 'stop',
|
|
|
|
|
+ timestamp: Date.now()
|
|
|
|
|
+ };
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('[H5] 停止 renderjs 失败:', e);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 清理消息队列
|
|
|
|
|
- messageQueue.value = [];
|
|
|
|
|
-
|
|
|
|
|
- // 移除事件监听器(仅在支持的平台)
|
|
|
|
|
- // #ifdef H5 || APP-PLUS
|
|
|
|
|
|
|
+ // 移除事件监听器
|
|
|
if (typeof window !== 'undefined') {
|
|
if (typeof window !== 'undefined') {
|
|
|
- window.removeEventListener('renderjs-stream-data', onStreamData);
|
|
|
|
|
|
|
+ try {
|
|
|
|
|
+ window.removeEventListener('renderjs-stream-data', onStreamData);
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('[H5] 移除事件监听失败:', e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // #endif
|
|
|
|
|
+
|
|
|
|
|
+ // #ifdef APP-HARMONY
|
|
|
|
|
+ // 停止鸿蒙端请求
|
|
|
|
|
+ if (isProcessing.value) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ stopSSERequestNative();
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('[鸿蒙] 停止请求失败:', e);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
// #endif
|
|
// #endif
|
|
|
|
|
+
|
|
|
|
|
+ // #ifdef MP-WEIXIN
|
|
|
|
|
+ // 停止微信小程序请求
|
|
|
|
|
+ if (isProcessing.value) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ stopWeixinRequest();
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('[微信小程序] 停止请求失败:', e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // #endif
|
|
|
|
|
+
|
|
|
|
|
+ // 清理消息队列
|
|
|
|
|
+ messageQueue.value = [];
|
|
|
|
|
+ isProcessing.value = false;
|
|
|
})
|
|
})
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
-<!-- #ifdef H5 || APP-PLUS -->
|
|
|
|
|
|
|
+<!-- #ifndef APP-HARMONY -->
|
|
|
<script module="renderModule" lang="renderjs">
|
|
<script module="renderModule" lang="renderjs">
|
|
|
// renderjs 模块状态
|
|
// renderjs 模块状态
|
|
|
let eventSource = null;
|
|
let eventSource = null;
|