chatMiniHandle.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788
  1. import msgItem from '@/components/msgItem'
  2. import emoji from '@/components/emoji'
  3. import chatAt from '@/components/chatAt'
  4. import atMe from '@/components/chatAt/atme'
  5. import chatPin from '@/components/chatPin'
  6. import toolbar from '@/components/chatInput/toolbar'
  7. import { mapActions, mapState, mapMutations } from 'vuex'
  8. import { getMiniWsUrl } from '@/util/contract.js'
  9. import { isMobile, scrollMsgIntoView, lazyloadImage } from '@/util/util.js'
  10. import WsManager from '@/util/wsManager.js'
  11. import PostMessager from '@/util/postMessager.js'
  12. import { Message } from 'element-ui'
  13. import ImageMin from '@/util/imageMin.js'
  14. import { chatAtMixin, chatInputMixin, changeLangMixin } from '@/mixins'
  15. import { accountLoginMixin } from '@/mixins/login'
  16. import { emojiList } from '@/util/emoji'
  17. import api from '@/api'
  18. import loginBox from '@/components/login/loginBox'
  19. export default {
  20. name: 'chatMini',
  21. mixins: [accountLoginMixin, chatAtMixin, chatInputMixin, changeLangMixin],
  22. components: {
  23. msgItem,
  24. emoji,
  25. chatAt,
  26. chatPin,
  27. atMe,
  28. toolbar,
  29. loginBox
  30. },
  31. props: {
  32. width: {
  33. type: Number,
  34. default: 274
  35. },
  36. height: {
  37. type: Number,
  38. default: 390
  39. },
  40. show: {
  41. type: Boolean,
  42. default: false
  43. },
  44. groupId: [Number, String]
  45. },
  46. computed: {
  47. ...mapState([
  48. 'account',
  49. 'group',
  50. 'userId',
  51. 'userInfo'
  52. ]),
  53. ...mapState({
  54. chatInputFocus: state => state.group.chatInputFocus,
  55. blockList: state => state.group.blockList,
  56. pinMsg: state => state.group.pinMsg,
  57. atList: state => state.group.atList,
  58. chatList: state => state.group.chatList,
  59. unreadNums: state => state.group.unreadNums,
  60. sessionList: state => state.chat.sessionList
  61. }),
  62. emojiMap () {
  63. var emojiMap = {}
  64. for (let i in emojiList) {
  65. let arr = emojiList[i]
  66. arr.forEach(v => {
  67. let names = JSON.stringify(v.names)
  68. let emoji = v.surrogates
  69. emojiMap[names] = emoji
  70. })
  71. }
  72. return emojiMap
  73. },
  74. linkToCreator () {
  75. let { creator, userId } = this.group
  76. let sessionId = creator > userId ? `${userId}-${creator}` : `${creator}-${userId}`
  77. return `${location.origin}/#/pm/${sessionId}`
  78. },
  79. isAdmin () {
  80. return (this.group.adminList && this.group.adminList.some(id => id == this.userId)) || this.group.creator == this.userId
  81. }
  82. },
  83. data () {
  84. return {
  85. loginBoxVisible: false,
  86. isLoadingRoom: true,
  87. isMobile: isMobile(),
  88. showChat: !!this.show, // 显示聊天窗
  89. showEmoji: false, // 显示表情选择框
  90. showMenuExtra: false, // 显示左上角菜单
  91. showLoginBtn: true, // 显示登录按钮
  92. lockMore: false,
  93. isScrollToView: false,
  94. lockEnd: false,
  95. loading: false,
  96. unreadCounts: 0, // 未读消息数
  97. inputMsg: '', // 用户输入的内容
  98. atInd: 0, // @人索引
  99. inputHeight: 18,
  100. enableScroll: false, // 记录滚动条是否激活的状态
  101. isBottom: true,
  102. toolShow: false,
  103. chatImageArrSel: null,
  104. serverUnRead: 0, // 客服未读
  105. personUnRead: 0 // 私聊未读
  106. }
  107. },
  108. watch: {
  109. inputMsg (val, newval) {
  110. let ele = this.$refs.chatInput
  111. this.inputHeight = 'auto'
  112. this.$nextTick(() => {
  113. this.inputHeight = Math.max(18, Math.min(ele.scrollHeight, 75)) + 'px'
  114. })
  115. },
  116. chatList (val) {
  117. let lastVal = val[val.length - 1]
  118. if ((lastVal && lastVal.msg_type == 4) || this.isBottom) {
  119. // 自己发的红包自动滚动到底部
  120. this.$nextTick(this.resizeToBottom)
  121. }
  122. },
  123. chatInputFocus (val, newval) {
  124. if (this.showLoginBtn) return
  125. let ele = this.$refs.chatInput
  126. if (val) {
  127. if (document.activeElement !== ele) {
  128. this.placeEnd(ele)
  129. ele.focus()
  130. }
  131. } else {
  132. if (document.activeElement === ele) {
  133. ele.blur()
  134. }
  135. }
  136. }
  137. },
  138. async mounted () {
  139. // 初始化postMessage消息管理器
  140. let callback = (event) => {
  141. if (event.action === 'meechat:setShow') {
  142. this.handleToggleChat(event.show)
  143. } else if (event.action === 'meechat:logout') {
  144. this.handleLogout2()
  145. }
  146. }
  147. this.postMessager = new PostMessager('*', { callback })
  148. window.postMessager = this.postMessager
  149. // 设置groupId
  150. this.initGroup({
  151. userId: this.userId,
  152. groupId: this.groupId,
  153. useCache: false
  154. })
  155. // 检查登录态
  156. let isLogin = await this.checkLocalLogin()
  157. let account = localStorage.getItem('accounts')
  158. if (isLogin) {
  159. this.setAccount(JSON.parse(account))
  160. this.showLoginBtn = false
  161. await this.getUserInfo()
  162. }
  163. this.$nextTick(this.initChat)
  164. this.$nextTick(this.initMiniSocket)
  165. document.getElementById('app').addEventListener('contextmenu', e => e.preventDefault())
  166. document.addEventListener('paste', this.initPaste)
  167. document.addEventListener('drop', this.initDrop)
  168. document.addEventListener('dragover', this.initDragOver)
  169. document.body.addEventListener('click', () => {
  170. this.showEmoji = false
  171. this.showMenuExtra = false
  172. this.toolShow = false
  173. })
  174. },
  175. beforeDestroy () {
  176. document.removeEventListener('paste', this.initPaste)
  177. document.removeEventListener('drop', this.initDrop)
  178. document.removeEventListener('dragover', this.initDragOver)
  179. },
  180. methods: {
  181. ...mapMutations([
  182. 'initGroup',
  183. 'setUserId',
  184. 'setToken',
  185. 'addChatItem',
  186. 'deleteChatItem',
  187. 'updateChatInputFocus',
  188. 'updateGroupBlockList',
  189. 'updateMembers',
  190. 'updateGroupPinMsg',
  191. 'repealChatItem',
  192. 'removeAtListLast',
  193. 'clearAtList',
  194. 'initState',
  195. 'addPacketItem',
  196. 'addPacketTip',
  197. 'addUnreadNums',
  198. 'resetUnreadNums',
  199. 'addPinChatItem'
  200. ]),
  201. ...mapActions([
  202. 'getUserInfo',
  203. 'setAccount',
  204. 'doGameLogin',
  205. 'doScatterLogout',
  206. 'getGroupInfo',
  207. 'getNewMsgFromDb',
  208. 'getNewMsg',
  209. 'getHistoryMsg',
  210. 'doSendMsg',
  211. 'doSendFile',
  212. 'doContractLogin',
  213. 'updateSessionLastmsg'
  214. ]),
  215. handleMoreClick () {
  216. this.showEmoji = false
  217. this.toolShow = !this.toolShow
  218. this.checkNeedToBottom()
  219. },
  220. handleEmojiClick () {
  221. this.toolShow = false
  222. this.showEmoji = !this.showEmoji
  223. this.checkNeedToBottom()
  224. },
  225. checkNeedToBottom () {
  226. if (!this.isBottom) return
  227. this.$nextTick(() => {
  228. this.resizeToBottom()
  229. })
  230. },
  231. async initMiniLoginCallback () {
  232. await this.initMiniSocket()
  233. this.showLoginBtn = false
  234. this.initGroup({
  235. userId: this.userId,
  236. groupId: this.groupId,
  237. useCache: false
  238. })
  239. await this.getUserInfo()
  240. this.loginBoxVisible = false
  241. },
  242. // 连接socket
  243. initMiniSocket () {
  244. if (!window.WebSocket) {
  245. console.log('Error: WebSocket is not supported .')
  246. return
  247. }
  248. let host = getMiniWsUrl() + `?group_id=${this.groupId}`
  249. if (this.socket) {
  250. this.socket.destroy()
  251. this.socket = null
  252. }
  253. this.socket = new WsManager(host, {
  254. autoConnect: true, // 自动连接
  255. reconnection: true, // 断开自动重连
  256. reconnectionDelay: 2000 // 重连间隔时间,单位秒
  257. })
  258. this.socket.on('open', res => {})
  259. this.socket.on('message', (data) => {
  260. data = JSON.parse(data)
  261. if (data.channel.match('chat:group')) {
  262. if (data.data.type === 'msg') {
  263. this.getNewMsg({ newMsg: true })
  264. if (data.data.from != this.userId) {
  265. // 未读消息数+1
  266. if (!this.showChat) {
  267. if (this.unreadCounts === 0) {
  268. this.postResize(130, 50)
  269. }
  270. this.unreadCounts++
  271. } else {
  272. this.addUnreadNums()
  273. }
  274. }
  275. }
  276. if (data.data.type === 'repeal') {
  277. this.repealChatItem(data.data)
  278. }
  279. if (data.data.type === 'block') {
  280. this.updateGroupBlockList({
  281. type: 'add',
  282. id: data.data.to
  283. })
  284. }
  285. if (data.data.type === 'unblock') {
  286. this.updateGroupBlockList({
  287. type: 'delete',
  288. id: data.data.to
  289. })
  290. }
  291. if (data.data.type === 'join') {
  292. this.updateMembers(data.data.user_info)
  293. }
  294. if (data.data.type === 'pin_msg') {
  295. this.updateGroupPinMsg(data.data.pinMsg)
  296. }
  297. if (data.data.type === 'unpin_msg') {
  298. this.updateGroupPinMsg(null)
  299. }
  300. if (data.data.type === 'new_redpack') {
  301. this.addPacketItem(data.data)
  302. if (data.data.from == this.userId) {
  303. this.$nextTick(this.resizeToBottom)
  304. }
  305. }
  306. if (data.data.type === 'grab_redpack') {
  307. if (data.data.from == this.userId || data.data.to == this.userId) {
  308. this.addPacketTip(data.data)
  309. }
  310. }
  311. }
  312. })
  313. },
  314. /**
  315. * 聊天群初始化处理
  316. * 先后调用 group/info, group/msg
  317. */
  318. async initChat () {
  319. this.handleToggleChat(this.show)
  320. this.isLoadingRoom = true
  321. await this.getGroupInfo()
  322. await this.getNewMsgFromDb()
  323. await this.getNewMsg()
  324. this.isLoadingRoom = false
  325. // 有登录态才要请求
  326. if (!this.showLoginBtn) {
  327. await this.getPmUnRead()
  328. }
  329. if (this.show) {
  330. this.$nextTick(this.resizeToBottom)
  331. }
  332. this.chatImageArrSel = this.$refs.scrollWrap.getElementsByTagName('img')
  333. },
  334. getPmUnRead () {
  335. api.session.getMiniUnRead().then(({ data }) => {
  336. this.personUnRead = data.data['0']
  337. this.serverUnRead = data.data[this.groupId] || 0
  338. })
  339. },
  340. /**
  341. * 清空私聊未读
  342. * @param {type} 1.客服2.私聊
  343. **/
  344. clearPmUnread (type) {
  345. if (type == 1) this.serverUnRead = 0
  346. else this.personUnRead = 0
  347. },
  348. async handleLogout () {
  349. this.doScatterLogout()
  350. this.showLoginBtn = true
  351. if (self !== top) {
  352. localStorage.removeItem('account')
  353. this.postMessager.send({
  354. action: 'meechat:logout'
  355. })
  356. }
  357. // 初始vuex数据
  358. this.$store.commit('setUserInfo', null)
  359. // this.$store.commit('initChatData')
  360. // this.$store.commit('initGroupData')
  361. // 注销后,刷新页面
  362. location.replace(location.href.replace('show=false', 'show=true'))
  363. },
  364. async handleLogout2 () {
  365. this.doScatterLogout()
  366. this.showLoginBtn = true
  367. },
  368. /**
  369. * 登录处理
  370. */
  371. async handleLogin () {
  372. // 设置登录按钮状态
  373. this.showLoginBtn = 'loading'
  374. // if (self !== top) {
  375. // this.handleParentLogin()
  376. // } else {
  377. // // 连接scatter
  378. // this.scatterConnect()
  379. // }
  380. // this.$login()
  381. this.loginBoxVisible = true
  382. },
  383. async scatterConnect () {
  384. await this.loginEos()
  385. },
  386. /**
  387. * @des 调用父级页面getIdentity
  388. */
  389. async getParentIdentity () {
  390. if (self !== top) {
  391. try {
  392. let response = await this.postMessager.send({ action: 'meechat:getIdentity' })
  393. let account = response.accounts.find(x => x.blockchain === 'eos')
  394. localStorage.setItem('account', JSON.stringify(account))
  395. this.setAccount(account)
  396. } catch (error) {
  397. this.showLoginBtn = true
  398. }
  399. }
  400. },
  401. /**
  402. * @des 调用父级页面登录
  403. */
  404. async handleParentLogin () {
  405. try {
  406. // scatter登录
  407. await this.getParentIdentity()
  408. if (!this.account) return
  409. // 合约登录
  410. await this.doContractLogin()
  411. location.replace(location.href.replace('show=false', 'show=true'))
  412. } catch (msg) {
  413. if (msg.type) {
  414. // scatter报错的情况
  415. } else {
  416. let json = JSON.parse(msg)
  417. let details = json.error.details
  418. Message({
  419. message: details[0].message,
  420. type: 'error'
  421. })
  422. }
  423. this.showLoginBtn = true
  424. }
  425. },
  426. /**
  427. * 聊天窗体滚动到底部
  428. */
  429. resizeToBottom () {
  430. this.$refs.scrollWrap.scrollTop = this.$refs.msgWrap.offsetHeight
  431. this.resetUnreadNums()
  432. this.isBottom = true
  433. lazyloadImage({
  434. wrap: this.$refs.scrollWrap,
  435. imageArr: this.chatImageArrSel,
  436. derection: 'up'
  437. })
  438. },
  439. /**
  440. * @des 点击,查看未读消息
  441. * 直接滚动到聊天列表底部
  442. */
  443. doSetRead () {
  444. this.resizeToBottom()
  445. },
  446. /**
  447. * 添加表情
  448. */
  449. addEmoji (value) {
  450. this.inputMsg += value
  451. },
  452. /**
  453. * @des 聊天窗体滚动事件处理集
  454. */
  455. handleScroll (e) {
  456. this.enableScroll = true
  457. e.target.focus()
  458. // 防止切换房间时触发滚动处理
  459. if (!this.group.chatList.length) {
  460. return
  461. }
  462. // 防止滚动到置顶消息触发滚动
  463. if (this.isScrollToView) {
  464. return
  465. }
  466. let msgWrap = this.$refs.msgWrap
  467. let totalHeight = msgWrap.offsetHeight
  468. let scrollTop = e.target.scrollTop
  469. if (scrollTop === 0 && !this.lockMore) {
  470. if (this.group.endHash !== null) {
  471. this.lockMore = true
  472. this.getHistoryMsg().then((res) => {
  473. if (res === 'end') {
  474. this.lockEnd = true
  475. } else {
  476. let scrollBottom = totalHeight - scrollTop
  477. this.$nextTick(() => {
  478. e.target.scrollTop = msgWrap.offsetHeight - scrollBottom
  479. this.ps && this.ps.update()
  480. setTimeout(() => {
  481. this.lockMore = false
  482. }, 800)
  483. })
  484. }
  485. })
  486. }
  487. }
  488. // 滚动到底部清空未读消息状态
  489. if (scrollTop + e.target.offsetHeight > totalHeight) {
  490. this.isBottom = true
  491. if (this.unreadNums) {
  492. this.resetUnreadNums()
  493. }
  494. } else {
  495. this.isBottom = false
  496. }
  497. lazyloadImage({
  498. wrap: this.$refs.scrollWrap,
  499. imageArr: this.chatImageArrSel,
  500. derection: 'up'
  501. })
  502. },
  503. /**
  504. * @des 处理消息发送
  505. */
  506. async handleSend (e) {
  507. // 判断是否被禁言
  508. if (this.blockList.some(id => id == this.userId)) {
  509. Message({
  510. message: this.$t('chat.youAreBan'),
  511. type: 'error'
  512. })
  513. return
  514. }
  515. // 替换emoji字符串
  516. let _inputMsg = this.inputMsg
  517. let parts = _inputMsg.match(/\["[a-z0-9A-Z_]+"\]/g)
  518. for (let k in parts) {
  519. let emoji = this.emojiMap[parts[k]]
  520. if (emoji) {
  521. _inputMsg = _inputMsg.replace(parts[k], emoji)
  522. }
  523. }
  524. let text = _inputMsg.trim()
  525. if (text.length === 0) {
  526. Message({
  527. message: this.$t('chat.cannotBeEmpty'),
  528. type: 'warning'
  529. })
  530. return
  531. }
  532. let opt = {
  533. type: 0,
  534. msg: text
  535. }
  536. // 清空输入框
  537. this.inputMsg = ''
  538. // 用户不是第一次发言
  539. if (this.group.members[this.userId]) {
  540. let createTime = Date.now()
  541. this.addChatItem({
  542. from: this.userId,
  543. content: text,
  544. hash: `${createTime}`,
  545. timestamp: createTime,
  546. createTime,
  547. msg_type: '0',
  548. loading: true
  549. })
  550. opt.createTime = createTime
  551. await this.doSendMsg(opt)
  552. } else {
  553. // 发言后,才滚动底部
  554. await this.doSendMsg(opt)
  555. }
  556. // 滚到底部
  557. this.$nextTick(function () {
  558. this.resizeToBottom()
  559. })
  560. e.preventDefault()
  561. return false
  562. },
  563. placeEnd (el) {
  564. var range = document.createRange()
  565. range.selectNodeContents(el)
  566. range.collapse(false)
  567. var sel = window.getSelection()
  568. sel.removeAllRanges()
  569. sel.addRange(range)
  570. },
  571. initDrop (e) {
  572. e.preventDefault()
  573. let files = Array.from(e.dataTransfer.files)
  574. files.forEach(file => this.handleFile(file))
  575. },
  576. initDragOver (e) {
  577. e.preventDefault()
  578. },
  579. initPaste (event) {
  580. var items = (event.clipboardData || window.clipboardData).items
  581. if (items && items.length) {
  582. Array.from(items).forEach(item => {
  583. let file = item.getAsFile()
  584. if (file) {
  585. this.handleFile(file)
  586. }
  587. })
  588. }
  589. },
  590. /**
  591. * 文件预处理
  592. * @return {Object} data 预处理文件信息
  593. * @param {Number} data.type
  594. * @param {File} data.res
  595. */
  596. async preHandleFile (file) {
  597. let type = file.type
  598. let size = file.size
  599. if (type.match('video')) {
  600. return size > 3 * 1024 * 1024 ? Promise.reject(new Error(file)) : Promise.resolve({
  601. type: 2,
  602. res: file
  603. })
  604. } else if (type.match('audio')) {
  605. return size > 2 * 1024 * 1024 ? Promise.reject(new Error(file)) : Promise.resolve({
  606. type: 3,
  607. res: file
  608. })
  609. } else if (type.match('image')) {
  610. let image = await new ImageMin({
  611. file: file,
  612. maxSize: 1024 * 1024
  613. })
  614. return {
  615. type: 1,
  616. preview: image.base64,
  617. res: image.res
  618. }
  619. }
  620. },
  621. /**
  622. * @des 处理文件发送
  623. */
  624. async handleFile (e) {
  625. let inputfile
  626. if (e.constructor === File) {
  627. inputfile = e
  628. } else {
  629. inputfile = e.target.files[0]
  630. }
  631. try {
  632. let fileInfo = await this.preHandleFile(inputfile)
  633. let opt = { res: fileInfo.res }
  634. if (this.group.members[this.userId]) {
  635. let createTime = Date.now()
  636. this.addChatItem({
  637. content: fileInfo.preview || '',
  638. from: this.userId,
  639. hash: `${createTime}`,
  640. msg_type: fileInfo.type,
  641. timestamp: createTime,
  642. res: fileInfo.res,
  643. loading: true,
  644. createTime
  645. })
  646. opt.createTime = createTime
  647. }
  648. this.doSendFile(opt)
  649. this.toolShow = false
  650. setTimeout(() => {
  651. this.$refs.toolbar.resetInput()
  652. this.resizeToBottom()
  653. }, 100)
  654. } catch (error) {
  655. Message({
  656. message: this.$t('chat.maxUploadTips'),
  657. type: 'warning'
  658. })
  659. }
  660. },
  661. /**
  662. * @des 控制聊天窗口开关
  663. */
  664. handleToggleChat (flag) {
  665. if (flag) {
  666. this.showChat = true
  667. this.unreadCounts = 0
  668. this.$nextTick(() => {
  669. this.postResize(this.width, this.height + 16)
  670. this.resizeToBottom()
  671. })
  672. } else {
  673. this.showChat = false
  674. this.$nextTick(() => {
  675. this.postResize(56, 50)
  676. })
  677. }
  678. },
  679. postResize (width, height) {
  680. let request = {
  681. action: 'meechat:resize',
  682. data: {
  683. ch: height,
  684. cw: width
  685. }
  686. }
  687. return window.parent.postMessage(request, '*')
  688. },
  689. /**
  690. * @des 引用某条消息
  691. */
  692. quoteMsg (msg) {
  693. this.inputMsg = msg
  694. },
  695. /**
  696. * @des 某条消息被删除
  697. */
  698. deleteMsg (hash) {
  699. this.deleteChatItem(hash)
  700. },
  701. pinMsgClose () {
  702. this.pinMsg.visible = false
  703. },
  704. scrollToView () {
  705. if (!this.pinMsg) return
  706. let hash = this.pinMsg.hash
  707. let index = this.group.chatList.findIndex(item => item.hash === hash)
  708. if (index < 0) {
  709. this.addPinChatItem(this.pinMsg)
  710. index = 0
  711. }
  712. let node = this.$refs.msgWrap.getElementsByClassName('msg-item')[index]
  713. let toOffsetTop = index >= 0 ? node.offsetTop - (this.pinMsg ? 40 : 10) : node.offsetTop
  714. scrollMsgIntoView(
  715. this.$refs.scrollWrap, toOffsetTop, node
  716. )
  717. // 防止加载更多
  718. this.isScrollToView = true
  719. setTimeout(() => {
  720. this.isScrollToView = false
  721. lazyloadImage({
  722. wrap: this.$refs.scrollWrap,
  723. imageArr: this.chatImageArrSel,
  724. derection: 'up'
  725. })
  726. }, 2000)
  727. },
  728. scrollToMsg (index) {
  729. let hash = this.atList[index].hash
  730. let eleIndex = this.group.chatList.findIndex(item => item.hash === hash)
  731. if (eleIndex >= 0) {
  732. let node = this.$refs.msgWrap.querySelectorAll('.msg-item').item(eleIndex)
  733. scrollMsgIntoView(this.$refs.scrollWrap, node.offsetTop - (this.pinMsg ? 40 : 10), node)
  734. }
  735. this.removeAtListLast()
  736. }
  737. }
  738. }