chatMini.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833
  1. <template>
  2. <div class="mini-wrap">
  3. <div class="mini-body" v-show="showChat">
  4. <div class="box">
  5. <div class="box-hd">
  6. <div class="btn-menu" @click.stop="showMenuExtra = !showMenuExtra"></div>
  7. <div class="btn-close" @click="handleToggleChat(false)"></div>
  8. <div class="box-title">{{group.groupName}}</div>
  9. <div class="user-tips">
  10. <em>{{group.userCounts}}</em>
  11. </div>
  12. <ul class="menu-extra" v-show="showMenuExtra">
  13. <div class="info clearfix" v-if="account">
  14. <span class="avatar avatar-bg">{{account.name.slice(0,2).toUpperCase()}}</span>
  15. <span class="account-info">
  16. <em>{{account.name}}</em>
  17. <i class="login-out" href="javascript:void(0);" @click="handleLogout">注销</i>
  18. </span>
  19. </div>
  20. <li v-if="group.groupId"><a :href="`/mini.html?groupId=${group.groupId}&show=true`" target="_blank">在新网页中打开聊天<i class="icon-arrow"></i></a></li>
  21. <li><a href="https://www.mee.chat/" target="_blank">官网:www.mee.chat</a></li>
  22. </ul>
  23. </div>
  24. <chat-pin v-bind="pinMsg" @pinMsgClose="pinMsgClose" @scrollToView="scrollToView"></chat-pin>
  25. <div class="box-bd" :style="{height:height - (pinMsg.visible ? 92 : 62) + 'px'} " v-bar>
  26. <div ref="scrollWrap" @scroll="handleScroll">
  27. <div ref="msgWrap" class="msg-wrap">
  28. <div class="msg-top-more" v-if="lockEnd">
  29. <em>没有更多了</em>
  30. </div>
  31. <div class="msg-top-load" v-if="lockMore && !lockEnd">
  32. <i class="msg-loading-icon"></i>
  33. </div>
  34. <template v-if="group.chatList.length">
  35. <msg-item v-for="(item ,key) in group.pinList"
  36. :key="'pin' + key"
  37. v-bind="item"
  38. @quoteMsg="quoteMsg"
  39. @deleteMsg="deleteMsg"
  40. >
  41. </msg-item>
  42. </template>
  43. <msg-item v-for="item in group.chatList"
  44. :key="item.hash"
  45. v-bind="item"
  46. :msgItem="item"
  47. @quoteMsg="quoteMsg"
  48. @deleteMsg="deleteMsg"
  49. ></msg-item>
  50. </div>
  51. </div>
  52. </div>
  53. <div class="box-ft">
  54. <chat-at
  55. ref="chatAt"
  56. v-if="atShow"
  57. @atperson="atPerson"
  58. :curInd="atInd"
  59. :filterList="filterMembers">
  60. </chat-at>
  61. <div class="input-ctrl" v-if="showLoginBtn">
  62. <span v-if="showLoginBtn === 'loading'">登录中...</span>
  63. <span v-else class="enable" @click="handleLogin">登录</span>
  64. </div>
  65. <div class="input-con" v-else>
  66. <div class="btn-file">
  67. <input type="file" ref="inputFile" name="res" @change="handleFile">
  68. </div>
  69. <div class="btn-emoji" @click.stop="showEmoji = !showEmoji"></div>
  70. <div class="icon-packet" @click="$packetSend"></div>
  71. <form class="input-wrap" @submit="handleSend">
  72. <textarea
  73. @keydown.up.prevent="handleUp"
  74. @keydown.down.prevent="handleDown"
  75. cols="1"
  76. ref="chatInput"
  77. rows="1"
  78. @keydown.enter="handleKeyDown"
  79. placeholder="Write a message"
  80. v-model="inputMsg"
  81. @blur="handleBlur"
  82. />
  83. </form>
  84. <div class="btn-send" @click="handleSend">发送</div>
  85. </div>
  86. <div class="emoji-wrap" v-if="showEmoji">
  87. <emoji-list @addEmoji="addEmoji"></emoji-list>
  88. </div>
  89. </div>
  90. </div>
  91. </div>
  92. <div @click="handleToggleChat(true)" class="mini-control" v-show="!showChat">
  93. <ul v-if="unreadCounts > 0">
  94. <li class="msg-tips">
  95. <em>+{{unreadCounts}}</em>
  96. </li>
  97. <li class="user-tips">
  98. <em>{{group.userCounts}}</em>
  99. </li>
  100. </ul>
  101. <div class="meechat-icon" v-else>
  102. <i class="user-tips">
  103. <em>{{group.userCounts}}</em>
  104. </i>
  105. </div>
  106. </div>
  107. </div>
  108. </template>
  109. <script>
  110. import msgItem from '@/components/msgItem'
  111. import emojiList from '@/components/emoji'
  112. import chatAt from '@/components/chatAt'
  113. import chatPin from '@/components/chatPin'
  114. import { mapActions, mapState, mapMutations } from 'vuex'
  115. import API from '@/api'
  116. import { getMiniWsUrl } from '@/util/contract.js'
  117. import { isMobile } from '@/util/util.js'
  118. import WsManager from '@/util/wsManager.js'
  119. import PostMessager from '@/util/postMessager.js'
  120. import ScatterJS from 'scatter-js/dist/scatter.esm'
  121. import { Message } from 'element-ui'
  122. import ImageMin from '@/util/imageMin.js'
  123. import scrollIntoView from 'scroll-into-view-if-needed'
  124. import { chatAtMixin } from '@/mixins'
  125. export default {
  126. name: 'chatMini',
  127. mixins: [chatAtMixin],
  128. components: {
  129. msgItem,
  130. emojiList,
  131. chatAt,
  132. chatPin
  133. },
  134. props: {
  135. width: {
  136. type: Number,
  137. default: 274
  138. },
  139. height: {
  140. type: Number,
  141. default: 390
  142. },
  143. show: {
  144. type: Boolean,
  145. default: false
  146. },
  147. groupId: [Number, String]
  148. },
  149. computed: {
  150. ...mapState([
  151. 'account',
  152. 'group',
  153. 'userId'
  154. ]),
  155. ...mapState({
  156. chatInputFocus: state => state.group.chatInputFocus,
  157. blockList: state => state.group.blockList,
  158. pinMsg: state => state.group.pinMsg,
  159. pinList: state => state.group.pinList
  160. }),
  161. filterMembers () {
  162. let val = this.inputMsg.replace('@', '')
  163. let members = this.group.members
  164. let resArr = []
  165. for (let k in this.group.members) {
  166. if (members[k].nick_name.indexOf(val) > -1) {
  167. resArr.push(members[k])
  168. }
  169. }
  170. return resArr
  171. },
  172. atShow () {
  173. return this.inputMsg.match(/^@/) && this.filterMembers.length
  174. }
  175. },
  176. data () {
  177. return {
  178. isMobile: isMobile(),
  179. showChat: !!this.show, // 显示聊天窗
  180. showEmoji: false, // 显示表情选择框
  181. showMenuExtra: false, // 显示左上角菜单
  182. showLoginBtn: true, // 显示登录按钮
  183. lockMore: false,
  184. lockEnd: false,
  185. loading: false,
  186. unreadCounts: 0, // 未读消息数
  187. inputMsg: '', // 用户输入的内容
  188. atInd: 0 // @人索引
  189. }
  190. },
  191. watch: {
  192. chatInputFocus (val, newval) {
  193. if (this.showLoginBtn) return
  194. let ele = this.$refs.chatInput
  195. if (val) {
  196. if (document.activeElement !== ele) {
  197. this.placeEnd(ele)
  198. ele.focus()
  199. }
  200. } else {
  201. if (document.activeElement === ele) {
  202. ele.blur()
  203. }
  204. }
  205. }
  206. },
  207. async mounted () {
  208. // 初始化postMessage消息管理器
  209. let callback = (event) => {
  210. if (event.action === 'meechat:setShow') {
  211. this.handleToggleChat(event.show)
  212. }
  213. }
  214. this.postMessager = new PostMessager('*', { callback })
  215. window.postMessager = this.postMessager
  216. // 检查登录态
  217. let userId = localStorage.getItem('user_id')
  218. let token = localStorage.getItem('token')
  219. let account = localStorage.getItem('account')
  220. if (userId && token && account) {
  221. this.setUserId(userId)
  222. this.setToken(token)
  223. this.setAccount(JSON.parse(account))
  224. this.showLoginBtn = false
  225. }
  226. // 设置groupId
  227. this.initGroup({
  228. userId: this.userId,
  229. groupId: this.groupId,
  230. useCache: false
  231. })
  232. this.$nextTick(this.initChat)
  233. this.$nextTick(this.initSocket)
  234. document.addEventListener('contextmenu', e => e.preventDefault())
  235. document.addEventListener('paste', this.initPaste)
  236. document.addEventListener('drop', this.initDrop)
  237. document.addEventListener('dragover', this.initDragOver)
  238. document.body.addEventListener('click', () => {
  239. this.showEmoji = false
  240. this.showMenuExtra = false
  241. })
  242. },
  243. beforeDestroy () {
  244. document.removeEventListener('paste', this.initPaste)
  245. document.removeEventListener('drop', this.initDrop)
  246. document.removeEventListener('dragover', this.initDragOver)
  247. },
  248. methods: {
  249. ...mapMutations([
  250. 'initGroup',
  251. 'setUserId',
  252. 'setToken',
  253. 'addChatItem',
  254. 'deleteChatItem',
  255. 'updateChatInputFocus',
  256. 'updateGroupBlockList',
  257. 'updateMembers',
  258. 'updateGroupPinMsg',
  259. 'repealChatItem',
  260. 'addPacketItem',
  261. 'addPacketTip'
  262. ]),
  263. ...mapActions([
  264. 'setScatter',
  265. 'setAccount',
  266. 'doGameLogin',
  267. 'doScatterLogin',
  268. 'doScatterLoginOut',
  269. 'getGroupInfo',
  270. 'getNewMsg',
  271. 'getHistoryMsg',
  272. 'doSendMsg',
  273. 'doSendFile'
  274. ]),
  275. // 连接socket
  276. initSocket () {
  277. if (!window.WebSocket) {
  278. console.error('Error: WebSocket is not supported .')
  279. return
  280. }
  281. let host = getMiniWsUrl() + `?group_id=${this.groupId}`
  282. if (this.socket) {
  283. this.socket.destroy()
  284. this.socket = null
  285. }
  286. this.socket = new WsManager(host, {
  287. autoConnect: true, // 自动连接
  288. reconnection: false, // 断开自动重连
  289. reconnectionDelay: 5000 // 重连间隔时间,单位秒
  290. })
  291. this.socket.on('open', res => {})
  292. this.socket.on('message', (data) => {
  293. data = JSON.parse(data)
  294. if (data.channel.match('chat:group')) {
  295. if (data.data.type === 'msg' && data.data.from != this.userId) {
  296. this.getNewMsg().then(() => {
  297. this.$nextTick(this.resizeToBottom)
  298. })
  299. // 未读消息数+1
  300. if (!this.showChat) {
  301. if (this.unreadCounts === 0) {
  302. this.postResize(130, 50)
  303. }
  304. this.unreadCounts++
  305. }
  306. }
  307. if (data.data.type === 'repeal') {
  308. this.repealChatItem(data.data)
  309. }
  310. if (data.data.type === 'block') {
  311. this.updateGroupBlockList({
  312. type: 'add',
  313. id: data.data.to
  314. })
  315. }
  316. if (data.data.type === 'unblock') {
  317. this.updateGroupBlockList({
  318. type: 'delete',
  319. id: data.data.to
  320. })
  321. }
  322. if (data.data.type === 'join') {
  323. this.updateMembers(data.data.user_info)
  324. }
  325. if (data.data.type === 'pin_msg') {
  326. this.updateGroupPinMsg(data.data.pinMsg)
  327. }
  328. if (data.data.type === 'unpin_msg') {
  329. this.updateGroupPinMsg(null)
  330. }
  331. if (data.data.type === 'new_redpack') {
  332. this.addPacketItem(data.data)
  333. this.$nextTick(this.resizeToBottom)
  334. }
  335. if (data.data.type === 'grab_redpack') {
  336. if (data.data.from == this.userId || data.data.to == this.userId) {
  337. this.addPacketTip(data.data)
  338. this.$nextTick(this.resizeToBottom)
  339. }
  340. }
  341. }
  342. })
  343. },
  344. /**
  345. * 聊天群初始化处理
  346. * 先后调用 group/info, group/msg
  347. */
  348. async initChat () {
  349. this.handleToggleChat(this.show)
  350. await this.getGroupInfo()
  351. await this.getNewMsg()
  352. if (this.show) {
  353. this.$nextTick(this.resizeToBottom)
  354. }
  355. },
  356. async handleLogout () {
  357. this.doScatterLoginOut()
  358. if (self !== top) {
  359. localStorage.removeItem('account')
  360. await this.postMessager.send({
  361. action: 'meechat:logout'
  362. })
  363. }
  364. this.showLoginBtn = true
  365. },
  366. /**
  367. * 登录处理
  368. */
  369. async handleLogin () {
  370. // 设置登录按钮状态
  371. this.showLoginBtn = 'loading'
  372. if (self !== top) {
  373. this.handleParentLogin()
  374. } else {
  375. // 连接scatter
  376. ScatterJS.scatter.connect('MEE_CHAT').then(async connected => {
  377. if (connected) {
  378. // 设置scatter
  379. this.setScatter(ScatterJS.scatter)
  380. // 清空全局scatter引用
  381. window.ScatterJS = null
  382. // 调起scatter授权登录
  383. await this.doScatterLogin().catch(e => {
  384. this.showLoginBtn = true
  385. })
  386. localStorage.setItem('account', JSON.stringify(this.account))
  387. // 签名登录
  388. await this.doGameLogin().catch(e => {
  389. this.showLoginBtn = true
  390. })
  391. this.showLoginBtn = false
  392. this.initGroup({
  393. userId: this.userId,
  394. groupId: this.groupId,
  395. useCache: false
  396. })
  397. }
  398. })
  399. }
  400. },
  401. /**
  402. * @des 调用父级页面getIdentity
  403. */
  404. async getParentIdentity () {
  405. if (self !== top) {
  406. let response = await this.postMessager.send({ action: 'meechat:getIdentity' })
  407. if (!response) {
  408. this.showLoginBtn = true
  409. return
  410. }
  411. let account = response.accounts.find(x => x.blockchain === 'eos')
  412. if (!account) {
  413. this.showLoginBtn = true
  414. }
  415. localStorage.setItem('account', JSON.stringify(account))
  416. this.setAccount(account)
  417. }
  418. },
  419. /**
  420. * @des 调用父级页面登录
  421. */
  422. async handleParentLogin () {
  423. await this.getParentIdentity()
  424. if (!this.account) {
  425. console.log('handleParentLogin, !this.account')
  426. this.showLoginBtn = true
  427. return
  428. }
  429. let publicKey = this.account.publicKey
  430. let response = null
  431. if (!publicKey) {
  432. // 插件版本的scatter拿不到publicKey
  433. response = await this.postMessager.send({
  434. action: 'meechat:getAccount',
  435. data: {
  436. name: this.account.name
  437. }
  438. })
  439. if (!response) {
  440. console.log('handleParentLogin, getAccount, !response')
  441. this.showLoginBtn = true
  442. return
  443. }
  444. let perm = response.permissions.find(x => x.perm_name === 'active')
  445. publicKey = perm.required_auth.keys[0].key
  446. }
  447. let { data } = await API.user.getRandom({ account: this.account.name })
  448. let random = data.data
  449. response = await this.postMessager.send({
  450. action: 'meechat:getArbitrarySignature',
  451. data: {
  452. public_key: publicKey,
  453. random: random
  454. }
  455. })
  456. if (!response) {
  457. console.log('handleParentLogin, getArbitrarySignature, !response')
  458. this.showLoginBtn = true
  459. return
  460. }
  461. let param = {
  462. account: this.account.name,
  463. sign: response,
  464. pubkey: publicKey,
  465. data: random
  466. }
  467. response = await API.user.eosLogin(param)
  468. data = response.data
  469. if (data.result === 1) {
  470. let userId = data.data.user_id
  471. let token = data.data.token
  472. localStorage.setItem('user_id', userId)
  473. localStorage.setItem('token', token)
  474. this.setUserId(userId)
  475. this.setToken(token)
  476. // 设置groupId
  477. this.initGroup({
  478. userId,
  479. groupId: this.groupId,
  480. useCache: false
  481. })
  482. this.showLoginBtn = false
  483. }
  484. },
  485. /**
  486. * 聊天窗体滚动到底部
  487. */
  488. resizeToBottom () {
  489. this.$refs.scrollWrap.scrollTop = this.$refs.msgWrap.offsetHeight
  490. },
  491. /**
  492. * 添加表情
  493. */
  494. addEmoji (value) {
  495. this.inputMsg += value
  496. },
  497. /**
  498. * @des 聊天窗体滚动事件处理集
  499. */
  500. handleScroll (e) {
  501. // 防止切换房间时触发滚动处理
  502. if (!this.group.chatList.length) {
  503. return
  504. }
  505. let msgWrap = this.$refs.msgWrap
  506. let totalHeight = msgWrap.offsetHeight
  507. let scrollTop = e.target.scrollTop
  508. if (scrollTop === 0 && !this.lockMore) {
  509. if (this.group.endHash !== null) {
  510. this.lockMore = true
  511. this.getHistoryMsg().then((res) => {
  512. if (res === 'end') {
  513. this.lockEnd = true
  514. } else {
  515. let scrollBottom = totalHeight - scrollTop
  516. this.$nextTick(() => {
  517. e.target.scrollTop = msgWrap.offsetHeight - scrollBottom
  518. this.ps && this.ps.update()
  519. setTimeout(() => {
  520. this.lockMore = false
  521. }, 800)
  522. })
  523. }
  524. })
  525. }
  526. }
  527. },
  528. /**
  529. * @des 处理消息发送
  530. */
  531. handleSend (e) {
  532. // 判断是否被禁言
  533. if (this.blockList.some(id => id == this.userId)) {
  534. Message({
  535. message: '您已被禁言',
  536. type: 'error'
  537. })
  538. return
  539. }
  540. let text = this.inputMsg.trim()
  541. if (text.length === 0) {
  542. Message({
  543. message: '聊天内容不能为空',
  544. type: 'warning'
  545. })
  546. return
  547. }
  548. let opt = {
  549. type: 0,
  550. msg: text
  551. }
  552. // 用户不是第一次发言
  553. if (this.group.members[this.userId]) {
  554. let createTime = Date.now()
  555. this.addChatItem({
  556. from: this.userId,
  557. content: text,
  558. hash: `${createTime}`,
  559. timestamp: createTime,
  560. createTime,
  561. msg_type: '0',
  562. loading: true
  563. })
  564. opt.createTime = createTime
  565. }
  566. this.doSendMsg(opt)
  567. // 滚到底部
  568. this.$nextTick(function () {
  569. this.inputMsg = ''
  570. this.resizeToBottom()
  571. let elem = this.$refs.chatInput
  572. elem.style.height = 'auto'
  573. elem.scrollTop = elem.scrollHeight
  574. })
  575. e.preventDefault()
  576. return false
  577. },
  578. handleBlur () {
  579. this.updateChatInputFocus(false)
  580. },
  581. placeEnd (el) {
  582. var range = document.createRange()
  583. range.selectNodeContents(el)
  584. range.collapse(false)
  585. var sel = window.getSelection()
  586. sel.removeAllRanges()
  587. sel.addRange(range)
  588. },
  589. initDrop (e) {
  590. e.preventDefault()
  591. let files = Array.from(e.dataTransfer.files)
  592. files.forEach(file => this.handleFile(file))
  593. },
  594. initDragOver (e) {
  595. e.preventDefault()
  596. },
  597. initPaste (event) {
  598. var items = (event.clipboardData || window.clipboardData).items
  599. if (items && items.length) {
  600. Array.from(items).forEach(item => {
  601. let file = item.getAsFile()
  602. if (file) {
  603. this.handleFile(file)
  604. }
  605. })
  606. }
  607. },
  608. /**
  609. * 文件预处理
  610. * @return {Object} data 预处理文件信息
  611. * @param {Number} data.type
  612. * @param {File} data.res
  613. */
  614. async preHandleFile (file) {
  615. let type = file.type
  616. let size = file.size
  617. if (type.match('video')) {
  618. return size > 3 * 1024 * 1024 ? Promise.reject(new Error(file)) : Promise.resolve({
  619. type: 2,
  620. res: file
  621. })
  622. } else if (type.match('audio')) {
  623. return size > 2 * 1024 * 1024 ? Promise.reject(new Error(file)) : Promise.resolve({
  624. type: 3,
  625. res: file
  626. })
  627. } else if (type.match('image')) {
  628. let image = await new ImageMin({
  629. file: file,
  630. maxSize: 1024 * 1024
  631. })
  632. return {
  633. type: 1,
  634. preview: image.base64,
  635. res: image.res
  636. }
  637. }
  638. },
  639. /**
  640. * @des 处理文件发送
  641. */
  642. async handleFile (e) {
  643. let inputfile
  644. if (e.constructor === File) {
  645. inputfile = e
  646. } else {
  647. inputfile = e.target.files[0]
  648. }
  649. try {
  650. let fileInfo = await this.preHandleFile(inputfile)
  651. let opt = { res: fileInfo.res }
  652. if (this.group.members[this.userId]) {
  653. let createTime = Date.now()
  654. this.addChatItem({
  655. content: fileInfo.preview || '',
  656. from: this.userId,
  657. hash: `${createTime}`,
  658. msg_type: fileInfo.type,
  659. timestamp: createTime,
  660. res: fileInfo.res,
  661. loading: true,
  662. createTime
  663. })
  664. opt.createTime = createTime
  665. }
  666. this.doSendFile(opt)
  667. setTimeout(() => {
  668. this.$refs.inputFile.value = null
  669. this.resizeToBottom()
  670. }, 100)
  671. } catch (error) {
  672. Message({
  673. message: '上传文件大小限制:音频2M以内,视频3M以内',
  674. type: 'warning'
  675. })
  676. }
  677. },
  678. /**
  679. * @des 控制聊天窗口开关
  680. */
  681. handleToggleChat (flag) {
  682. if (flag) {
  683. this.showChat = true
  684. this.unreadCounts = 0
  685. this.$nextTick(() => {
  686. this.postResize(this.width, this.height + 16)
  687. this.resizeToBottom()
  688. })
  689. } else {
  690. this.showChat = false
  691. this.$nextTick(() => {
  692. this.postResize(56, 50)
  693. })
  694. }
  695. },
  696. postResize (width, height) {
  697. let request = {
  698. action: 'meechat:resize',
  699. data: {
  700. ch: height,
  701. cw: width
  702. }
  703. }
  704. return window.parent.postMessage(request, '*')
  705. },
  706. handleKeyDown (event) {
  707. if (this.atShow) {
  708. event.preventDefault()
  709. let item = this.filterMembers[this.atInd]
  710. this.atPerson(item.nick_name)
  711. return
  712. }
  713. let elem = event.currentTarget
  714. if (event.altKey || event.ctrlKey) {
  715. // 单纯换行
  716. this.inputMsg = this.inputMsg + '\n'
  717. // 重新调整高度
  718. if (elem.scrollHeight < 175) {
  719. elem.style.height = 'auto'
  720. this.$nextTick(function () {
  721. elem.scrollTop = 0 // 防抖动
  722. elem.style.height = elem.scrollHeight + 'px'
  723. })
  724. }
  725. } else {
  726. event.returnValue = false
  727. this.handleSend(event)
  728. }
  729. return false
  730. },
  731. /**
  732. * @des 引用某条消息
  733. */
  734. quoteMsg (msg) {
  735. this.inputMsg = msg
  736. },
  737. /**
  738. * @des 某条消息被删除
  739. */
  740. deleteMsg (hash) {
  741. this.deleteChatItem(hash)
  742. },
  743. pinMsgClose () {
  744. this.pinMsg.visible = false
  745. },
  746. scrollToView () {
  747. if (this.pinList.length) {
  748. let node = this.$refs.msgWrap.querySelector('.msg-item')
  749. scrollIntoView(node, { behavior: 'smooth', scrollMode: 'if-needed' })
  750. } else {
  751. let hash = this.pinMsg.hash
  752. let index = this.group.chatList.findIndex(item => item.hash === hash)
  753. if (index >= 0) {
  754. let node = this.$refs.msgWrap.querySelectorAll('.msg-item')[index]
  755. scrollIntoView(node, { behavior: 'smooth', scrollMode: 'if-needed' })
  756. }
  757. }
  758. // 防止加载更多
  759. this.lockMore = true
  760. setTimeout(() => {
  761. this.lockMore = false
  762. }, 2000)
  763. }
  764. }
  765. }
  766. </script>
  767. <style lang="scss" scoped>
  768. @import "./chatMini.scss";
  769. </style>