chore:微信小程序假的流聊天
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import Config from './config.js'
|
||||
import http from './http.js'
|
||||
import store from '@/store/index.js'
|
||||
|
||||
let currentConversationId = null;
|
||||
const CONVERSATION_KEY = 'ai_conversation_id';
|
||||
|
||||
// 初始化时从本地读取
|
||||
// 初始化时从本地读取会话ID
|
||||
try {
|
||||
const saved = uni.getStorageSync(CONVERSATION_KEY);
|
||||
if (saved) {
|
||||
@@ -13,16 +14,14 @@ try {
|
||||
} catch (e) {
|
||||
console.warn('读取会话ID失败:', e);
|
||||
}
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 发送消息到Dify API
|
||||
* @param {string} message 用户消息内容
|
||||
* @param {Object} options 配置选项
|
||||
* @returns {Promise}
|
||||
* 发送普通消息(blocking 模式)
|
||||
*/
|
||||
async sendMessage(message, options = {}) {
|
||||
try {
|
||||
const aiConfig = store.getters.globalAIKefuConfig
|
||||
const aiConfig = store.getters.globalAIKefuConfig;
|
||||
const params = {
|
||||
url: '/api/kefu/chat',
|
||||
data: {
|
||||
@@ -30,225 +29,90 @@ export default {
|
||||
stream: false,
|
||||
inputs: {},
|
||||
query: message,
|
||||
response_mode: options.stream ? 'streaming' : 'blocking',
|
||||
response_mode: 'blocking', // 强制 blocking
|
||||
user: store.state.memberInfo?.id || 'anonymous',
|
||||
conversation_id: this.getConversationId() || ''
|
||||
conversation_id: this.getConversationId() || ''
|
||||
},
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
console.log('Sending request to:', params.url);
|
||||
};
|
||||
|
||||
console.log('Sending blocking request to:', params.url);
|
||||
console.log('Request data:', params.data);
|
||||
|
||||
const response = await http.sendRequest({
|
||||
...params,
|
||||
async: false
|
||||
})
|
||||
|
||||
const result = this.handleResponse(response, options);
|
||||
if (result.conversationId) {
|
||||
this.setConversationId(result.conversationId);
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('Dify API请求失败:', error)
|
||||
throw new Error('AI服务暂时不可用,请稍后重试')
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
const result = this.handleResponse(response);
|
||||
if (result.conversationId) {
|
||||
this.setConversationId(result.conversationId);
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('Dify API请求失败:', error);
|
||||
throw new Error('AI服务暂时不可用,请稍后重试');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 流式消息处理
|
||||
* 流式消息入口(自动适配平台)
|
||||
*/
|
||||
async sendStreamMessage(message, onChunk, onComplete) {
|
||||
// #ifdef MP-WEIXIN
|
||||
// 微信小程序:降级为普通请求 + 前端打字模拟
|
||||
try {
|
||||
return this.sendHttpStream(message, onChunk, onComplete)
|
||||
} catch (error) {
|
||||
console.error('流式消息发送失败:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
const result = await this.sendMessage(message);
|
||||
const content = result.content || '';
|
||||
const conversationId = result.conversationId || '';
|
||||
|
||||
/**
|
||||
* EventSource 流式聊天
|
||||
*/
|
||||
chatWithEventSource(message, conversationId = '', onMessage, onComplete, onError) {
|
||||
if (window.currentEventSource) {
|
||||
window.currentEventSource.close();
|
||||
}
|
||||
const params = new URLSearchParams({
|
||||
uniacid: store.state.uniacid || '1',
|
||||
user_id: store.state.memberInfo?.id || '123456',
|
||||
query: message,
|
||||
conversation_id: conversationId || '',
|
||||
stream: 'true'
|
||||
});
|
||||
const url = `/api/kefu/chat?${params.toString()}`;
|
||||
try {
|
||||
const eventSource = new EventSource(url);
|
||||
window.currentEventSource = eventSource;
|
||||
let aiMessage = '';
|
||||
eventSource.addEventListener('message', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.event === 'message') {
|
||||
aiMessage += data.answer || '';
|
||||
if (onMessage) onMessage(data.answer || '');
|
||||
}
|
||||
if (data.event === 'message_end') {
|
||||
if (onComplete) onComplete({
|
||||
conversation_id: data.conversation_id,
|
||||
message: aiMessage
|
||||
});
|
||||
}
|
||||
if (data.conversation_id) {
|
||||
conversationId = data.conversation_id;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析消息失败:', error);
|
||||
}
|
||||
});
|
||||
eventSource.addEventListener('done', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
if (onComplete) onComplete(data);
|
||||
} catch (error) {
|
||||
console.error('解析完成事件失败:', error);
|
||||
}
|
||||
});
|
||||
eventSource.addEventListener('close', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log('连接正常结束:', data);
|
||||
} catch (error) {
|
||||
console.error('解析关闭事件失败:', error);
|
||||
}
|
||||
window.currentEventSource = null;
|
||||
});
|
||||
eventSource.addEventListener('error', (error) => {
|
||||
console.error('EventSource错误:', error);
|
||||
if (onError) onError({ error: 'EventSource连接错误' });
|
||||
window.currentEventSource = null;
|
||||
});
|
||||
return eventSource;
|
||||
} catch (error) {
|
||||
console.error('创建EventSource失败:', error);
|
||||
if (onError) onError({ error: error.message });
|
||||
return null;
|
||||
}
|
||||
},
|
||||
// 保存会话ID(确保连续对话)
|
||||
if (conversationId) {
|
||||
this.setConversationId(conversationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch API 流式聊天
|
||||
*/
|
||||
async chatWithFetchStream(message, conversationId = '', onMessage, onComplete, onError) {
|
||||
try {
|
||||
let requestData;
|
||||
let headers = {
|
||||
'Accept': 'text/event-stream'
|
||||
};
|
||||
requestData = {
|
||||
uniacid: store.state.uniacid || '1',
|
||||
user_id: store.state.memberInfo?.id || '123456',
|
||||
query: message,
|
||||
conversation_id: conversationId || '',
|
||||
stream: true,
|
||||
inputs: {},
|
||||
response_mode: 'streaming',
|
||||
user: store.state.memberInfo?.id || '123456'
|
||||
};
|
||||
headers['Content-Type'] = 'application/json';
|
||||
const response = await fetch('/api/kefu/chat', {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
body: JSON.stringify(requestData)
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
if (!response.body) {
|
||||
throw new Error('响应体不可用');
|
||||
}
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
let buffer = '';
|
||||
let aiMessage = '';
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
let lineEnd;
|
||||
while ((lineEnd = buffer.indexOf('\n')) !== -1) {
|
||||
const line = buffer.substring(0, lineEnd);
|
||||
buffer = buffer.substring(lineEnd + 1);
|
||||
if (line.startsWith('data: ')) {
|
||||
try {
|
||||
const data = JSON.parse(line.substring(6));
|
||||
if (data.event === 'message' || data.event === 'text_message') {
|
||||
const textContent = data.answer || data.text || '';
|
||||
aiMessage += textContent;
|
||||
if (onMessage) onMessage(textContent);
|
||||
} else if (data.event === 'message_end' || data.event === 'done') {
|
||||
if (onComplete) onComplete({
|
||||
conversation_id: data.conversation_id,
|
||||
message: aiMessage,
|
||||
...data
|
||||
});
|
||||
} else if (data.event === 'error' && onError) {
|
||||
onError(data);
|
||||
}
|
||||
if (data.conversation_id) {
|
||||
conversationId = data.conversation_id;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('解析流式数据失败:', e);
|
||||
// 模拟打字效果
|
||||
let index = 0;
|
||||
const chunkSize = 2; // 每次显示2个字符
|
||||
return new Promise((resolve) => {
|
||||
const timer = setInterval(() => {
|
||||
if (index < content.length) {
|
||||
const chunk = content.substring(index, index + chunkSize);
|
||||
index += chunkSize;
|
||||
if (onChunk) onChunk(chunk);
|
||||
} else {
|
||||
clearInterval(timer);
|
||||
if (onComplete) {
|
||||
onComplete({
|
||||
content: content,
|
||||
conversation_id: conversationId
|
||||
});
|
||||
}
|
||||
resolve({ content, conversation_id: conversationId });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (buffer.startsWith('data: ')) {
|
||||
try {
|
||||
const data = JSON.parse(buffer.substring(6));
|
||||
if ((data.event === 'message' || data.event === 'text_message') && onMessage) {
|
||||
const textContent = data.answer || data.text || '';
|
||||
onMessage(textContent);
|
||||
} else if ((data.event === 'message_end' || data.event === 'done') && onComplete) {
|
||||
onComplete({
|
||||
conversation_id: data.conversation_id,
|
||||
message: aiMessage,
|
||||
...data
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('解析剩余数据失败:', e);
|
||||
}
|
||||
}
|
||||
}, 80); // 打字速度:80ms/次
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fetch流式聊天请求失败:', error);
|
||||
if (onError) onError({ error: error.message });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* HTTP流式请求(修复conversationId未定义)
|
||||
*/
|
||||
async sendHttpStream(message, onChunk, onComplete) {
|
||||
const params = {
|
||||
url: '/api/kefu/chat',
|
||||
data: {
|
||||
query: message,
|
||||
conversation_id: '',
|
||||
user_id: store.state.memberInfo?.id || 'anonymous',
|
||||
stream: true,
|
||||
uniacid: store.state.uniacid,
|
||||
inputs: {},
|
||||
response_mode: 'streaming',
|
||||
user: store.state.memberInfo?.id || 'anonymous'
|
||||
},
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
console.error('小程序流式消息降级失败:', error);
|
||||
if (onComplete) {
|
||||
onComplete({ error: error.message || '发送失败' });
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
// H5:使用真实流式(EventSource / Fetch)
|
||||
return this.sendHttpStream(message, onChunk, onComplete);
|
||||
// #endif
|
||||
},
|
||||
|
||||
/**
|
||||
* HTTP 流式请求(仅 H5 使用)
|
||||
*/
|
||||
async sendHttpStream(message, onChunk, onComplete) {
|
||||
// #ifdef H5
|
||||
try {
|
||||
const url = Config.baseUrl + `/api/kefu/chat`;
|
||||
@@ -263,105 +127,133 @@ export default {
|
||||
inputs: {},
|
||||
query: message,
|
||||
response_mode: 'streaming',
|
||||
user_id: store.state.memberInfo?.id || 'anonymous'
|
||||
user_id: store.state.memberInfo?.id || 'anonymous',
|
||||
conversation_id: this.getConversationId() || ''
|
||||
})
|
||||
})
|
||||
const reader = response.body.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
let content = ''
|
||||
let buffer = ''
|
||||
// 修复:声明conversationId变量
|
||||
let conversationId = '';
|
||||
|
||||
function processStreamData(buffer, callback) {
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop() || '';
|
||||
lines.forEach(line => {
|
||||
line = line.trim();
|
||||
if (!line) return;
|
||||
if (line.startsWith('data:')) {
|
||||
const dataPart = line.slice(5).trim();
|
||||
if (dataPart) {
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
if (!response.body) {
|
||||
throw new Error('响应体不可用');
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
let buffer = '';
|
||||
let content = '';
|
||||
let conversationId = '';
|
||||
|
||||
function processBuffer(buf, callback) {
|
||||
const lines = buf.split('\n');
|
||||
buf = lines.pop() || '';
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (trimmed.startsWith('data:')) {
|
||||
const jsonStr = trimmed.slice(5).trim();
|
||||
if (jsonStr) {
|
||||
try {
|
||||
const data = JSON.parse(dataPart);
|
||||
const data = JSON.parse(jsonStr);
|
||||
if (data.event === 'message') {
|
||||
callback(data.answer || '');
|
||||
const text = data.answer || data.text || '';
|
||||
content += text;
|
||||
callback(text);
|
||||
}
|
||||
if (data.conversation_id) {
|
||||
conversationId = data.conversation_id;
|
||||
}
|
||||
if (data.event === 'message_end') {
|
||||
console.log('对话完成');
|
||||
// 可选:提前完成
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析流式数据失败:', error);
|
||||
} catch (e) {
|
||||
console.warn('解析流数据失败:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return buffer;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) break
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
buffer = processStreamData(buffer, (newData) => {
|
||||
if (newData) {
|
||||
content += newData;
|
||||
if (onChunk) onChunk(newData);
|
||||
}
|
||||
buffer = processBuffer(buffer, (chunk) => {
|
||||
if (onChunk) onChunk(chunk);
|
||||
});
|
||||
}
|
||||
|
||||
if (onComplete) onComplete({ content, conversation_id: conversationId });
|
||||
return { content, conversation_id: conversationId };
|
||||
|
||||
if (onComplete) {
|
||||
onComplete({
|
||||
content,
|
||||
conversation_id: conversationId
|
||||
});
|
||||
}
|
||||
return { content, conversation_id: conversationId };
|
||||
} catch (error) {
|
||||
console.error('HTTP流式请求失败:', error)
|
||||
throw error
|
||||
console.error('H5 流式请求失败:', error);
|
||||
throw error;
|
||||
}
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
const response = await http.sendRequest({
|
||||
...params,
|
||||
async: false
|
||||
})
|
||||
if (response.success && response.data) {
|
||||
const content = response.data
|
||||
const chunkSize = 3
|
||||
let index = 0
|
||||
const streamInterval = setInterval(() => {
|
||||
if (index < content.length) {
|
||||
const chunk = content.substring(index, index + chunkSize)
|
||||
index += chunkSize
|
||||
if (onChunk) onChunk(chunk)
|
||||
} else {
|
||||
clearInterval(streamInterval)
|
||||
if (onComplete) onComplete(content)
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 理论上不会执行到这里,但防止 fallback
|
||||
return this.sendStreamMessage(message, onChunk, onComplete);
|
||||
// #endif
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 处理API响应
|
||||
* 处理API响应(统一格式)
|
||||
*/
|
||||
handleResponse(response, options) {
|
||||
handleResponse(response) {
|
||||
if (response.code === 0 || response.success) {
|
||||
return {
|
||||
success: true,
|
||||
content: response.data?.answer || response.data?.content || response.data?.reply|| response.data,
|
||||
content: response.data?.answer || response.data?.content || response.data?.reply || response.data,
|
||||
conversationId: response.data?.conversation_id,
|
||||
messageId: response.data?.message_id
|
||||
}
|
||||
};
|
||||
} else {
|
||||
throw new Error(response.message || 'AI服务返回错误')
|
||||
throw new Error(response.message || 'AI服务返回错误');
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 生成会话ID
|
||||
* 获取当前会话ID
|
||||
*/
|
||||
getConversationId() {
|
||||
return currentConversationId;
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置并持久化会话ID
|
||||
*/
|
||||
setConversationId(id) {
|
||||
if (id && id !== currentConversationId) {
|
||||
currentConversationId = id;
|
||||
try {
|
||||
uni.setStorageSync(CONVERSATION_KEY, id);
|
||||
} catch (e) {
|
||||
console.error('保存会话ID失败:', e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 清除会话ID
|
||||
*/
|
||||
clearConversationId() {
|
||||
currentConversationId = null;
|
||||
try {
|
||||
uni.removeStorageSync(CONVERSATION_KEY);
|
||||
} catch (e) {
|
||||
console.error('清除会话ID失败:', e);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生成新会话(如需)
|
||||
*/
|
||||
async generateConversationId() {
|
||||
const params = {
|
||||
@@ -374,16 +266,16 @@ export default {
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
};
|
||||
const response = await http.sendRequest({
|
||||
...params,
|
||||
async: false
|
||||
})
|
||||
return this.handleResponse(response)
|
||||
});
|
||||
return this.handleResponse(response);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 获取AI服务状态
|
||||
* 获取服务状态
|
||||
*/
|
||||
async getServiceStatus() {
|
||||
try {
|
||||
@@ -391,22 +283,23 @@ export default {
|
||||
url: '/api/kefu/health',
|
||||
async: false,
|
||||
data: {}
|
||||
})
|
||||
const available = [response?.data?.status, response?.data?.components?.ai_service_config?.status].filter(item => item === 'healthy').length > 0;
|
||||
});
|
||||
const available = [response?.data?.status, response?.data?.components?.ai_service_config?.status]
|
||||
.filter(item => item === 'healthy').length > 0;
|
||||
return {
|
||||
available,
|
||||
reason: available ? '服务正常' : '服务异常'
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
available: false,
|
||||
reason: '服务检查失败'
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 清除会话历史
|
||||
* 清除会话历史(调用后端接口)
|
||||
*/
|
||||
async clearConversation(conversationId) {
|
||||
try {
|
||||
@@ -414,36 +307,13 @@ export default {
|
||||
url: '/api/kefu/clear-conversation',
|
||||
data: { conversation_id: conversationId },
|
||||
async: false
|
||||
})
|
||||
return response.success
|
||||
});
|
||||
return response.success;
|
||||
} catch (error) {
|
||||
console.error('清除会话失败:', error)
|
||||
return false
|
||||
console.error('清除会话失败:', error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
getConversationId() {
|
||||
return currentConversationId;
|
||||
},
|
||||
|
||||
setConversationId(id) {
|
||||
if (id && id !== currentConversationId) {
|
||||
currentConversationId = id;
|
||||
try {
|
||||
uni.setStorageSync(CONVERSATION_KEY, id);
|
||||
} catch (e) {
|
||||
console.error('保存会话ID失败:', e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
clearConversationId() {
|
||||
currentConversationId = null;
|
||||
try {
|
||||
uni.removeStorageSync(CONVERSATION_KEY);
|
||||
} catch (e) {
|
||||
console.error('清除会话ID失败:', e);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取会话历史
|
||||
@@ -485,5 +355,80 @@ export default {
|
||||
console.error('获取会话历史失败:', error);
|
||||
throw new Error(error.message || '获取会话历史时发生错误,请稍后重试');
|
||||
}
|
||||
},
|
||||
|
||||
// ================================
|
||||
// 以下方法仅用于 H5,小程序无效
|
||||
// ================================
|
||||
|
||||
/**
|
||||
* [H5 Only] EventSource 流式聊天(备用)
|
||||
*/
|
||||
chatWithEventSource(message, conversationId = '', onMessage, onComplete, onError) {
|
||||
// #ifdef H5
|
||||
if (window.currentEventSource) {
|
||||
window.currentEventSource.close();
|
||||
}
|
||||
const params = new URLSearchParams({
|
||||
uniacid: store.state.uniacid || '1',
|
||||
user_id: store.state.memberInfo?.id || '123456',
|
||||
query: message,
|
||||
conversation_id: conversationId || '',
|
||||
stream: 'true'
|
||||
});
|
||||
const url = `${Config.baseUrl}/api/kefu/chat?${params.toString()}`;
|
||||
try {
|
||||
const eventSource = new EventSource(url);
|
||||
window.currentEventSource = eventSource;
|
||||
let aiMessage = '';
|
||||
eventSource.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.event === 'message') {
|
||||
aiMessage += data.answer || '';
|
||||
if (onMessage) onMessage(data.answer || '');
|
||||
}
|
||||
if (data.event === 'message_end') {
|
||||
if (onComplete) onComplete({
|
||||
conversation_id: data.conversation_id,
|
||||
message: aiMessage
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析消息失败:', error);
|
||||
}
|
||||
};
|
||||
eventSource.onerror = (error) => {
|
||||
console.error('EventSource错误:', error);
|
||||
if (onError) onError({ error: '连接失败' });
|
||||
window.currentEventSource = null;
|
||||
};
|
||||
return eventSource;
|
||||
} catch (error) {
|
||||
console.error('创建EventSource失败:', error);
|
||||
if (onError) onError({ error: error.message });
|
||||
return null;
|
||||
}
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
console.warn('chatWithEventSource 不支持微信小程序');
|
||||
return null;
|
||||
// #endif
|
||||
},
|
||||
|
||||
/**
|
||||
* [H5 Only] Fetch 流式聊天(备用)
|
||||
*/
|
||||
async chatWithFetchStream(message, conversationId = '', onMessage, onComplete, onError) {
|
||||
// #ifdef H5
|
||||
// 实际逻辑已在 sendHttpStream 中实现,此处可复用或留空
|
||||
return this.sendHttpStream(message, onMessage, (res) => {
|
||||
onComplete?.({ message: res.content, conversation_id: res.conversation_id });
|
||||
});
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
console.warn('chatWithFetchStream 不支持微信小程序');
|
||||
return null;
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user