You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
493 lines
12 KiB
493 lines
12 KiB
import http from '../../../utils/api'
|
|
|
|
Page({
|
|
data: {
|
|
// 当前时间
|
|
currentTime: '',
|
|
// 聊天消息
|
|
messages: [],
|
|
// 输入框相关
|
|
inputValue: '',
|
|
autoFocus: false,
|
|
inputHeight: 60,
|
|
isKeyboardShow: false,
|
|
// 滚动控制
|
|
scrollIntoView: 'welcome-message',
|
|
scrollAnimation: true,
|
|
isUserScrolling: false, // 用户是否正在手动滚动
|
|
scrollTimer: null, // 滚动定时器
|
|
lastMessageCount: 0, // 上次消息数量
|
|
// 症状选择
|
|
quickSymptoms: [],
|
|
wzsearch: {},
|
|
selectedSymptoms: [],
|
|
showSymptomSelector: false,
|
|
// 状态控制
|
|
isAIThinking: false,
|
|
isLoading: false,
|
|
loadingText: '',
|
|
showMoreMenu: false
|
|
},
|
|
|
|
onLoad() {
|
|
this.initData();
|
|
this.getInquiry();
|
|
// 监听键盘高度变化
|
|
wx.onKeyboardHeightChange(this.onKeyboardHeightChange.bind(this));
|
|
},
|
|
|
|
onUnload() {
|
|
// 页面卸载时移除监听
|
|
wx.offKeyboardHeightChange(this.onKeyboardHeightChange);
|
|
// 清除定时器
|
|
if (this.data.scrollTimer) {
|
|
clearTimeout(this.data.scrollTimer);
|
|
}
|
|
},
|
|
|
|
// 监听滚动事件
|
|
onScroll(e) {
|
|
// 检测用户是否在手动滚动
|
|
const scrollTop = e.detail.scrollTop;
|
|
|
|
// 获取滚动容器和底部元素的位置
|
|
const query = wx.createSelectorQuery();
|
|
query.select('#scrollBottom').boundingClientRect();
|
|
query.select('#chatScroll').boundingClientRect();
|
|
query.exec((res) => {
|
|
if (res[0] && res[1]) {
|
|
const bottomPosition = res[0].top - res[1].top + res[1].height;
|
|
// 如果滚动位置不在底部(偏差超过50px),认为是用户手动滚动
|
|
if (Math.abs(scrollTop - bottomPosition) > 100) {
|
|
if (!this.data.isUserScrolling) {
|
|
this.setData({
|
|
isUserScrolling: true
|
|
});
|
|
}
|
|
|
|
// 清除之前的定时器
|
|
if (this.data.scrollTimer) {
|
|
clearTimeout(this.data.scrollTimer);
|
|
}
|
|
|
|
// 5秒后重置用户滚动状态
|
|
const timer = setTimeout(() => {
|
|
this.setData({
|
|
isUserScrolling: false
|
|
});
|
|
// 重置后检查是否需要滚动到底部
|
|
this.checkAndScrollToBottom();
|
|
}, 5000);
|
|
this.setData({ scrollTimer: timer });
|
|
}
|
|
}
|
|
});
|
|
},
|
|
|
|
// 滚动到底部事件
|
|
onScrollToLower() {
|
|
// 用户滚动到底部时,重置滚动状态
|
|
this.setData({
|
|
isUserScrolling: false
|
|
});
|
|
},
|
|
|
|
// 键盘高度变化监听
|
|
onKeyboardHeightChange(res) {
|
|
if (res.height > 0) {
|
|
// 键盘弹起
|
|
this.setData({
|
|
isKeyboardShow: true
|
|
});
|
|
// 强制滚动到底部
|
|
this.scrollToBottom(true);
|
|
} else {
|
|
// 键盘收起
|
|
this.setData({
|
|
isKeyboardShow: false
|
|
});
|
|
}
|
|
},
|
|
|
|
// 检查并滚动到底部
|
|
checkAndScrollToBottom() {
|
|
// 如果用户没有手动滚动,则滚动到底部
|
|
if (!this.data.isUserScrolling) {
|
|
this.scrollToBottom(true);
|
|
}
|
|
},
|
|
|
|
// AI问诊快捷字列表
|
|
getInquiry() {
|
|
http.inquiry({
|
|
data: {},
|
|
success: res => {
|
|
console.log('快捷症状列表', res);
|
|
this.setData({
|
|
quickSymptoms: res.rows
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
onShow() {
|
|
this.updateCurrentTime();
|
|
// 延迟设置焦点,避免影响滚动
|
|
setTimeout(() => {
|
|
this.setData({
|
|
autoFocus: true
|
|
});
|
|
}, 300);
|
|
},
|
|
|
|
// 初始化数据
|
|
initData() {
|
|
// 设置当前时间
|
|
this.updateCurrentTime();
|
|
|
|
// 定时更新当前时间
|
|
setInterval(() => {
|
|
this.updateCurrentTime();
|
|
}, 60000);
|
|
|
|
// 初始化消息数量
|
|
this.setData({
|
|
lastMessageCount: this.data.messages.length
|
|
});
|
|
},
|
|
|
|
// 更新当前时间
|
|
updateCurrentTime() {
|
|
const now = new Date();
|
|
const timeString = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
|
|
this.setData({
|
|
currentTime: timeString
|
|
});
|
|
},
|
|
|
|
// 滚动到底部
|
|
scrollToBottom(force = false) {
|
|
// 如果用户正在手动滚动且不是强制滚动,则不滚动
|
|
if (this.data.isUserScrolling && !force) {
|
|
console.log('用户正在手动滚动,跳过自动滚动');
|
|
return;
|
|
}
|
|
|
|
// 获取最新消息的ID
|
|
let targetId = 'scrollBottom';
|
|
|
|
// 优先滚动到最新的消息
|
|
if (this.data.messages.length > 0) {
|
|
targetId = `msg-${this.data.messages.length - 1}`;
|
|
} else {
|
|
targetId = 'welcome-message';
|
|
}
|
|
|
|
console.log('滚动到:', targetId);
|
|
|
|
// 先重置scrollIntoView,确保能触发滚动
|
|
this.setData({
|
|
scrollIntoView: '',
|
|
scrollAnimation: true
|
|
}, () => {
|
|
// 延迟一下再设置滚动目标,确保视图更新
|
|
setTimeout(() => {
|
|
this.setData({
|
|
scrollIntoView: targetId,
|
|
scrollAnimation: true
|
|
});
|
|
}, 50);
|
|
});
|
|
|
|
// 多重保障滚动
|
|
setTimeout(() => {
|
|
if (!this.data.isUserScrolling || force) {
|
|
this.setData({
|
|
scrollIntoView: targetId
|
|
});
|
|
}
|
|
}, 150);
|
|
|
|
setTimeout(() => {
|
|
if (!this.data.isUserScrolling || force) {
|
|
this.setData({
|
|
scrollIntoView: targetId
|
|
});
|
|
}
|
|
}, 300);
|
|
},
|
|
|
|
// 输入框行数变化
|
|
onInputLineChange(e) {
|
|
// 输入框高度变化时,确保滚动到底部
|
|
if (this.data.isKeyboardShow) {
|
|
this.scrollToBottom();
|
|
}
|
|
},
|
|
|
|
// 输入框聚焦
|
|
onInputFocus(e) {
|
|
this.setData({
|
|
autoFocus: true,
|
|
isKeyboardShow: true
|
|
});
|
|
// 延迟滚动到底部,等待键盘完全弹起
|
|
setTimeout(() => {
|
|
this.scrollToBottom(true);
|
|
}, 300);
|
|
},
|
|
|
|
// 输入框失焦
|
|
onInputBlur(e) {
|
|
this.setData({
|
|
autoFocus: false,
|
|
isKeyboardShow: false
|
|
});
|
|
},
|
|
|
|
// 输入框变化
|
|
onInput(e) {
|
|
this.setData({
|
|
inputValue: e.detail.value
|
|
});
|
|
},
|
|
|
|
// 发送消息
|
|
sendMessage() {
|
|
const message = this.data.inputValue.trim();
|
|
if (!message) return;
|
|
|
|
// 添加用户消息
|
|
const userMessage = {
|
|
id: Date.now(),
|
|
type: 'user',
|
|
content: message,
|
|
time: this.getCurrentTime()
|
|
};
|
|
|
|
// 更新消息列表
|
|
const newMessages = [...this.data.messages, userMessage];
|
|
|
|
this.setData({
|
|
inputValue: '',
|
|
autoFocus: true,
|
|
inputHeight: 60,
|
|
messages: newMessages,
|
|
isUserScrolling: false, // 发送消息时重置用户滚动状态
|
|
lastMessageCount: newMessages.length
|
|
});
|
|
|
|
// 立即滚动到底部
|
|
this.scrollToBottom(true);
|
|
|
|
// 显示AI思考状态
|
|
this.setData({
|
|
isAIThinking: true
|
|
});
|
|
|
|
// 滚动到底部(显示思考状态后)
|
|
setTimeout(() => {
|
|
this.scrollToBottom(true);
|
|
}, 100);
|
|
|
|
// 模拟AI思考时间
|
|
setTimeout(() => {
|
|
this.generateAIResponse(message);
|
|
}, 1500 + Math.random() * 1000);
|
|
},
|
|
|
|
// 获取当前时间
|
|
getCurrentTime() {
|
|
const now = new Date();
|
|
return `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
|
|
},
|
|
|
|
// 生成AI响应
|
|
generateAIResponse(userMessage) {
|
|
console.log('发送消息:', userMessage);
|
|
|
|
http.search({
|
|
data: {
|
|
keyword: userMessage
|
|
},
|
|
success: res => {
|
|
console.log('AI响应:', res);
|
|
|
|
let aiMessage;
|
|
if (res.rows && res.rows.length > 0) {
|
|
const wzsearch = res.rows[0];
|
|
|
|
aiMessage = {
|
|
id: Date.now() + 1,
|
|
type: 'assistant',
|
|
content: '根据您的描述,' + (wzsearch.title || '已收到您的症状描述'),
|
|
diagnosis: wzsearch,
|
|
time: this.getCurrentTime()
|
|
};
|
|
} else {
|
|
aiMessage = {
|
|
id: Date.now() + 1,
|
|
type: 'assistant',
|
|
content: '已收到您的症状描述',
|
|
diagnosis: {
|
|
possibleDiseases: '暂无匹配诊断',
|
|
severityLevel: '未知',
|
|
suggestions: '建议您提供更详细的症状描述,或直接咨询专业兽医'
|
|
},
|
|
time: this.getCurrentTime()
|
|
};
|
|
}
|
|
|
|
// 添加AI消息
|
|
const newMessages = [...this.data.messages, aiMessage];
|
|
|
|
this.setData({
|
|
messages: newMessages,
|
|
isAIThinking: false,
|
|
isUserScrolling: false, // 收到新消息时强制重置用户滚动状态
|
|
lastMessageCount: newMessages.length
|
|
}, () => {
|
|
// 消息添加后立即滚动到底部
|
|
console.log('AI消息已添加,滚动到底部');
|
|
this.scrollToBottom(true);
|
|
|
|
// 多重保险滚动
|
|
setTimeout(() => {
|
|
this.scrollToBottom(true);
|
|
}, 100);
|
|
|
|
setTimeout(() => {
|
|
this.scrollToBottom(true);
|
|
}, 300);
|
|
|
|
setTimeout(() => {
|
|
this.scrollToBottom(true);
|
|
}, 500);
|
|
});
|
|
},
|
|
fail: err => {
|
|
console.error('API请求失败:', err);
|
|
|
|
const aiMessage = {
|
|
id: Date.now() + 1,
|
|
type: 'assistant',
|
|
content: '已收到您的症状描述',
|
|
diagnosis: {
|
|
possibleDiseases: '网络请求失败',
|
|
severityLevel: '未知',
|
|
suggestions: '请检查网络连接后重试,或直接联系兽医'
|
|
},
|
|
time: this.getCurrentTime()
|
|
};
|
|
|
|
const newMessages = [...this.data.messages, aiMessage];
|
|
|
|
this.setData({
|
|
messages: newMessages,
|
|
isAIThinking: false,
|
|
isUserScrolling: false,
|
|
lastMessageCount: newMessages.length
|
|
}, () => {
|
|
this.scrollToBottom(true);
|
|
});
|
|
},
|
|
complete: () => {
|
|
if (this.data.isAIThinking) {
|
|
this.setData({
|
|
isAIThinking: false
|
|
});
|
|
}
|
|
}
|
|
});
|
|
},
|
|
|
|
// 选择快捷症状
|
|
selectQuickSymptom(e) {
|
|
const symptom = e.currentTarget.dataset.symptom.keywords;
|
|
this.setData({
|
|
inputValue: symptom
|
|
});
|
|
this.sendMessage();
|
|
},
|
|
|
|
// 返回
|
|
goBack() {
|
|
wx.navigateBack();
|
|
},
|
|
|
|
// 显示更多菜单
|
|
showMoreMenu() {
|
|
this.setData({
|
|
showMoreMenu: true
|
|
});
|
|
},
|
|
|
|
// 关闭更多菜单
|
|
closeMoreMenu() {
|
|
this.setData({
|
|
showMoreMenu: false
|
|
});
|
|
},
|
|
|
|
// 阻止事件冒泡
|
|
stopPropagation() {},
|
|
|
|
// 清空记录
|
|
clearHistory() {
|
|
wx.showModal({
|
|
title: '提示',
|
|
content: '确定要清空所有聊天记录吗?',
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
this.setData({
|
|
messages: [],
|
|
selectedSymptoms: [],
|
|
isUserScrolling: false,
|
|
lastMessageCount: 0
|
|
}, () => {
|
|
// 清空后滚动到欢迎消息
|
|
this.setData({
|
|
scrollIntoView: 'welcome-message'
|
|
});
|
|
});
|
|
this.closeMoreMenu();
|
|
}
|
|
}
|
|
});
|
|
},
|
|
|
|
// 导出记录
|
|
exportChat() {
|
|
wx.showToast({
|
|
title: '记录已保存到本地',
|
|
icon: 'success'
|
|
});
|
|
this.closeMoreMenu();
|
|
},
|
|
|
|
// 联系兽医
|
|
contactDoctor() {
|
|
wx.showModal({
|
|
title: '联系兽医',
|
|
content: '确定要拨打兽医热线吗?',
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
wx.makePhoneCall({
|
|
phoneNumber: '400-123-4567'
|
|
});
|
|
}
|
|
}
|
|
});
|
|
this.closeMoreMenu();
|
|
},
|
|
|
|
// 显示使用说明
|
|
showInstructions() {
|
|
wx.showModal({
|
|
title: '使用说明',
|
|
content: '1. 描述您或牲畜的症状\n2. AI助手会分析并提供建议\n3. 可使用快捷症状选择\n4. 诊断结果仅供参考,请及时咨询专业兽医',
|
|
showCancel: false
|
|
});
|
|
this.closeMoreMenu();
|
|
}
|
|
});
|