chatRoom.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. <template>
  2. <div class="c-view">
  3. <div class="box">
  4. <div class="box-hd">
  5. <div class="title-wrap">
  6. {{isPrivate ? group.privateName : group.groupName}}
  7. <i class="el-icon-more" v-if="!isPrivate" @click="groupSet = !groupSet"></i>
  8. </div>
  9. </div>
  10. <chat-pin v-bind="pinMsg" @pinMsgClose="pinMsgClose" @scrollToView="scrollToView"></chat-pin>
  11. <div class="box-bd" :style="{height:scrollHeight - (pinMsg.visible ? 30 : 0) + 'px'} "
  12. v-bar="{
  13. resizeDebounce: 500
  14. }">
  15. <div ref="scrollWrap" class="bar" @scroll="handleScroll">
  16. <div ref="msgWrap" class="scroll-wrapper">
  17. <div class="msg-wrap">
  18. <div class="msg-top-more" v-if="lockEnd">
  19. <em>没有更多了</em>
  20. </div>
  21. <div class="msg-top-load" v-if="lockMore && !lockEnd">
  22. <i class="msg-loading-icon"></i>
  23. </div>
  24. <template v-if="group.chatList.length">
  25. <msg-item v-for="(item ,key) in group.pinList"
  26. :key="'pin' + key"
  27. v-bind="item"
  28. @quoteMsg="quoteMsg"
  29. @deleteMsg="deleteMsg"
  30. >
  31. </msg-item>
  32. </template>
  33. <msg-item v-for="(item, key) in group.chatList"
  34. :key="key"
  35. :isPrivate ="isPrivate"
  36. v-bind="item"
  37. @quoteMsg="quoteMsg"
  38. @deleteMsg="deleteMsg"
  39. >
  40. </msg-item>
  41. </div>
  42. </div>
  43. </div>
  44. <div class="msg-unread"
  45. @click="doSetRead"
  46. v-if="group.unreadNums > 0 && enableScroll">
  47. <em><i class="el-icon-d-arrow-right"></i>{{group.unreadNums}}条未读消息</em>
  48. </div>
  49. <at-me :atList="atList" @scrollToMsg="scrollToMsg">
  50. </at-me>
  51. </div>
  52. <input-area ref="inputArea" @toBottom="resizeToBottom"></input-area>
  53. </div>
  54. <chat-set
  55. v-if="group.members && !isPrivate"
  56. :class="{'move-left': groupSet}">
  57. </chat-set>
  58. </div>
  59. </template>
  60. <script>
  61. import Vue from 'vue'
  62. import msgItem from '@/components/msgItem'
  63. import chatPin from '@/components/chatPin'
  64. import atMe from '@/components/chatAt/atme'
  65. import { mapActions, mapState, mapMutations } from 'vuex'
  66. import chatSet from '@/components/chatSet'
  67. import inputArea from './inputArea'
  68. import { getResizeHeight } from '@/util/util.js'
  69. import { Button } from 'element-ui'
  70. import scrollIntoView from 'scroll-into-view-if-needed'
  71. Vue.component(Button.name, Button)
  72. export default {
  73. name: 'chatRoom',
  74. components: {
  75. msgItem,
  76. inputArea,
  77. chatSet,
  78. chatPin,
  79. atMe
  80. },
  81. props: {
  82. sessionId: [String, Number]
  83. },
  84. watch: {
  85. sessionId (val) {
  86. // 切换房间
  87. this.groupSet = false
  88. this.lockMore = false
  89. this.lockEnd = false
  90. this.enableScroll = false
  91. this.initRoom()
  92. }
  93. },
  94. data () {
  95. return {
  96. groupSet: false,
  97. lockMore: false,
  98. lockEnd: false,
  99. enableScroll: false, // 记录滚动条是否激活的状态
  100. scrollHeight: 100, // 滚动条高度
  101. isScrollToView: false
  102. }
  103. },
  104. computed: {
  105. ...mapState([
  106. 'group',
  107. 'userId'
  108. ]),
  109. ...mapState({
  110. pinMsg: state => state.group.pinMsg,
  111. pinList: state => state.group.pinList,
  112. atList: state => state.group.atList
  113. }),
  114. isPrivate () {
  115. return /-/g.test(this.sessionId)
  116. }
  117. },
  118. mounted () {
  119. this.scrollHeight = getResizeHeight()
  120. window.onresize = () => {
  121. this.scrollHeight = getResizeHeight()
  122. }
  123. this.initRoom()
  124. document.addEventListener('contextmenu', e => e.preventDefault())
  125. },
  126. methods: {
  127. ...mapMutations([
  128. 'initGroup',
  129. 'resetUnreadNums',
  130. 'addChatItem',
  131. 'deleteChatItem',
  132. 'initState'
  133. ]),
  134. ...mapActions([
  135. 'getGroupInfo',
  136. 'getNewMsg',
  137. 'getHistoryMsg',
  138. 'doSendMsg',
  139. 'getPrivateNewMsg',
  140. 'getPrivateHistoryMsg',
  141. 'doSendPrivateMsg'
  142. ]),
  143. initRoom () {
  144. this.initState(this.userId)
  145. if (this.isPrivate) {
  146. this.initPersonChat()
  147. } else {
  148. this.initGroupChat()
  149. }
  150. },
  151. /**
  152. * @des 私聊初始化处理
  153. */
  154. async initPersonChat () {
  155. await this.getPrivateNewMsg()
  156. this.$nextTick(this.resizeToBottom)
  157. },
  158. /**
  159. * @des 聊天群初始化处理
  160. */
  161. async initGroupChat () {
  162. this.initGroup({
  163. userId: this.userId,
  164. groupId: this.sessionId,
  165. useCache: false
  166. })
  167. await this.getGroupInfo()
  168. await this.getNewMsg()
  169. this.$nextTick(this.resizeToBottom)
  170. },
  171. /**
  172. * @des 滚动事件监听
  173. */
  174. initScrollEvent () {
  175. },
  176. /**
  177. * @des 聊天窗体滚动事件处理集
  178. */
  179. async handleScroll (e) {
  180. // 防止切换房间时触发滚动处理
  181. if (!this.group.chatList.length) {
  182. return
  183. }
  184. // 防止滚动到置顶消息触发滚动
  185. if (this.isScrollToView) {
  186. return
  187. }
  188. // 激活滚动条
  189. this.enableScroll = true
  190. let totalHeight = this.$refs.msgWrap.offsetHeight
  191. let scrollTop = e.target.scrollTop
  192. // 差不多滚动到顶部
  193. if (scrollTop === 0 && !this.lockMore) {
  194. console.log(scrollTop)
  195. if (this.group.endHash !== null) {
  196. this.lockMore = true
  197. let res
  198. if (this.isPrivate) {
  199. res = await this.getPrivateHistoryMsg()
  200. } else {
  201. res = await this.getHistoryMsg()
  202. }
  203. if (res === 'end') {
  204. this.lockEnd = true
  205. } else {
  206. let scrollBottom = totalHeight - scrollTop
  207. this.$nextTick(() => {
  208. e.target.scrollTop = this.$refs.msgWrap.offsetHeight - scrollBottom
  209. setTimeout(() => {
  210. this.lockMore = false
  211. }, 800)
  212. })
  213. }
  214. }
  215. }
  216. // 滚动到底部清空未读消息状态
  217. if (scrollTop + e.target.offsetHeight > totalHeight) {
  218. if (this.group.unreadNums) {
  219. this.resetUnreadNums()
  220. }
  221. }
  222. },
  223. /**
  224. * @des 聊天窗体滚动到底部
  225. */
  226. resizeToBottom () {
  227. this.$refs.scrollWrap.scrollTop = this.$refs.msgWrap.offsetHeight
  228. },
  229. /**
  230. * @des 点击,查看未读消息
  231. * 直接滚动到聊天列表底部
  232. */
  233. doSetRead () {
  234. this.resizeToBottom()
  235. this.resetUnreadNums()
  236. },
  237. /**
  238. * @des 引用某条消息
  239. */
  240. quoteMsg (msg) {
  241. this.$refs.inputArea.inputMsg = msg
  242. },
  243. /**
  244. * @des 某条消息被删除
  245. */
  246. deleteMsg (hash) {
  247. this.deleteChatItem(hash)
  248. },
  249. pinMsgClose () {
  250. this.pinMsg.visible = false
  251. },
  252. scrollToView () {
  253. if (this.pinList.length) {
  254. let node = this.$refs.msgWrap.querySelector('.msg-item')
  255. scrollIntoView(node, { behavior: 'smooth', scrollMode: 'if-needed' })
  256. } else {
  257. let hash = this.pinMsg.hash
  258. let index = this.group.chatList.findIndex(item => item.hash === hash)
  259. if (index >= 0) {
  260. let node = this.$refs.msgWrap.querySelectorAll('.msg-item')[index]
  261. scrollIntoView(node, { behavior: 'smooth', scrollMode: 'if-needed' })
  262. }
  263. }
  264. // 防止加载更多
  265. this.isScrollToView = true
  266. setTimeout(() => {
  267. this.isScrollToView = false
  268. }, 2000)
  269. },
  270. scrollToMsg () {
  271. }
  272. }
  273. }
  274. </script>
  275. <style lang="scss" scoped>
  276. @import './chatRoom.scss';
  277. </style>