index.vue 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. <template>
  2. <scroll-view
  3. class="scroll-view"
  4. scroll-y="true"
  5. :style="{'height': `${scrollHeight}px`,'margin-top':`${topbarOffsetHeight}px`}"
  6. v-if="scrollHeight>0"
  7. lower-threshold="200"
  8. :scroll-top="scrollTop"
  9. :scroll-into-view="scrollTo"
  10. scroll-with-animation="true"
  11. refresher-enabled="true"
  12. :refresher-triggered="scrollTriggered"
  13. :refresher-threshold="45"
  14. refresher-default-style="white"
  15. refresher-background="#151126"
  16. @refresherrefresh="scrollRefresh"
  17. @refresherpulling="scrollPulling"
  18. @refresherrestore="scrollRestore"
  19. @refresherabort="scrollAbort"
  20. @scrolltolower="scrollToBottom"
  21. >
  22. <view id="message-scroll" style="width:100%">
  23. <view class="no-message" v-if="isCompleted">没有更多啦</view>
  24. <view v-for="(item,index) in messageList" :key="index" class="t-message">
  25. <view v-if="conversation.type !== '@TIM#SYSTEM'" :id="item.ID">
  26. <view class="t-message-item">
  27. <TUI-TipMessage v-if="item.type === 'TIMGroupTipElem'" :message="item"></TUI-TipMessage>
  28. <view v-if="item.type !== 'TIMGroupTipElem'" :class="item.flow === 'out'||(item.flow === 'in'&&item.payload.data&&JSON.parse(item.payload.data).type===99) ? 't-self-message' : 't-recieve-message'">
  29. <image
  30. class="t-message-avatar"
  31. v-if="(item.flow === 'in'&&item.type !== 'TIMCustomElem')||(item.flow === 'in'&&(item.payload.data&&JSON.parse(item.payload.data).type!==99))"
  32. :src="item.avatar || 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/avatar_21.png'"
  33. @click="toDetail(item.from)"
  34. ></image>
  35. <view class="read-receipts" v-if="conversation.type === 'C2C' && item.flow === 'out'">
  36. <view v-if="item.isPeerRead">已读</view>
  37. <view v-else>未读</view>
  38. </view>
  39. <view>
  40. <TUI-TextMessage v-if="item.type === 'TIMTextElem'" :message="item" :isMine="item.flow === 'out'"></TUI-TextMessage>
  41. <TUI-ImageMessage v-if="item.type === 'TIMImageElem'" :message="item" :isMine="item.flow === 'out'"></TUI-ImageMessage>
  42. <TUI-VideoMessage v-if="item.type === 'TIMVideoFileElem'" :message="item" :isMine="item.flow === 'out'"></TUI-VideoMessage>
  43. <TUI-AudioMessage v-if="item.type === 'TIMSoundElem'" :message="item" :isMine="item.flow === 'out'"></TUI-AudioMessage>
  44. <TUI-CustomMessage v-if="item.type === 'TIMCustomElem'" :message="item" :isMine="item.flow === 'out'"></TUI-CustomMessage>
  45. <TUI-FaceMessage v-if="item.type === 'TIMFaceElem'" :message="item" :isMine="item.flow === 'out'"></TUI-FaceMessage>
  46. <TUI-FileMessage v-if="item.type === 'TIMFileElem'" :message="item" :isMine="item.flow === 'out'"></TUI-FileMessage>
  47. </view>
  48. <image
  49. class="t-message-avatar"
  50. v-if="item.flow === 'out'"
  51. :src="item.avatar || 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/avatar_21.png'"
  52. ></image>
  53. <!-- <image
  54. class="t-message-avatar"
  55. v-if="item.flow === 'in'&&(item.payload.data&&JSON.parse(item.payload.data).type!==99)"
  56. :src="mineUserInfo.icon || 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/avatar_21.png'"
  57. ></image> -->
  58. </view>
  59. </view>
  60. </view>
  61. <view v-else :id="item.ID" :data-value="item.ID"><TUI-SystemMessage :message="item"></TUI-SystemMessage></view>
  62. </view>
  63. </view>
  64. <view :id="endItem"></view>
  65. </scroll-view>
  66. </template>
  67. <script>
  68. import TUITextMessage from '../message-elements/text-message/index';
  69. import TUIImageMessage from '../message-elements/image-message/index';
  70. import TUIVideoMessage from '../message-elements/video-message/index';
  71. import TUIAudioMessage from '../message-elements/audio-message/index';
  72. import TUICustomMessage from '../message-elements/custom-message/index';
  73. import TUITipMessage from '../message-elements/tip-message/index';
  74. import TUISystemMessage from '../message-elements/system-message/index';
  75. import TUIFaceMessage from '../message-elements/face-message/index';
  76. import TUIFileMessage from '../message-elements/file-message/index';
  77. export default {
  78. data() {
  79. return {
  80. // 当前会话
  81. messageList: [],
  82. // 自己的 ID 用于区分历史消息中,哪部分是自己发出的
  83. scrollView: '',
  84. scrollTo: '',
  85. endItem:'',
  86. scrollTop:null,
  87. scrollRefreshing:false,
  88. scrollTriggered:true,
  89. nextReqMessageID: '',
  90. isSend:false,
  91. // 下一条消息标志
  92. isCompleted: false ,// 当前会话消息是否已经请求完毕
  93. };
  94. },
  95. components: {
  96. TUITextMessage,
  97. TUIImageMessage,
  98. TUIVideoMessage,
  99. TUIAudioMessage,
  100. TUICustomMessage,
  101. TUITipMessage,
  102. TUISystemMessage,
  103. TUIFaceMessage,
  104. TUIFileMessage
  105. },
  106. props: {
  107. conversation: {
  108. type: Object,
  109. default: () => {}
  110. },
  111. topbarOffsetHeight:{
  112. type:Number,
  113. default:0
  114. },
  115. scrollHeight:{
  116. type:Number,
  117. default:0
  118. },
  119. },
  120. watch: {
  121. conversation: {
  122. handler: function(newVal) {//发送消息不进行全部拉取
  123. if (newVal&&newVal.conversationID&&!this.isSend) {
  124. console.log(newVal.conversationID)
  125. this.conversation=newVal;
  126. this.getMessageList(newVal);
  127. };
  128. },
  129. immediate: true,
  130. deep: true
  131. }
  132. },
  133. computed:{
  134. mineUserInfo(){
  135. return JSON.parse(uni.getStorageSync('userInfo'));
  136. }
  137. },
  138. mounted() {
  139. uni.$TUIKit.on(uni.$TUIKitEvent.MESSAGE_RECEIVED, this.$onMessageReceived, this);
  140. uni.$TUIKit.on(uni.$TUIKitEvent.MESSAGE_READ_BY_PEER, this.$onMessageReadByPeer, this);
  141. this.$EventBus.$on('sendMessage', (message)=>{
  142. this.isSend=true;
  143. this.updateMessageList(message);
  144. })
  145. },
  146. destroyed() {
  147. // 一定要解除相关的事件绑定
  148. uni.$TUIKit.off(uni.$TUIKitEvent.MESSAGE_RECEIVED, this.$onMessageReceived);
  149. },
  150. methods: {
  151. /**
  152. * 推荐下拉刷新、加载更多
  153. */
  154. scrollPulling(e) {},
  155. scrollRestore() {this.scrollTriggered = true;},
  156. scrollAbort() {},
  157. scrollToBottom(){},
  158. scrollRefresh() {
  159. if (this.scrollRefreshing||this.isCompleted)
  160. {
  161. this.isSend=false;
  162. this.isCompleted=true;
  163. this.scrollTriggered=false;
  164. this.scrollRefreshing = false;
  165. return;
  166. }
  167. this.scrollRefreshing = true;
  168. this.getMessageList(this.conversation);
  169. setTimeout(() => {
  170. this.scrollTriggered = false;
  171. this.scrollRefreshing = false;
  172. }, 1000);
  173. },
  174. toDetail(id){
  175. console.log(id)
  176. uni.showLoading({})
  177. let user=JSON.parse(uni.getStorageSync('user'));
  178. this.$api.public.userDetail({getAlbum:true,completeUser:user,uponUserId:id}).then(res=>{
  179. if(res.data.frozen){
  180. uni.showToast({
  181. title:'该用户已被冻结',
  182. icon:'none'
  183. });
  184. return;
  185. }
  186. if(res.data.sex===user.sex){
  187. uni.showToast({
  188. title:'同性用户不能查看主页',
  189. icon:'none'
  190. })
  191. }
  192. else{
  193. uni.setStorageSync('otherInfo',JSON.stringify(res.data));
  194. uni.hideLoading();
  195. uni.navigateTo({
  196. url:`/pages/friends/user?id=${id}`
  197. })
  198. }
  199. })
  200. },
  201. getMessageList(conversation) {
  202. if (!this.isCompleted) {
  203. uni.$TUIKit
  204. .getMessageList({
  205. conversationID: conversation.conversationID,
  206. nextReqMessageID: this.nextReqMessageID,
  207. count: 15
  208. })
  209. .then(res => {
  210. const { messageList } = res.data; // 消息列表。
  211. console.log(res)
  212. this.nextReqMessageID = res.data.nextReqMessageID; // 用于续拉,分页续拉时需传入该字段。
  213. this.isCompleted = res.data.isCompleted; // 表示是否已经拉完所有消息。
  214. this.messageList = [...messageList, ...this.messageList];
  215. this.$handleMessageRender(this.messageList, messageList);
  216. });
  217. }
  218. },
  219. // 自己的消息上屏
  220. updateMessageList(msg) {
  221. this.messageList.push(msg);
  222. this.scrollToButtom();
  223. },
  224. // 消息已读更新
  225. $onMessageReadByPeer() {
  226. this.messageList=this.messageList
  227. },
  228. scrollToButtom() {
  229. const query = uni.createSelectorQuery().in(this);
  230. let nodesRef = query.select('#message-scroll');
  231. nodesRef
  232. .boundingClientRect(res => {
  233. if(res){
  234. this.$nextTick(() => {
  235. //进入页面滚动到底部
  236. this.scrollTop = res.height;
  237. // this.endItem='view_id_' + parseInt(Math.random() * 1000000)
  238. // this.scrollTo=this.endItem;
  239. console.log('滑动到底部了')
  240. });
  241. }
  242. })
  243. .exec();
  244. },
  245. // 收到的消息
  246. $onMessageReceived(value) {
  247. this.isSend=true;
  248. if(this.messageList.indexOf(value.data[0])===-1){
  249. this.messageList.push(value.data[0]);
  250. }
  251. this.scrollToButtom();
  252. },
  253. // 历史消息渲染
  254. $handleMessageRender(messageList) {
  255. if (messageList.length > 0) {
  256. this.messageList=messageList;
  257. setTimeout(()=>{
  258. this.$nextTick(() => {
  259. //进入页面滚动到底部
  260. this.scrollTop = 99999;
  261. // this.endItem='view_id_' + parseInt(Math.random() * 1000000)
  262. // this.scrollTo=this.endItem;
  263. console.log('历史消息渲染滑动到底部了')
  264. });
  265. },500)
  266. }
  267. }
  268. }
  269. };
  270. </script>
  271. <style>
  272. @import './index.css';
  273. </style>