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.
642 lines
16 KiB
642 lines
16 KiB
Page({
|
|
data: {
|
|
// 专家信息
|
|
expertInfo: {
|
|
id: 1,
|
|
name: '张明专家',
|
|
title: '资深畜牧兽医',
|
|
expertise: '牛羊疾病防治',
|
|
avatar: '/images/avatars/expert1.png',
|
|
online: true,
|
|
phone: '13800138000'
|
|
},
|
|
|
|
// 用户信息
|
|
userInfo: {
|
|
id: 1001,
|
|
name: '养殖户',
|
|
avatar: '/images/avatars/user.png'
|
|
},
|
|
|
|
// 消息列表
|
|
messageList: [],
|
|
scrollTop: 0,
|
|
scrollAnimate: false,
|
|
|
|
// 输入相关 - 优化
|
|
inputValue: '',
|
|
inputFocus: false,
|
|
|
|
// 多媒体
|
|
showMediaSheet: false,
|
|
|
|
// 页面状态
|
|
showDateDivider: true,
|
|
todayDate: '',
|
|
loading: false,
|
|
loadingMore: false,
|
|
|
|
// 滚动相关
|
|
isScrolling: false,
|
|
lastScrollTop: 0,
|
|
|
|
// 当前专家ID
|
|
currentExpertId: 1,
|
|
|
|
// 分页相关
|
|
page: 1,
|
|
pageSize: 20,
|
|
hasMore: true,
|
|
|
|
// 时间显示间隔
|
|
timeInterval: 5,
|
|
lastShowTimeStamp: 0,
|
|
|
|
// 键盘高度
|
|
keyboardHeight: 0
|
|
},
|
|
|
|
onLoad: function(options) {
|
|
this.setTodayDate();
|
|
this.loadUserInfo();
|
|
|
|
if (options.expertId) {
|
|
this.setData({ currentExpertId: options.expertId });
|
|
this.loadExpertInfo(options.expertId);
|
|
} else {
|
|
this.loadChatHistory();
|
|
}
|
|
|
|
wx.onKeyboardHeightChange(this.onKeyboardHeightChange.bind(this));
|
|
},
|
|
|
|
onUnload: function() {
|
|
wx.offKeyboardHeightChange();
|
|
},
|
|
|
|
// ========== 输入框相关方法 ==========
|
|
|
|
// 输入处理
|
|
onInput: function(e) {
|
|
this.setData({
|
|
inputValue: e.detail.value
|
|
});
|
|
},
|
|
|
|
// 输入框获得焦点
|
|
onInputFocus: function() {
|
|
this.setData({
|
|
inputFocus: true
|
|
}, () => {
|
|
setTimeout(() => {
|
|
this.scrollToBottom();
|
|
}, 200);
|
|
});
|
|
},
|
|
|
|
// 输入框失去焦点
|
|
onInputBlur: function() {
|
|
this.setData({
|
|
inputFocus: false
|
|
});
|
|
},
|
|
|
|
// 清除输入
|
|
clearInput: function() {
|
|
this.setData({
|
|
inputValue: '',
|
|
inputFocus: true
|
|
});
|
|
},
|
|
|
|
// 发送文本消息
|
|
sendTextMessage: function() {
|
|
const content = this.data.inputValue.trim();
|
|
if (!content) return;
|
|
|
|
const newMessage = {
|
|
id: 'msg-' + Date.now(),
|
|
sender: 'user',
|
|
type: 'text',
|
|
content: content,
|
|
timestamp: Date.now(),
|
|
status: 'sending'
|
|
};
|
|
|
|
this.addMessageToList(newMessage);
|
|
|
|
// 清空输入框
|
|
this.setData({
|
|
inputValue: '',
|
|
inputFocus: false
|
|
});
|
|
|
|
// 模拟发送成功
|
|
setTimeout(() => {
|
|
this.updateMessageStatus(newMessage.id, 'success');
|
|
|
|
setTimeout(() => {
|
|
this.receiveExpertReply();
|
|
}, 1000);
|
|
}, 500);
|
|
},
|
|
|
|
// 添加消息到列表
|
|
addMessageToList: function(message) {
|
|
const { messageList } = this.data;
|
|
const processedMessage = this.processSingleMessageTime(message, messageList);
|
|
|
|
messageList.push(processedMessage);
|
|
|
|
this.setData({ messageList }, () => {
|
|
this.scrollToBottom();
|
|
});
|
|
},
|
|
|
|
// 更新消息状态
|
|
updateMessageStatus: function(messageId, status) {
|
|
const { messageList } = this.data;
|
|
const index = messageList.findIndex(msg => msg.id === messageId);
|
|
|
|
if (index !== -1) {
|
|
messageList[index].status = status;
|
|
this.setData({ messageList });
|
|
}
|
|
},
|
|
|
|
// 接收专家回复
|
|
receiveExpertReply: function() {
|
|
const replies = [
|
|
'收到您的消息,让我分析一下您说的情况。',
|
|
'建议您提供更多细节,比如发病时间、具体症状等。',
|
|
'根据描述,可能是饲料问题引起的,建议调整饲料配方。',
|
|
'可以考虑添加一些维生素补充剂,改善食欲问题。',
|
|
'最好能提供照片,这样我可以更准确地判断情况。'
|
|
];
|
|
|
|
const randomReply = replies[Math.floor(Math.random() * replies.length)];
|
|
|
|
const newMessage = {
|
|
id: 'exp-' + Date.now(),
|
|
sender: 'expert',
|
|
type: 'text',
|
|
content: randomReply,
|
|
timestamp: Date.now(),
|
|
status: 'success'
|
|
};
|
|
|
|
this.addMessageToList(newMessage);
|
|
},
|
|
|
|
// ========== 滚动相关 ==========
|
|
|
|
// 滚动事件
|
|
onScroll: function(e) {
|
|
const scrollTop = e.detail.scrollTop;
|
|
this.setData({
|
|
lastScrollTop: scrollTop,
|
|
isScrolling: true
|
|
});
|
|
|
|
clearTimeout(this.scrollTimer);
|
|
this.scrollTimer = setTimeout(() => {
|
|
this.setData({ isScrolling: false });
|
|
}, 200);
|
|
|
|
if (scrollTop <= 100 && !this.data.loadingMore && this.data.hasMore) {
|
|
this.loadMoreMessages();
|
|
}
|
|
},
|
|
|
|
// 加载更多消息
|
|
loadMoreMessages: function() {
|
|
if (this.data.loadingMore || !this.data.hasMore) return;
|
|
|
|
this.setData({
|
|
loadingMore: true,
|
|
page: this.data.page + 1
|
|
}, () => {
|
|
this.loadChatHistory();
|
|
});
|
|
},
|
|
|
|
// 滚动到底部
|
|
scrollToBottom: function(animate = true) {
|
|
if (this.data.isScrolling) return;
|
|
|
|
this.setData({
|
|
scrollAnimate: animate
|
|
}, () => {
|
|
setTimeout(() => {
|
|
this.setData({
|
|
scrollTop: 999999
|
|
});
|
|
}, 50);
|
|
});
|
|
},
|
|
|
|
// 键盘高度变化
|
|
onKeyboardHeightChange: function(res) {
|
|
this.setData({
|
|
keyboardHeight: res.height
|
|
});
|
|
|
|
if (res.height > 0) {
|
|
this.setData({ showMediaSheet: false });
|
|
|
|
setTimeout(() => {
|
|
this.scrollToBottom();
|
|
}, 100);
|
|
}
|
|
},
|
|
|
|
// ========== 多媒体相关 ==========
|
|
|
|
// 显示多媒体选择面板
|
|
showMediaActionSheet: function() {
|
|
this.setData({
|
|
showMediaSheet: true,
|
|
inputFocus: false
|
|
});
|
|
},
|
|
|
|
// 隐藏多媒体选择面板
|
|
hideMediaActionSheet: function() {
|
|
this.setData({
|
|
showMediaSheet: false
|
|
});
|
|
},
|
|
|
|
// 选择图片
|
|
chooseImage: function() {
|
|
this.hideMediaActionSheet();
|
|
|
|
wx.chooseImage({
|
|
count: 9,
|
|
sizeType: ['compressed'],
|
|
sourceType: ['album'],
|
|
success: (res) => {
|
|
this.uploadImages(res.tempFilePaths);
|
|
},
|
|
fail: (err) => {
|
|
wx.showToast({
|
|
title: '选择图片失败',
|
|
icon: 'none'
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
// 选择视频
|
|
chooseVideo: function() {
|
|
this.hideMediaActionSheet();
|
|
|
|
wx.chooseVideo({
|
|
sourceType: ['album'],
|
|
compressed: true,
|
|
maxDuration: 60,
|
|
success: (res) => {
|
|
this.uploadVideo(res.tempFilePath, res.thumbTempFilePath);
|
|
},
|
|
fail: (err) => {
|
|
wx.showToast({
|
|
title: '选择视频失败',
|
|
icon: 'none'
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
// 选择文件
|
|
chooseFile: function() {
|
|
this.hideMediaActionSheet();
|
|
|
|
wx.chooseMessageFile({
|
|
count: 1,
|
|
type: 'all',
|
|
success: (res) => {
|
|
const file = res.tempFiles[0];
|
|
this.uploadFile(file.path, file.name, file.size);
|
|
},
|
|
fail: (err) => {
|
|
wx.showToast({
|
|
title: '选择文件失败',
|
|
icon: 'none'
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
// 上传图片
|
|
uploadImages: function(tempFilePaths) {
|
|
tempFilePaths.forEach((tempFilePath, index) => {
|
|
const fileName = `image_${Date.now()}_${index}.jpg`;
|
|
this.uploadFile(tempFilePath, fileName, 0, 'image');
|
|
});
|
|
},
|
|
|
|
// 上传视频
|
|
uploadVideo: function(tempFilePath, thumbPath) {
|
|
const fileName = `video_${Date.now()}.mp4`;
|
|
this.uploadFile(tempFilePath, fileName, 0, 'video', thumbPath);
|
|
},
|
|
|
|
// 通用文件上传
|
|
uploadFile: function(tempFilePath, fileName, fileSize = 0, type = 'file', thumbPath = '') {
|
|
const messageId = 'file-' + Date.now();
|
|
const message = {
|
|
id: messageId,
|
|
sender: 'user',
|
|
type: type,
|
|
content: tempFilePath,
|
|
thumb: thumbPath,
|
|
fileName: fileName,
|
|
fileSize: fileSize,
|
|
extension: fileName.split('.').pop().toLowerCase(),
|
|
timestamp: Date.now(),
|
|
status: 'uploading',
|
|
progress: 0
|
|
};
|
|
|
|
this.addMessageToList(message);
|
|
this.simulateUpload(messageId, type);
|
|
},
|
|
|
|
// 模拟上传过程
|
|
simulateUpload: function(messageId, type) {
|
|
let progress = 0;
|
|
const uploadInterval = setInterval(() => {
|
|
progress += Math.random() * 20 + 10;
|
|
|
|
if (progress >= 100) {
|
|
progress = 100;
|
|
clearInterval(uploadInterval);
|
|
|
|
setTimeout(() => {
|
|
this.updateMessageStatus(messageId, 'success');
|
|
|
|
const { messageList } = this.data;
|
|
const index = messageList.findIndex(msg => msg.id === messageId);
|
|
if (index !== -1) {
|
|
delete messageList[index].progress;
|
|
this.setData({ messageList });
|
|
|
|
if (type === 'image' || type === 'video') {
|
|
setTimeout(() => {
|
|
this.receiveMediaReply(type);
|
|
}, 800);
|
|
}
|
|
}
|
|
}, 200);
|
|
}
|
|
|
|
const { messageList } = this.data;
|
|
const index = messageList.findIndex(msg => msg.id === messageId);
|
|
if (index !== -1) {
|
|
messageList[index].progress = Math.min(progress, 100);
|
|
this.setData({ messageList });
|
|
}
|
|
}, 100);
|
|
},
|
|
|
|
// 接收媒体回复
|
|
receiveMediaReply: function(type) {
|
|
const imageReplies = [
|
|
'照片收到了,牛的状况看起来确实不太理想。',
|
|
'从照片看,饲养环境需要改善一下。',
|
|
'图片清晰,我可以更准确地判断问题了。'
|
|
];
|
|
|
|
const videoReplies = [
|
|
'视频看到了,动物的精神状态需要关注。',
|
|
'从视频可以观察到更多细节,这很有帮助。',
|
|
'视频内容很有价值,让我了解了具体情况。'
|
|
];
|
|
|
|
const replies = type === 'image' ? imageReplies : videoReplies;
|
|
const randomReply = replies[Math.floor(Math.random() * replies.length)];
|
|
|
|
const newMessage = {
|
|
id: 'exp-media-' + Date.now(),
|
|
sender: 'expert',
|
|
type: 'text',
|
|
content: randomReply,
|
|
timestamp: Date.now(),
|
|
status: 'success'
|
|
};
|
|
|
|
this.addMessageToList(newMessage);
|
|
},
|
|
|
|
// 预览图片
|
|
previewImage: function(e) {
|
|
const url = e.currentTarget.dataset.url;
|
|
wx.previewImage({
|
|
current: url,
|
|
urls: [url]
|
|
});
|
|
},
|
|
|
|
// ========== 数据初始化 ==========
|
|
|
|
// 设置今天日期
|
|
setTodayDate: function() {
|
|
const now = new Date();
|
|
const month = now.getMonth() + 1;
|
|
const date = now.getDate();
|
|
const week = ['日', '一', '二', '三', '四', '五', '六'][now.getDay()];
|
|
this.setData({
|
|
todayDate: `${month}月${date}日 星期${week}`
|
|
});
|
|
},
|
|
|
|
// 加载用户信息
|
|
loadUserInfo: function() {
|
|
try {
|
|
const userInfo = wx.getStorageSync('userInfo');
|
|
if (userInfo) {
|
|
this.setData({ userInfo });
|
|
}
|
|
} catch (e) {
|
|
console.error('加载用户信息失败:', e);
|
|
}
|
|
},
|
|
|
|
// 加载专家信息
|
|
loadExpertInfo: function(expertId) {
|
|
wx.showLoading({ title: '加载中...' });
|
|
|
|
// 模拟网络请求
|
|
setTimeout(() => {
|
|
this.setData({
|
|
expertInfo: {
|
|
id: Number(expertId),
|
|
name: '张明专家',
|
|
title: '资深畜牧兽医',
|
|
expertise: '牛羊疾病防治',
|
|
avatar: '/images/avatars/expert1.png',
|
|
online: true,
|
|
phone: '13800138000'
|
|
},
|
|
loading: false
|
|
});
|
|
|
|
wx.hideLoading();
|
|
this.loadChatHistory();
|
|
}, 500);
|
|
},
|
|
|
|
// 加载聊天记录
|
|
loadChatHistory: function() {
|
|
this.setData({ loading: true });
|
|
|
|
// 模拟历史消息
|
|
setTimeout(() => {
|
|
const now = Date.now();
|
|
const mockMessages = [
|
|
{
|
|
id: 'msg-1',
|
|
sender: 'expert',
|
|
type: 'text',
|
|
content: '您好,我是张明专家,有什么可以帮您?',
|
|
timestamp: now - 10 * 60 * 1000,
|
|
status: 'success'
|
|
},
|
|
{
|
|
id: 'msg-2',
|
|
sender: 'user',
|
|
type: 'text',
|
|
content: '您好,我养的牛最近食欲不振,请问是什么原因?',
|
|
timestamp: now - 9 * 60 * 1000,
|
|
status: 'success'
|
|
},
|
|
{
|
|
id: 'msg-3',
|
|
sender: 'expert',
|
|
type: 'text',
|
|
content: '可能是饲料问题或环境变化引起的,请描述一下具体情况。',
|
|
timestamp: now - 7 * 60 * 1000,
|
|
status: 'success'
|
|
},
|
|
{
|
|
id: 'msg-4',
|
|
sender: 'user',
|
|
type: 'text',
|
|
content: '具体症状是拉稀,体温偏高,精神状态不好。',
|
|
timestamp: now - 2 * 60 * 1000,
|
|
status: 'success'
|
|
},
|
|
{
|
|
id: 'msg-5',
|
|
sender: 'expert',
|
|
type: 'text',
|
|
content: '明白了,建议您调整饲料配方,添加一些益生菌。',
|
|
timestamp: now - 1 * 60 * 1000,
|
|
status: 'success'
|
|
}
|
|
];
|
|
|
|
const processedMessages = this.processMessageTimes(mockMessages);
|
|
|
|
this.setData({
|
|
messageList: processedMessages,
|
|
loading: false,
|
|
hasMore: false
|
|
}, () => {
|
|
this.scrollToBottom(true);
|
|
});
|
|
}, 800);
|
|
},
|
|
|
|
// 处理消息时间显示
|
|
processMessageTimes: function(messages) {
|
|
if (!messages || messages.length === 0) return [];
|
|
|
|
const sortedMessages = [...messages].sort((a, b) => a.timestamp - b.timestamp);
|
|
const processedMessages = [];
|
|
let lastShowTime = null;
|
|
|
|
for (let i = 0; i < sortedMessages.length; i++) {
|
|
const msg = { ...sortedMessages[i] };
|
|
|
|
if (!msg.timestamp || isNaN(msg.timestamp) || msg.timestamp <= 0) {
|
|
msg.timestamp = Date.now() - (sortedMessages.length - i) * 60000;
|
|
}
|
|
|
|
if (i === 0) {
|
|
msg.showTime = true;
|
|
lastShowTime = msg.timestamp;
|
|
} else {
|
|
const timeDiffMinutes = (msg.timestamp - lastShowTime) / (1000 * 60);
|
|
msg.showTime = timeDiffMinutes >= this.data.timeInterval;
|
|
if (msg.showTime) lastShowTime = msg.timestamp;
|
|
}
|
|
|
|
processedMessages.push(msg);
|
|
}
|
|
|
|
if (lastShowTime) {
|
|
this.setData({ lastShowTimeStamp: lastShowTime });
|
|
}
|
|
|
|
return processedMessages;
|
|
},
|
|
|
|
// 处理单条消息时间
|
|
processSingleMessageTime: function(message, messageList) {
|
|
const msg = { ...message };
|
|
|
|
if (!msg.timestamp || isNaN(msg.timestamp) || msg.timestamp <= 0) {
|
|
msg.timestamp = Date.now();
|
|
}
|
|
|
|
if (messageList.length === 0) {
|
|
msg.showTime = true;
|
|
this.setData({ lastShowTimeStamp: msg.timestamp });
|
|
return msg;
|
|
}
|
|
|
|
const lastShowTime = this.data.lastShowTimeStamp;
|
|
const timeDiffMinutes = (msg.timestamp - lastShowTime) / (1000 * 60);
|
|
|
|
msg.showTime = timeDiffMinutes >= this.data.timeInterval;
|
|
if (msg.showTime) {
|
|
this.setData({ lastShowTimeStamp: msg.timestamp });
|
|
}
|
|
|
|
return msg;
|
|
},
|
|
|
|
// ========== 工具方法 ==========
|
|
|
|
// 格式化时间
|
|
formatTime: function(timestamp) {
|
|
if (!timestamp || timestamp <= 0) return '未知时间';
|
|
|
|
const date = new Date(Number(timestamp));
|
|
if (isNaN(date.getTime())) return '未知时间';
|
|
|
|
const hours = date.getHours().toString().padStart(2, '0');
|
|
const minutes = date.getMinutes().toString().padStart(2, '0');
|
|
return `${hours}:${minutes}`;
|
|
},
|
|
|
|
// 格式化文件大小
|
|
formatFileSize: function(bytes) {
|
|
if (!bytes || bytes === 0) return '0B';
|
|
|
|
const units = ['B', 'KB', 'MB', 'GB'];
|
|
let size = bytes;
|
|
let unitIndex = 0;
|
|
|
|
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
size /= 1024;
|
|
unitIndex++;
|
|
}
|
|
|
|
return size.toFixed(1) + units[unitIndex];
|
|
},
|
|
|
|
// 阻止事件冒泡
|
|
stopPropagation: function() {}
|
|
});
|