index.jsx 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. import { useEffect, useState } from 'react'
  2. import Taro from '@tarojs/taro'
  3. import { ScrollView, Input, Image, Button } from '@tarojs/components'
  4. import withLayout from '@/layout'
  5. import { API_QUERY_USERINFO_BYID } from '@/constants/api'
  6. import { ROLE_CODE } from '@/constants/user'
  7. import { uploadFiles, fetch } from '@/utils/request'
  8. import { queryChatHistory, setMessageReaded } from '@/services/chat'
  9. import im from '@/utils/im'
  10. import { getDateForHumans } from '@/utils/chatDate'
  11. import '@/assets/css/iconfont.css'
  12. import Card from './Card'
  13. import useScrollTop from './useScrollTop'
  14. import './index.scss'
  15. export default withLayout((props) => {
  16. const { router, person } = props;
  17. const { friend } = router.params
  18. const [submitting, setSubmitting] = useState(false)
  19. const [text, setText] = useState()
  20. const [receiver, setReceiver] = useState({})
  21. const [PageList, setPageList] = useState([])
  22. const [scrollTop, scroll] = useScrollTop('#chat-scroll')
  23. const sendMessage = (msg, type = im.MESSAGETYPE.TEXT) => {
  24. const message = msg
  25. const messageType = type
  26. // 用来回调写入消息列表
  27. const newMessage = {
  28. chatId: Math.random().toString(36).substring(2),
  29. message,
  30. messageType,
  31. receivePerson: friend,
  32. sendPerson: person.personId,
  33. createDate: new Date(),
  34. }
  35. // 发送消息
  36. setSubmitting(true)
  37. im.sendMessage(message, messageType).then(() => {
  38. // 发送成功
  39. setText()
  40. // 立刻回显刚刚发送内容
  41. setPageList(PageList.concat(newMessage))
  42. // 滚动到底部
  43. scroll()
  44. //
  45. setSubmitting(false)
  46. }).catch(() => {
  47. setSubmitting(false)
  48. Taro.showToast({
  49. title: '发送失败, 请重试',
  50. icon: 'none',
  51. })
  52. })
  53. }
  54. const submitText = () => {
  55. if (text && text.trim()) {
  56. sendMessage(text.trim())
  57. }
  58. }
  59. const submitImage = () => {
  60. Taro.chooseImage({
  61. count: 1,
  62. sizeType: ['original', 'compressed'],
  63. sourceType: ['album', 'camera'],
  64. success: res => {
  65. uploadFiles(res.tempFilePaths).then(data => {
  66. sendMessage(data[0], im.MESSAGETYPE.IMAGE)
  67. })
  68. }
  69. })
  70. }
  71. const getChatHistory = (params) => {
  72. Taro.showLoading()
  73. queryChatHistory({
  74. chatWith: friend,
  75. pageSize: 20,
  76. ...params || {},
  77. }).then((res) => {
  78. const { records } = res;
  79. Taro.hideLoading()
  80. const lst = (records || []).reverse()
  81. // 如果聊天对象是有身份的
  82. if (receiver.personType !== ROLE_CODE.DRIFT && receiver.personType !== ROLE_CODE.CUSTOMER) {
  83. const mockMessage = {
  84. chatId: Math.random().toString(36).substring(2),
  85. message: '',
  86. messageType: im.MESSAGETYPE.CARD,
  87. receivePerson: person.personId,
  88. sendPerson: friend,
  89. createDate: new Date(),
  90. }
  91. lst.push(mockMessage)
  92. }
  93. setPageList(lst)
  94. scroll()
  95. })
  96. }
  97. // 留电话
  98. // 实际上是发送一条文本记录
  99. const leavePhone = () => {
  100. sendMessage(person.phone)
  101. }
  102. useEffect(() => {
  103. if (friend) {
  104. im.bindReceiver(friend)
  105. fetch({ url: `${API_QUERY_USERINFO_BYID}/${friend}`, spin: true }).then((res) => {
  106. setReceiver(res)
  107. getChatHistory({ pageNumber: 1 })
  108. })
  109. } else {
  110. Taro.showToast({
  111. title: '无聊天对象,请退出重试',
  112. icon: 'none',
  113. })
  114. }
  115. // eslint-disable-next-line react-hooks/exhaustive-deps
  116. }, [friend])
  117. useEffect(() => {
  118. return im.listen((message) => {
  119. const receiveData = JSON.parse(message)
  120. setPageList(PageList.concat(receiveData))
  121. // 滚动到底部
  122. scroll()
  123. // 更新记录为已读
  124. setMessageReaded(receiveData.chatId)
  125. })
  126. }, [PageList, scroll])
  127. const CheckBigImg = (img) => {
  128. return () => {
  129. Taro.previewImage({ current: img, urls: [img] })
  130. }
  131. }
  132. return (
  133. <view className='chatDetail flex-v'>
  134. <view className='flex-item'>
  135. <view>
  136. <ScrollView id='chat-scroll' scrollY enhanced scrollTop={scrollTop}>
  137. <view className='PageContent'>
  138. {
  139. PageList.map((item) => (
  140. <view key={item.chatId} className='ChatItem'>
  141. <view className='Time'>
  142. <text>{getDateForHumans(item.createDate, true)}</text>
  143. </view>
  144. <view className='flex-h'>
  145. {
  146. item.sendPerson === friend ? (
  147. <>
  148. <view className='Icon'>
  149. <Image mode='scaleToFill' src={receiver.avatarurl}></Image>
  150. </view>
  151. <view className='flex-item'>
  152. { /* 如果是文本 */
  153. item.messageType === im.MESSAGETYPE.TEXT && (
  154. <view className='Message Left'>
  155. <text>{item.message}</text>
  156. </view>
  157. )
  158. }
  159. { /* 图片消息 */
  160. item.messageType === im.MESSAGETYPE.IMAGE &&
  161. <view className='Message Left Img'>
  162. <view>
  163. <Image onClick={CheckBigImg(item.message)} mode='scaleToFill' src={item.message}></Image>
  164. </view>
  165. </view>
  166. }
  167. { /* 如果是卡片 */
  168. item.messageType === im.MESSAGETYPE.CARD && (
  169. <Card person={receiver} onLeavePhone={leavePhone} />
  170. )
  171. }
  172. </view>
  173. </>
  174. ) : (
  175. <>
  176. <view className='flex-item'>
  177. { /* 如果是文本 */
  178. item.messageType === im.MESSAGETYPE.TEXT && (
  179. <view className='Message Right'>
  180. <text>{item.message}</text>
  181. </view>
  182. )
  183. }
  184. { /* 图片消息 */
  185. item.messageType === im.MESSAGETYPE.IMAGE &&
  186. <view className='Message Right Img'>
  187. <view>
  188. <Image onClick={CheckBigImg(item.message)} mode='scaleToFill' src={item.message}></Image>
  189. </view>
  190. </view>
  191. }
  192. </view>
  193. <view className='Icon'>
  194. <Image mode='scaleToFill' src={person.avatarurl}></Image>
  195. </view>
  196. </>
  197. )
  198. }
  199. {/* 系统模板消息(对方消息) */}
  200. {/* {
  201. index === 0 &&
  202. } */}
  203. </view>
  204. </view>
  205. ))
  206. }
  207. </view>
  208. </ScrollView>
  209. </view>
  210. </view>
  211. <view className='SendContent flex-h'>
  212. <view className='flex-item'>
  213. <Input placeholder='发送消息' value={text} onInput={(e) => setText(e.detail.value)} />
  214. </view>
  215. <text className='iconfont icon-tianjia' onClick={submitImage}></text>
  216. <Button loading={submitting} className='Send' onClick={submitText}>发送</Button>
  217. </view>
  218. </view>
  219. )
  220. })