与牧同行-兽医端小程序
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.

533 lines
13 KiB

  1. // 专家端聊天页面 - 纯模拟数据,无真实接口
  2. Page({
  3. data: {
  4. // 专家信息
  5. expertInfo: {
  6. id: 'expert_001',
  7. name: '张医生',
  8. avatar: '/pages/images/tx.png'
  9. },
  10. expertAvatar: '/pages/images/tx.png',
  11. // 对话信息
  12. conversation: {
  13. userId: 'user_001',
  14. userName: '李华',
  15. userAvatar: '/pages/images/tx.png'
  16. },
  17. // 在线状态
  18. onlineStatus: true,
  19. // 消息列表
  20. messageList: [],
  21. scrollTop: 0,
  22. scrollAnimate: true,
  23. // 输入相关
  24. inputValue: '',
  25. inputFocus: false,
  26. // 多媒体面板
  27. showMediaSheet: false,
  28. // 页面状态
  29. loadingMore: false,
  30. hasMore: true,
  31. page: 1,
  32. // 模拟数据
  33. mockMessages: [
  34. {
  35. id: 'msg_1',
  36. isMe: false,
  37. type: 'text',
  38. content: '医生您好,我想咨询一下皮肤问题',
  39. timestamp: Date.now() - 3600000,
  40. status: 'success',
  41. avatar: '/pages/images/tx.png'
  42. },
  43. {
  44. id: 'msg_2',
  45. isMe: true,
  46. type: 'text',
  47. content: '您好,请描述一下您的具体症状',
  48. timestamp: Date.now() - 3500000,
  49. status: 'success',
  50. avatar: '/pages/images/tx.png'
  51. },
  52. {
  53. id: 'msg_3',
  54. isMe: false,
  55. type: 'text',
  56. content: '脸上起了很多小红点,有点痒',
  57. timestamp: Date.now() - 3400000,
  58. status: 'success',
  59. avatar: '/pages/images/tx.png'
  60. },
  61. {
  62. id: 'msg_4',
  63. isMe: false,
  64. type: 'image',
  65. content: 'https://picsum.photos/200/200?random=1',
  66. timestamp: Date.now() - 3300000,
  67. status: 'success',
  68. avatar: '/pages/images/tx.png'
  69. },
  70. {
  71. id: 'msg_5',
  72. isMe: true,
  73. type: 'text',
  74. content: '看起来像是过敏反应,最近有接触什么新的东西吗?',
  75. timestamp: Date.now() - 3200000,
  76. status: 'success',
  77. avatar: '/pages/images/tx.png'
  78. }
  79. ],
  80. // 定时器
  81. typingTimer: null,
  82. mockUserTimer: null
  83. },
  84. onLoad: function(options) {
  85. console.log('专家端聊天页面加载', options);
  86. // 获取传递的参数
  87. if (options.userId) {
  88. this.setData({
  89. 'conversation.userId': options.userId,
  90. 'conversation.userName': options.userName || '用户'
  91. });
  92. }
  93. // 加载模拟消息
  94. this.loadMockMessages();
  95. // 模拟用户在线状态变化
  96. this.simulateOnlineStatus();
  97. // 模拟用户发送消息
  98. this.startMockUserTyping();
  99. },
  100. onShow: function() {
  101. // 滚动到底部
  102. setTimeout(() => {
  103. this.scrollToBottom(false);
  104. }, 200);
  105. },
  106. onUnload: function() {
  107. // 清理定时器
  108. if (this.data.mockUserTimer) {
  109. clearInterval(this.data.mockUserTimer);
  110. }
  111. if (this.data.typingTimer) {
  112. clearTimeout(this.data.typingTimer);
  113. }
  114. },
  115. // 返回上一页
  116. goBack: function() {
  117. wx.navigateBack({
  118. delta: 1
  119. });
  120. },
  121. // 加载模拟消息
  122. loadMockMessages: function() {
  123. // 处理消息时间显示
  124. const messages = this.processMessageTimes(this.data.mockMessages);
  125. this.setData({
  126. messageList: messages,
  127. hasMore: false
  128. });
  129. },
  130. // 加载更多消息(模拟)
  131. loadMoreMessages: function() {
  132. if (this.data.loadingMore || !this.data.hasMore) return;
  133. this.setData({ loadingMore: true });
  134. // 模拟网络延迟
  135. setTimeout(() => {
  136. // 模拟更早的消息
  137. const olderMessages = [
  138. {
  139. id: 'old_' + Date.now(),
  140. isMe: false,
  141. type: 'text',
  142. content: '之前也出现过类似情况',
  143. timestamp: Date.now() - 86400000,
  144. status: 'success',
  145. avatar: '/pages/images/tx.png'
  146. },
  147. {
  148. id: 'old_' + (Date.now() + 1),
  149. isMe: true,
  150. type: 'text',
  151. content: '那当时是怎么处理的呢?',
  152. timestamp: Date.now() - 86000000,
  153. status: 'success',
  154. avatar: '/pages/images/tx.png'
  155. }
  156. ];
  157. const processedOld = this.processMessageTimes(olderMessages);
  158. const newList = [...processedOld, ...this.data.messageList];
  159. this.setData({
  160. messageList: newList,
  161. loadingMore: false,
  162. hasMore: false // 模拟没有更多了
  163. });
  164. // 调整滚动位置
  165. setTimeout(() => {
  166. this.setData({
  167. scrollAnimate: false,
  168. scrollTop: 300
  169. }, () => {
  170. setTimeout(() => {
  171. this.setData({ scrollAnimate: true });
  172. }, 100);
  173. });
  174. }, 50);
  175. }, 800);
  176. },
  177. // 模拟用户在线状态变化
  178. simulateOnlineStatus: function() {
  179. setInterval(() => {
  180. // 随机改变在线状态(模拟)
  181. const random = Math.random();
  182. this.setData({
  183. onlineStatus: random > 0.3 // 70%时间在线
  184. });
  185. }, 30000);
  186. },
  187. // 模拟用户正在输入并发送消息
  188. startMockUserTyping: function() {
  189. // 每45-90秒模拟用户发送一条消息
  190. const timer = setInterval(() => {
  191. // 只有在线时才发送
  192. if (this.data.onlineStatus) {
  193. this.simulateUserMessage();
  194. }
  195. }, Math.random() * 45000 + 45000); // 45-90秒
  196. this.setData({ mockUserTimer: timer });
  197. },
  198. // 模拟用户发送消息
  199. simulateUserMessage: function() {
  200. const messages = [
  201. '好的,我明白了',
  202. '谢谢医生!',
  203. '需要用什么药吗?',
  204. '大概多久能好?',
  205. '有没有什么需要注意的?',
  206. '好的,我试试看',
  207. '明白了,非常感谢!'
  208. ];
  209. const randomMsg = messages[Math.floor(Math.random() * messages.length)];
  210. const newMsg = {
  211. id: 'user_' + Date.now() + Math.random(),
  212. isMe: false,
  213. type: 'text',
  214. content: randomMsg,
  215. timestamp: Date.now(),
  216. status: 'success',
  217. avatar: this.data.conversation.userAvatar || '/pages/images/tx.png'
  218. };
  219. this.addMessageToList(newMsg);
  220. // 震动提示(可选)
  221. wx.vibrateShort({ type: 'light' });
  222. },
  223. // 发送文本消息
  224. sendTextMessage: function() {
  225. const content = this.data.inputValue.trim();
  226. if (!content) return;
  227. const messageId = 'msg_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
  228. // 创建本地消息
  229. const newMessage = {
  230. id: messageId,
  231. isMe: true,
  232. type: 'text',
  233. content: content,
  234. timestamp: Date.now(),
  235. status: 'sending',
  236. avatar: this.data.expertAvatar
  237. };
  238. // 添加到列表
  239. this.addMessageToList(newMessage);
  240. // 清空输入框
  241. this.setData({ inputValue: '' });
  242. // 模拟发送延迟
  243. setTimeout(() => {
  244. this.updateMessageStatus(messageId, 'success');
  245. }, 500);
  246. },
  247. // 添加消息到列表
  248. addMessageToList: function(message) {
  249. const { messageList } = this.data;
  250. // 新消息添加到末尾
  251. messageList.push(message);
  252. // 重新处理时间显示
  253. const processedMessages = this.processMessageTimes(messageList);
  254. this.setData({
  255. messageList: processedMessages
  256. }, () => {
  257. this.scrollToBottom(true);
  258. });
  259. },
  260. // 更新消息状态
  261. updateMessageStatus: function(messageId, status) {
  262. const { messageList } = this.data;
  263. const index = messageList.findIndex(msg => msg.id === messageId);
  264. if (index !== -1) {
  265. messageList[index].status = status;
  266. this.setData({ messageList });
  267. }
  268. },
  269. // 重试发送
  270. retrySend: function(e) {
  271. const messageId = e.currentTarget.dataset.id;
  272. const { messageList } = this.data;
  273. const msg = messageList.find(m => m.id === messageId);
  274. if (msg) {
  275. msg.status = 'sending';
  276. this.setData({ messageList });
  277. setTimeout(() => {
  278. msg.status = 'success';
  279. this.setData({ messageList });
  280. }, 500);
  281. }
  282. },
  283. // 处理消息时间显示
  284. processMessageTimes: function(messages) {
  285. if (!messages || messages.length === 0) return [];
  286. const timeInterval = 5; // 5分钟间隔显示时间
  287. return messages.map((msg, index) => {
  288. const showTime = index === 0 ||
  289. (msg.timestamp - messages[index - 1].timestamp) > timeInterval * 60 * 1000;
  290. return {
  291. ...msg,
  292. showTime
  293. };
  294. });
  295. },
  296. // 格式化时间
  297. formatTime: function(timestamp) {
  298. if (!timestamp) return '';
  299. const date = new Date(Number(timestamp));
  300. const hours = date.getHours().toString().padStart(2, '0');
  301. const minutes = date.getMinutes().toString().padStart(2, '0');
  302. return `${hours}:${minutes}`;
  303. },
  304. // 格式化文件大小
  305. formatFileSize: function(bytes) {
  306. if (!bytes || bytes === 0) return '0B';
  307. const units = ['B', 'KB', 'MB', 'GB'];
  308. let size = bytes;
  309. let unitIndex = 0;
  310. while (size >= 1024 && unitIndex < units.length - 1) {
  311. size /= 1024;
  312. unitIndex++;
  313. }
  314. return size.toFixed(1) + units[unitIndex];
  315. },
  316. // 输入处理
  317. onInput: function(e) {
  318. this.setData({ inputValue: e.detail.value });
  319. },
  320. // 输入框获得焦点
  321. onInputFocus: function() {
  322. this.setData({ inputFocus: true }, () => {
  323. setTimeout(() => {
  324. this.scrollToBottom(true);
  325. }, 200);
  326. });
  327. },
  328. // 输入框失去焦点
  329. onInputBlur: function() {
  330. this.setData({ inputFocus: false });
  331. },
  332. // 清除输入
  333. clearInput: function() {
  334. this.setData({
  335. inputValue: '',
  336. inputFocus: true
  337. });
  338. },
  339. // 滚动事件
  340. onScroll: function(e) {
  341. const scrollTop = e.detail.scrollTop;
  342. this.setData({ lastScrollTop: scrollTop });
  343. // 滚动到顶部加载更多
  344. if (scrollTop <= 30 && !this.data.loadingMore && this.data.hasMore) {
  345. this.loadMoreMessages();
  346. }
  347. },
  348. // 滚动到底部
  349. scrollToBottom: function(animate = true) {
  350. this.setData({
  351. scrollAnimate: animate
  352. }, () => {
  353. setTimeout(() => {
  354. this.setData({ scrollTop: 999999 });
  355. }, 50);
  356. });
  357. },
  358. // 显示多媒体选择面板
  359. showMediaActionSheet: function() {
  360. this.setData({
  361. showMediaSheet: true,
  362. inputFocus: false
  363. });
  364. },
  365. // 隐藏多媒体选择面板
  366. hideMediaActionSheet: function() {
  367. this.setData({ showMediaSheet: false });
  368. },
  369. // 选择图片(模拟)
  370. chooseImage: function() {
  371. this.hideMediaActionSheet();
  372. // 模拟选择图片
  373. const mockImages = [
  374. 'https://picsum.photos/200/200?random=2',
  375. 'https://picsum.photos/200/200?random=3',
  376. 'https://picsum.photos/200/200?random=4'
  377. ];
  378. const randomImage = mockImages[Math.floor(Math.random() * mockImages.length)];
  379. const messageId = 'img_' + Date.now();
  380. const newMessage = {
  381. id: messageId,
  382. isMe: true,
  383. type: 'image',
  384. content: randomImage,
  385. timestamp: Date.now(),
  386. status: 'uploading',
  387. progress: 0,
  388. avatar: this.data.expertAvatar
  389. };
  390. this.addMessageToList(newMessage);
  391. // 模拟上传进度
  392. let progress = 0;
  393. const interval = setInterval(() => {
  394. progress += 20;
  395. if (progress >= 100) {
  396. clearInterval(interval);
  397. this.updateMessageStatus(messageId, 'success');
  398. } else {
  399. const msgIndex = this.data.messageList.findIndex(m => m.id === messageId);
  400. if (msgIndex !== -1) {
  401. this.data.messageList[msgIndex].progress = progress;
  402. this.setData({ messageList: this.data.messageList });
  403. }
  404. }
  405. }, 200);
  406. },
  407. // 选择视频(模拟)
  408. chooseVideo: function() {
  409. this.hideMediaActionSheet();
  410. const messageId = 'video_' + Date.now();
  411. const newMessage = {
  412. id: messageId,
  413. isMe: true,
  414. type: 'video',
  415. content: 'https://example.com/video.mp4',
  416. thumb: 'https://picsum.photos/200/200?random=5',
  417. timestamp: Date.now(),
  418. status: 'uploading',
  419. progress: 0,
  420. avatar: this.data.expertAvatar
  421. };
  422. this.addMessageToList(newMessage);
  423. // 模拟上传进度
  424. let progress = 0;
  425. const interval = setInterval(() => {
  426. progress += 25;
  427. if (progress >= 100) {
  428. clearInterval(interval);
  429. this.updateMessageStatus(messageId, 'success');
  430. } else {
  431. const msgIndex = this.data.messageList.findIndex(m => m.id === messageId);
  432. if (msgIndex !== -1) {
  433. this.data.messageList[msgIndex].progress = progress;
  434. this.setData({ messageList: this.data.messageList });
  435. }
  436. }
  437. }, 300);
  438. },
  439. // 预览图片
  440. previewImage: function(e) {
  441. const url = e.currentTarget.dataset.url;
  442. wx.previewImage({
  443. current: url,
  444. urls: [url]
  445. });
  446. },
  447. // 阻止事件冒泡
  448. stopPropagation: function() {}
  449. });