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

371 lines
14 KiB

  1. <!-- pages/consult/consult.wxml -->
  2. <view class="consult-page">
  3. <!-- 头部专家信息 -->
  4. <view class="consult-header">
  5. <view class="header-content">
  6. <view class="header-left">
  7. <button class="back-btn" bindtap="goBack">
  8. <image src="/images/icons/back.png" class="back-icon"></image>
  9. </button>
  10. </view>
  11. <view class="header-center">
  12. <view class="expert-info">
  13. <text class="expert-name">{{expertInfo.name}}</text>
  14. <view class="expert-status">
  15. <view class="status-dot {{expertInfo.online ? 'online' : 'offline'}}"></view>
  16. <text class="status-text">{{expertInfo.online ? '在线' : '离线'}}</text>
  17. </view>
  18. </view>
  19. </view>
  20. <view class="header-right">
  21. <button class="header-action-btn" bindtap="makePhoneCall">
  22. <image src="/images/icons/phone.png" class="header-action-icon"></image>
  23. </button>
  24. </view>
  25. </view>
  26. </view>
  27. <!-- 聊天内容区域 -->
  28. <scroll-view
  29. id="chatScroll"
  30. class="chat-container"
  31. scroll-y
  32. scroll-top="{{scrollTop}}"
  33. scroll-with-animation="{{scrollAnimate}}"
  34. enable-back-to-top="true"
  35. show-scrollbar="{{false}}"
  36. bindscroll="onScroll"
  37. >
  38. <!-- 日期分隔线 -->
  39. <view class="date-divider" wx:if="{{showDateDivider}}">
  40. <text class="date-text">{{todayDate}}</text>
  41. </view>
  42. <!-- 消息列表 -->
  43. <block wx:for="{{messageList}}" wx:key="id">
  44. <!-- 微信样式时间分隔 -->
  45. <view class="time-divider" wx:if="{{item.showTime}}">
  46. <view class="time-line"></view>
  47. <text class="time-text">{{formatTime(item.timestamp)}}</text>
  48. <view class="time-line"></view>
  49. </view>
  50. <!-- 对方消息 -->
  51. <view class="message-item message-left" wx:if="{{item.sender === 'expert'}}">
  52. <view class="message-avatar">
  53. <image src="{{expertInfo.avatar}}" class="avatar-img"></image>
  54. </view>
  55. <view class="message-content-wrapper">
  56. <!-- 文本消息 -->
  57. <view class="message-bubble bubble-left" wx:if="{{item.type === 'text'}}">
  58. <text class="message-text">{{item.content}}</text>
  59. <view class="message-arrow arrow-left"></view>
  60. </view>
  61. <!-- 图片消息 -->
  62. <view class="media-bubble" wx:elif="{{item.type === 'image'}}">
  63. <image
  64. src="{{item.content}}"
  65. class="message-image"
  66. mode="aspectFill"
  67. bindtap="previewImage"
  68. data-url="{{item.content}}"
  69. ></image>
  70. <view class="message-arrow arrow-left"></view>
  71. </view>
  72. <!-- 视频消息 -->
  73. <view class="media-bubble" wx:elif="{{item.type === 'video'}}">
  74. <video
  75. src="{{item.content}}"
  76. class="message-video"
  77. controls
  78. show-center-play-btn
  79. poster="{{item.thumb}}"
  80. ></video>
  81. <view class="video-play-overlay">
  82. <image src="/images/icons/play.png" class="play-icon"></image>
  83. </view>
  84. <view class="message-arrow arrow-left"></view>
  85. </view>
  86. <!-- 语音消息 -->
  87. <view class="message-bubble bubble-left message-audio" wx:elif="{{item.type === 'audio'}}">
  88. <image src="/images/icons/voice_left.png" class="audio-icon-left"></image>
  89. <view class="audio-content">
  90. <view class="audio-wave">
  91. <view class="wave-bar" style="animation-delay: 0s;"></view>
  92. <view class="wave-bar" style="animation-delay: 0.2s;"></view>
  93. <view class="wave-bar" style="animation-delay: 0.4s;"></view>
  94. </view>
  95. <text class="audio-duration">{{item.duration || 0}}''</text>
  96. </view>
  97. <view class="message-arrow arrow-left"></view>
  98. </view>
  99. <!-- 文件消息 -->
  100. <view class="message-bubble bubble-left message-file" wx:elif="{{item.type === 'file'}}">
  101. <view class="file-icon-box">
  102. <image src="/images/icons/file_icon.png" class="file-icon"></image>
  103. </view>
  104. <view class="file-info">
  105. <text class="file-name">{{item.fileName}}</text>
  106. <text class="file-size">{{formatFileSize(item.fileSize)}}</text>
  107. </view>
  108. <view class="message-arrow arrow-left"></view>
  109. </view>
  110. </view>
  111. </view>
  112. <!-- 我的消息 -->
  113. <view class="message-item message-right" wx:else>
  114. <view class="message-content-wrapper">
  115. <!-- 文本消息 -->
  116. <view class="message-bubble bubble-right" wx:if="{{item.type === 'text'}}">
  117. <text class="message-text">{{item.content}}</text>
  118. <view class="message-arrow arrow-right"></view>
  119. </view>
  120. <!-- 图片消息 -->
  121. <view class="media-bubble" wx:elif="{{item.type === 'image'}}">
  122. <image
  123. src="{{item.content}}"
  124. class="message-image"
  125. mode="aspectFill"
  126. bindtap="previewImage"
  127. data-url="{{item.content}}"
  128. ></image>
  129. <view class="upload-progress" wx:if="{{item.status === 'uploading'}}">
  130. <view class="progress-circle">
  131. <text class="progress-text">{{item.progress}}%</text>
  132. </view>
  133. </view>
  134. <view class="message-arrow arrow-right"></view>
  135. </view>
  136. <!-- 视频消息 -->
  137. <view class="media-bubble" wx:elif="{{item.type === 'video'}}">
  138. <video
  139. src="{{item.content}}"
  140. class="message-video"
  141. controls
  142. show-center-play-btn
  143. poster="{{item.thumb}}"
  144. ></video>
  145. <view class="video-play-overlay">
  146. <image src="/images/icons/play.png" class="play-icon"></image>
  147. </view>
  148. <view class="upload-progress" wx:if="{{item.status === 'uploading'}}">
  149. <view class="progress-circle">
  150. <text class="progress-text">{{item.progress}}%</text>
  151. </view>
  152. </view>
  153. <view class="message-arrow arrow-right"></view>
  154. </view>
  155. <!-- 语音消息 -->
  156. <view class="message-bubble bubble-right message-audio" wx:elif="{{item.type === 'audio'}}">
  157. <view class="audio-content">
  158. <view class="audio-wave">
  159. <view class="wave-bar" style="animation-delay: 0s;"></view>
  160. <view class="wave-bar" style="animation-delay: 0.2s;"></view>
  161. <view class="wave-bar" style="animation-delay: 0.4s;"></view>
  162. </view>
  163. <text class="audio-duration">{{item.duration || 0}}''</text>
  164. </view>
  165. <image src="/images/icons/voice_right.png" class="audio-icon-right"></image>
  166. <view class="message-arrow arrow-right"></view>
  167. </view>
  168. <!-- 文件消息 -->
  169. <view class="message-bubble bubble-right message-file" wx:elif="{{item.type === 'file'}}">
  170. <view class="file-icon-box">
  171. <image src="/images/icons/file_icon.png" class="file-icon"></image>
  172. </view>
  173. <view class="file-info">
  174. <text class="file-name">{{item.fileName}}</text>
  175. <text class="file-size">{{formatFileSize(item.fileSize)}}</text>
  176. </view>
  177. <view class="upload-progress" wx:if="{{item.status === 'uploading'}}">
  178. <view class="progress-circle">
  179. <text class="progress-text">{{item.progress}}%</text>
  180. </view>
  181. </view>
  182. <view class="message-arrow arrow-right"></view>
  183. </view>
  184. </view>
  185. <view class="message-avatar">
  186. <image src="{{userInfo.avatar}}" class="avatar-img"></image>
  187. </view>
  188. </view>
  189. </block>
  190. <!-- 加载更多提示 -->
  191. <view class="load-more-tip" wx:if="{{loadingMore}}">
  192. <text>加载中...</text>
  193. </view>
  194. <!-- 没有消息提示 -->
  195. <view class="empty-tip" wx:if="{{messageList.length === 0 && !loading}}">
  196. <image src="/images/icons/empty_chat.png" class="empty-icon"></image>
  197. <text class="empty-text">暂无聊天记录,开始咨询吧</text>
  198. </view>
  199. </scroll-view>
  200. <!-- 输入区域 -->
  201. <view class="input-section">
  202. <!-- 语音输入模式 -->
  203. <view class="voice-input-panel" wx:if="{{inputMode === 'voice'}}">
  204. <!-- 切换到文字输入按钮 -->
  205. <button class="voice-input-btn" bindtap="switchInputMode">
  206. <image src="/images/icons/keyboard.png" class="voice-btn-icon"></image>
  207. </button>
  208. <!-- 语音输入按钮 -->
  209. <view class="input-wrapper">
  210. <button
  211. class="voice-record-btn"
  212. bindtouchstart="startVoiceRecord"
  213. bindtouchmove="onVoiceTouchMove"
  214. bindtouchend="endVoiceRecord"
  215. bindtouchcancel="cancelVoiceRecord"
  216. >
  217. <image src="/images/icons/microphone.png" class="mic-icon"></image>
  218. <text class="voice-tip">{{voiceTip}}</text>
  219. </button>
  220. </view>
  221. <!-- 更多按钮 -->
  222. <button class="more-btn" bindtap="showMediaActionSheet">
  223. <image src="/images/icons/plus.png" class="more-icon"></image>
  224. </button>
  225. </view>
  226. <!-- 文字输入模式 -->
  227. <view class="text-input-panel" wx:else>
  228. <!-- 切换到语音输入按钮 -->
  229. <button class="voice-input-btn" bindtap="switchInputMode">
  230. <image src="/images/icons/voice.png" class="voice-btn-icon"></image>
  231. </button>
  232. <!-- 文字输入框 -->
  233. <view class="input-wrapper">
  234. <input
  235. type="text"
  236. class="chat-input"
  237. placeholder="{{inputPlaceholder}}"
  238. placeholder-class="input-placeholder"
  239. value="{{inputValue}}"
  240. bindinput="onInput"
  241. bindconfirm="sendTextMessage"
  242. confirm-type="send"
  243. focus="{{inputFocus}}"
  244. adjust-position="{{false}}"
  245. cursor-spacing="20"
  246. bindfocus="onInputFocus"
  247. bindblur="onInputBlur"
  248. />
  249. <!-- 输入框操作按钮 -->
  250. <view class="input-actions" wx:if="{{inputValue.trim()}}">
  251. <button class="clear-btn" bindtap="clearInput">
  252. <image src="/images/icons/clear.png" class="clear-icon"></image>
  253. </button>
  254. </view>
  255. </view>
  256. <!-- 发送/更多按钮 -->
  257. <button
  258. class="send-btn"
  259. bindtap="sendTextMessage"
  260. wx:if="{{inputValue.trim()}}"
  261. >
  262. <text class="send-text">发送</text>
  263. </button>
  264. <button class="more-btn" bindtap="showMediaActionSheet" wx:else>
  265. <image src="/images/icons/plus.png" class="more-icon"></image>
  266. </button>
  267. </view>
  268. </view>
  269. <!-- 多媒体选择面板 -->
  270. <view class="media-action-sheet" wx:if="{{showMediaSheet}}" catchtap="hideMediaActionSheet">
  271. <view class="media-sheet-content" catchtap="stopPropagation">
  272. <view class="media-sheet-header">
  273. <text class="sheet-title">发送内容</text>
  274. <button class="close-sheet-btn" bindtap="hideMediaActionSheet">
  275. <image src="/images/icons/close_round.png"></image>
  276. </button>
  277. </view>
  278. <view class="media-options-grid">
  279. <button class="media-option" bindtap="chooseImage">
  280. <view class="option-icon-box photo-icon">
  281. <image src="/images/icons/photo.png"></image>
  282. </view>
  283. <text class="option-text">照片</text>
  284. </button>
  285. <button class="media-option" bindtap="takePhoto">
  286. <view class="option-icon-box camera-icon">
  287. <image src="/images/icons/camera.png"></image>
  288. </view>
  289. <text class="option-text">拍摄</text>
  290. </button>
  291. <button class="media-option" bindtap="chooseVideo">
  292. <view class="option-icon-box video-icon">
  293. <image src="/images/icons/video.png"></image>
  294. </view>
  295. <text class="option-text">视频</text>
  296. </button>
  297. <button class="media-option" bindtap="recordAudio">
  298. <view class="option-icon-box audio-icon">
  299. <image src="/images/icons/audio_msg.png"></image>
  300. </view>
  301. <text class="option-text">语音</text>
  302. </button>
  303. <button class="media-option" bindtap="chooseFile">
  304. <view class="option-icon-box file-icon">
  305. <image src="/images/icons/file.png"></image>
  306. </view>
  307. <text class="option-text">文件</text>
  308. </button>
  309. </view>
  310. <view class="sheet-bottom">
  311. <text class="bottom-tip">最多可选择9张照片</text>
  312. </view>
  313. </view>
  314. </view>
  315. <!-- 录音提示 -->
  316. <view class="recording-modal" wx:if="{{isRecording}}">
  317. <view class="recording-box {{isCanceling ? 'is-canceling' : ''}}">
  318. <view class="recording-icon">
  319. <image
  320. src="{{isCanceling ? '/images/icons/cancel_recording.png' : '/images/icons/recording.png'}}"
  321. class="recording-mic-icon"
  322. ></image>
  323. </view>
  324. <text class="recording-tip">{{recordingTip}}</text>
  325. <view class="recording-meter">
  326. <view class="recording-waves">
  327. <view
  328. class="recording-wave"
  329. wx:for="{{[1,2,3,4,5]}}"
  330. wx:key="index"
  331. style="animation-delay: {{index * 0.1}}s;"
  332. ></view>
  333. </view>
  334. <text class="recording-time">{{recordingTime}}s</text>
  335. </view>
  336. </view>
  337. </view>
  338. </view>