chatRoom.vue 8.1 KB

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