index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. import api from '@/api'
  2. import { getLanguage, getUserOpt, setUserOpt, mobileInputBlur } from '@/util/util'
  3. import { mapState } from 'vuex'
  4. import _ from 'lodash'
  5. // 搜索mixin
  6. export const searchUserMixin = {
  7. data () {
  8. return {
  9. isSearch: false, // 搜索展示
  10. searchList: null // 搜索结果
  11. }
  12. },
  13. methods: {
  14. searchUser (e, originList) {
  15. let val = e.target.value.toLocaleLowerCase()
  16. if (!val) {
  17. this.isSearch = false
  18. return
  19. }
  20. this.searchList = originList.filter(item => {
  21. let inName, inNick
  22. let name = (item.name || item.user_name || '').toLocaleLowerCase()
  23. let nickName = (item.nick_name || '').toLocaleLowerCase()
  24. inName = name ? name.indexOf(val) > -1 : false
  25. inNick = nickName ? nickName.indexOf(val) > -1 : false
  26. return inName || inNick
  27. })
  28. this.isSearch = true
  29. }
  30. }
  31. }
  32. // 群搜索mixin
  33. export const searchGroupUserMixin = {
  34. data () {
  35. return {
  36. isSearchGroup: false,
  37. searchUserTimer: null
  38. }
  39. },
  40. methods: {
  41. async searchUser (e, originList) {
  42. clearTimeout(this.searchUserTimer)
  43. this.searchUserTimer = setTimeout(async () => {
  44. let val = e.target.value.toLocaleLowerCase()
  45. let inviteType = this.inviteType
  46. this.isSearchGroup = true
  47. if (!val.trim()) {
  48. originList.forEach(item => {
  49. this.$set(item, 'isShow', true)
  50. })
  51. this.$nextTick(() => {
  52. this.isSearchGroup = false
  53. this.showNum = originList.length
  54. })
  55. return
  56. }
  57. // 当前数据搜索
  58. originList.forEach(item => {
  59. let inName, inNick
  60. let name = (item.user_name || '').toLocaleLowerCase()
  61. let nickName = (item.nick_name || '').toLocaleLowerCase()
  62. inName = name ? name.indexOf(val) > -1 : false
  63. inNick = nickName ? nickName.indexOf(val) > -1 : false
  64. this.$set(item, 'isShow', inName || inNick)
  65. })
  66. // 接口搜索
  67. if (!(originList.length + 1 >= this.membersNum || inviteType == 1 || inviteType == 2)) {
  68. await api.group.searchMember({
  69. group_id: this.group.groupId,
  70. keyword: val
  71. }).then(({ data }) => {
  72. let list = data.data
  73. list.forEach(item => {
  74. item.isChecked = false
  75. item.isChoosed = false
  76. item.isShow = true
  77. })
  78. originList = _.unionBy([...originList, ...list], 'user_id')
  79. })
  80. }
  81. this.$nextTick(() => {
  82. this.isSearchGroup = false
  83. this.showNum = originList.filter(item => {
  84. return item.isShow
  85. }).length
  86. })
  87. }, 200)
  88. }
  89. }
  90. }
  91. // @人
  92. export const chatAtMixin = {
  93. data () {
  94. return {
  95. atInd: 0 // @人索引
  96. }
  97. },
  98. methods: {
  99. atPerson (name, nickName = name) {
  100. let el = this.$refs.chatInput
  101. let selectionStart = el.selectionStart
  102. let realStart = selectionStart - this.keyAfterAt.length
  103. this.inputMsg = this.inputMsg.slice(0, realStart) + `${name} ` + this.inputMsg.slice(selectionStart)
  104. this.atInd = 0
  105. el.focus()
  106. this.$nextTick(() => {
  107. el.setSelectionRange(realStart + name.length + 1, realStart + name.length + 1)
  108. })
  109. this.$store.commit('updateIsNewAt', {
  110. isNewAt: this.group.isNewAt,
  111. isNewAtFound: true
  112. })
  113. this.$store.commit('updateGroupSearchList', [])
  114. },
  115. handleUp (event) {
  116. if (this.atInd > 0) {
  117. this.atInd--
  118. }
  119. this.atShow && event.preventDefault()
  120. return true
  121. },
  122. handleDown (event) {
  123. let membersLen = this.filterMembers.length
  124. this.atInd < membersLen - 1 ? this.atInd++ : this.atInd = 0
  125. this.atShow && event.preventDefault()
  126. return true
  127. }
  128. }
  129. }
  130. // 聊天输入框
  131. export const chatInputMixin = {
  132. data () {
  133. return {
  134. selectionAfterAt: false, // 光标在@后面
  135. keyAfterAt: '', // 光标后的搜索字
  136. keyAfterAtTimer: '', // 光标后的搜索字触发定时器
  137. lastKeyAfterAt: '',
  138. isIOS: !!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),
  139. isShowContextmenu: false,
  140. menuTop: 0, // 右键菜单-top
  141. menuLeft: 0 // 右键菜单-left
  142. }
  143. },
  144. mounted () {
  145. },
  146. computed: {
  147. ...mapState([
  148. 'copyText'
  149. ]),
  150. filterMembers () {
  151. if (!this.group.groupId) return []
  152. let members = this.group.membersArray
  153. let resArr = []
  154. if (this.keyAfterAt !== '') {
  155. resArr = this.group.searchList
  156. } else {
  157. for (let k = 0; k < members.length; k++) {
  158. if (members[k].user_id == this.userId) continue
  159. resArr.push(members[k])
  160. }
  161. }
  162. return resArr.slice(0, 100)
  163. },
  164. atShow () {
  165. this.atInd = 0
  166. return this.selectionAfterAt && this.filterMembers.length
  167. }
  168. },
  169. watch: {
  170. keyAfterAt (val) {
  171. clearTimeout(this.keyAfterAtTimer)
  172. this.keyAfterAtTimer = setTimeout(() => {
  173. this.handleKeyAfterAt(val)
  174. }, 200)
  175. },
  176. inputMsg (val, newval) {
  177. this.handleSelectionChange()
  178. }
  179. },
  180. methods: {
  181. handleKeyAfterAt (val) {
  182. if (!this.group.groupId || val.trim() == '') return
  183. if (this.group.isNewAtFound) return
  184. if (this.group.membersArray.length < this.group.membersNum) {
  185. api.group.searchMember({
  186. group_id: this.group.groupId,
  187. keyword: val
  188. }).then(({ data }) => {
  189. this.$store.commit('updateGroupSearchList', data.data)
  190. })
  191. } else {
  192. let searchList = this.group.membersArray.filter(item => {
  193. let inName, inNick
  194. inName = item.user_name ? item.user_name.indexOf(val) > -1 : false
  195. inNick = item.nick_name ? item.nick_name.indexOf(val) > -1 : false
  196. return inName || inNick
  197. })
  198. this.$store.commit('updateGroupSearchList', searchList)
  199. }
  200. },
  201. getStrBeforeSelection () {
  202. let el = this.$refs.chatInput
  203. if (!el) return ''
  204. let selectionStart = el.selectionStart
  205. let prevStr = this.inputMsg.slice(0, selectionStart)
  206. return prevStr
  207. },
  208. getStrAfterSelection () {
  209. let el = this.$refs.chatInput
  210. if (!el) return ''
  211. let selectionStart = el.selectionStart
  212. let prevStr = this.inputMsg.slice(selectionStart)
  213. return prevStr
  214. },
  215. handleClick () {
  216. this.isShowContextmenu = false
  217. },
  218. handleLeft (event) {
  219. let el = this.$refs.chatInput
  220. let selectionStart = el.selectionStart
  221. if (selectionStart === 0) return true
  222. let prevStr = this.getStrBeforeSelection()
  223. let members = this.group.members
  224. for (let k in members) {
  225. let name = members[k].user_name
  226. let reg = new RegExp(`@${name} $`)
  227. if (reg.test(prevStr)) {
  228. event.preventDefault()
  229. el.setSelectionRange(selectionStart - name.length - 2, selectionStart - name.length - 2)
  230. return true
  231. }
  232. }
  233. },
  234. handleRight (event) {
  235. let el = this.$refs.chatInput
  236. let selectionStart = el.selectionStart
  237. let afterStr = this.getStrAfterSelection()
  238. let members = this.group.members
  239. for (let k in members) {
  240. let name = members[k].user_name
  241. let reg = new RegExp(`^@${name} `)
  242. if (reg.test(afterStr)) {
  243. event.preventDefault()
  244. el.setSelectionRange(selectionStart + name.length + 2, selectionStart + name.length + 2)
  245. return true
  246. }
  247. }
  248. return true
  249. },
  250. handleDel (event) {
  251. let el = this.$refs.chatInput
  252. let selectionStart = el.selectionStart
  253. if (selectionStart === 0) return
  254. let prevStr = this.getStrBeforeSelection()
  255. let members = this.group.members
  256. for (let k in members) {
  257. let name = members[k].user_name
  258. let reg = new RegExp(`@${name} $`)
  259. if (reg.test(prevStr)) {
  260. event.preventDefault()
  261. this.inputMsg = this.inputMsg.slice(0, selectionStart - name.length - 2) + this.inputMsg.slice(selectionStart)
  262. this.$nextTick(() => {
  263. el.setSelectionRange(selectionStart - name.length - 2, selectionStart - name.length - 2)
  264. })
  265. return
  266. }
  267. }
  268. },
  269. handleKeyDown (event) {
  270. if (this.atShow) {
  271. event.preventDefault()
  272. let item = this.filterMembers[this.atInd]
  273. this.atPerson(item.user_name, item.nick_name)
  274. return
  275. }
  276. if (event.altKey || event.ctrlKey) {
  277. let beforeStr = this.getStrBeforeSelection()
  278. let afterStr = this.getStrAfterSelection()
  279. // 单纯换行
  280. this.inputMsg = beforeStr + '\n' + afterStr
  281. this.$nextTick(() => {
  282. this.$refs.chatInput.setSelectionRange(beforeStr.length + 1, beforeStr.length + 1)
  283. })
  284. } else {
  285. this.handleSend(event)
  286. event.preventDefault()
  287. }
  288. return true
  289. },
  290. handleFocus () {
  291. this.$emit('handleFocus') // 针对完整版
  292. this.updateChatInputFocus(true)
  293. document.addEventListener('selectionchange', this.handleSelectionChange)
  294. if (this.isIOS) {
  295. setTimeout(() => {
  296. this.$root.$el.addEventListener('click', this.fixIOS)
  297. }, 0)
  298. }
  299. },
  300. handleBlur () {
  301. document.removeEventListener('selectionchange', this.handleSelectionChange)
  302. this.updateChatInputFocus(false)
  303. // 定时防止@列表/右键粘贴点击前消失
  304. if (this.selectionAfterAt || this.isShowContextmenu) {
  305. setTimeout(() => {
  306. this.selectionAfterAt = false
  307. this.isShowContextmenu = false
  308. }, 200)
  309. }
  310. if (this.isIOS) {
  311. let scrollTop = document.body.scrollTop
  312. document.body.scrollTop = scrollTop
  313. this.$root.$el.removeEventListener('click', this.fixIOS)
  314. }
  315. mobileInputBlur()
  316. },
  317. handleEsc () {
  318. this.selectionAfterAt = false
  319. },
  320. /**
  321. * 监听光标位置
  322. */
  323. handleSelectionChange () {
  324. let el = this.$refs.chatInput
  325. if (!el) return
  326. let selectionStart = el.selectionStart
  327. let selectionEnd = this.$refs.chatInput.selectionEnd
  328. if (selectionStart !== selectionEnd) return
  329. let prevStr = this.getStrBeforeSelection()
  330. this.selectionAfterAt = /@/.test(prevStr)
  331. if (this.selectionAfterAt) {
  332. this.keyAfterAt = prevStr.slice(prevStr.lastIndexOf('@') + 1)
  333. let isNewAt = this.keyAfterAt.indexOf(this.lastKeyAfterAt) < 0
  334. this.$store.commit('updateIsNewAt', {
  335. isNewAt: isNewAt,
  336. isNewAtFound: isNewAt ? false : this.group.isNewAtFound
  337. })
  338. if (isNewAt) this.$store.commit('updateGroupSearchList', [])
  339. this.lastKeyAfterAt = this.keyAfterAt
  340. }
  341. },
  342. // 监听右键
  343. handleContextmenu (ev) {
  344. if (!this.copyText) return
  345. this.isShowContextmenu = !this.isShowContextmenu
  346. if (this.isShowContextmenu) {
  347. this.menuTop = ev.offsetY
  348. this.menuLeft = ev.offsetX > 100 ? ev.offsetX - 30 : ev.offsetX
  349. }
  350. },
  351. // 监听粘贴
  352. handlePaste () {
  353. this.isShowContextmenu = false
  354. this.inputMsg += this.copyText
  355. },
  356. // 关闭emoji面板
  357. closeEmojiList () {
  358. this.emojiShow = false
  359. },
  360. fixIOS (event) {
  361. setTimeout(() => {
  362. if (this.$refs.chatInput === document.activeElement) {
  363. document.activeElement.blur()
  364. }
  365. }, 0)
  366. }
  367. }
  368. }
  369. //
  370. export const addPanelSessionMixin = {
  371. methods: {
  372. addPanelSession (serverId, sessionId) {
  373. // 先判断serverId有没有在sessionList
  374. let isInSession = this.sessionList.some(v => {
  375. return v.session_id == sessionId
  376. })
  377. if (!isInSession) {
  378. // 没有就加入一个会话
  379. api.user.getOtherInfo({
  380. target_id: serverId
  381. }).then(({ data }) => {
  382. let userInfo = data.data
  383. let obj = {
  384. cover_photo: userInfo.cover_photo,
  385. is_group: '0',
  386. name: userInfo.nick_name,
  387. session_id: sessionId,
  388. unread: 0
  389. }
  390. this.$store.commit('addSessionItem', obj)
  391. })
  392. }
  393. }
  394. }
  395. }
  396. // 切换语言
  397. export const changeLangMixin = {
  398. data () {
  399. return {
  400. curLang: getUserOpt('lang') || getLanguage()
  401. }
  402. },
  403. methods: {
  404. changeLang () {
  405. let changeLang = this.curLang == 'en' ? 'zh' : 'en'
  406. this.$i18n.locale = changeLang
  407. this.curLang = changeLang
  408. // localStorage.setItem('lang', changeLang)
  409. setUserOpt('lang', changeLang)
  410. }
  411. }
  412. }