import { MessageBox } from 'element-ui' import TWEEN from '@tweenjs/tween.js' import twemoji from 'twemoji' import dayjs from 'dayjs' const timestampInterval = 1e3 * 60 * 3 // 3分钟的间隔时间 const cryptoKey = 'dqWt6twz6JyEy3EZ' export function isDev () { return /^test|\.webdev2\./.test(window.location.host) || window.location.port !== '' } // 错误弹窗 export function showError (msg, title = 'Error') { MessageBox.confirm(msg, title, { center: true, showCancelButton: false, showConfirmButton: false, callback () {} }) } // 确认操作弹窗 export function confirmPopup (msg, title = '提示') { return new Promise((resolve, reject) => { MessageBox.confirm(msg, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }) .then(() => { resolve() }) .catch(() => {}) }) } /** * 判断系统||浏览器中英文 * 对于不支持的浏览器 一律默认为 中文 */ export function getLanguage () { var language = (navigator.language || navigator.browserLanguage).toLowerCase() var locale = 'zh' if (language.indexOf('en') > -1) { locale = 'en' } else { locale = 'zh' } return locale } /** * 获取Url上指定参数 * @param {String} name 参数名 */ export function getUrlParam (name) { var reg = new RegExp('[?&]' + name + '=([^&#?]*)(&|#|$)') var r = window.location.href.match(reg) return r ? r[1] : null } export var Cookie = { setCookie (name, value) { var Days = 7 var exp = new Date() exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000) exp.setTime(exp.getTime() + 60 * 1000) if ( window.location.port === '8080' || /^test-|\.webdev2\./.test(window.location.host) ) { document.cookie = name + '=' + escape(value) + ';expires=' + exp.toGMTString() } else { document.cookie = name + '=' + escape(value) + ';domain=.mee.chat;path=/;expires=' + exp.toGMTString() } }, getCookie (name) { var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)') var arr = document.cookie.match(reg) if (arr) { return unescape(arr[2]) } else { return null } }, delCookie (name) { var str1 = name + '=;domain=.mee.chat;path=/' str1 += ';expires=' + new Date(0).toGMTString() document.cookie = str1 var str2 = name + '=;path=/' str2 += ';expires=' + new Date(0).toGMTString() document.cookie = str2 } } /** * 获取聊天区域高度 * @param {String} msg */ export function getResizeHeight () { let clientHeight = document.documentElement.clientHeight let clientWidth = document.documentElement.clientWidth let topHeight = 61 let botHeight = 181 var chatBoxHeight if (clientHeight < 600) { chatBoxHeight = 600 - topHeight - botHeight } if (clientHeight < 800 || clientWidth < 1000) { chatBoxHeight = clientHeight - topHeight - botHeight } else { chatBoxHeight = clientHeight * 0.8 - topHeight - botHeight } return chatBoxHeight } function rc4 (str, key) { var s = [] var j = 0 var x var res = '' for (var i = 0; i < 256; i++) { s[i] = i } for (i = 0; i < 256; i++) { j = (j + s[i] + key.charCodeAt(i % key.length)) % 256 x = s[i] s[i] = s[j] s[j] = x } i = 0 j = 0 for (var y = 0; y < str.length; y++) { i = (i + 1) % 256 j = (j + s[i]) % 256 x = s[i] s[i] = s[j] s[j] = x res += String.fromCharCode(str.charCodeAt(y) ^ s[(s[i] + s[j]) % 256]) } return res } /** * 加密信息 * @param {String} msg */ export function cryptoMsg (msg) { let result try { result = btoa(rc4(encodeURIComponent(msg), cryptoKey)) } catch { return msg } return result } /** * 解密信息 * @param {String} msg */ export function decryptoMsg (msg) { let result try { result = decodeURIComponent(rc4(atob(msg), cryptoKey)) } catch { return msg } return result } const linkRule = new RegExp( '(https?|ftp|file)://[-a-zA-Z0-9/-_.?#!+%&]+' ) /** * 向数组添加数据 * @param {Array} data */ export function addSomeInArray (data) { let lastTime = null data.forEach(item => { // 添加timeMsg tag if (lastTime === null) { item.timeMsg = false } else { item.timeMsg = parseInt(item.timestamp) - lastTime > timestampInterval } lastTime = parseInt(item.timestamp) addLinkItem(item) }) } /** * 单个添加timeMsg tag * @param {Array} data */ export function addTimeMsgInItem (item, arr) { if (arr.length === 0) { item.timeMsg = true } else { let lastTime = parseInt(arr[arr.length - 1].timestamp) item.timeMsg = parseInt(item.timestamp) - lastTime > timestampInterval } } /** * 判断是否是移动端 */ export function isMobile () { return /Android|webOS|iPhone|iPod|iPad|BlackBerry/i.test(navigator.userAgent) } /** * 获得meechat版本 * */ export function getMeechatType () { if (location.pathname.indexOf('mini') > -1) { return 'mini' } else if (isMobile()) { return 'h5' } else { return 'pc' } } function convertEntity (str) { // :) const entityMap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' } return str.replace(/[&<>'"]/g, function (matched) { return entityMap[matched] }) } /* * 单个添加链接 msg_type = 10 * @param {Array} data */ export function addLinkItem (item) { // 先用实体处理 if (item.msg_type == 0) { item.content = convertEntity(item.content) if (item.content.match(linkRule)) { item.content = item.content.replace(linkRule, a => { return `${a}` }) } item.content = twemoji.parse(item.content, { callback: function (icon, options) { return 'https://w2.meechat.me/emoji/' + icon + '.svg' } }) } } /** * 移除arr2中满足callback的arr1数组元素 * @param {Array} arr1 [{hash:1}] * @param {Array} arr2 [{hash:1},{hash:2}] * @result arr1 => [] */ export function removeItemIfEixt (arr1, arr2, callback) { arr1.forEach((item, index) => { if (arr2.some(item2 => callback(item2) == callback(item))) { arr1.splice(index, 1) } }) } /** * 格式化置顶消息 */ export function formatPinMsg (pinMsg, userId) { pinMsg.name = pinMsg.nick_name pinMsg.content = decryptoMsg(pinMsg.msg) pinMsg.type = pinMsg.from == userId ? 'me' : 'you' pinMsg.avatar = pinMsg.cover_photo || '' pinMsg.userId = pinMsg.from } /** * @param {store} state * @param {Number} createTime */ export function dealErrorMsg (state, createTime) { state.chatList.forEach(item => { if (item.createTime == createTime) { item.fail = true item.loading = false } }) } /** * 如果含有at他人的信息,格式化 * @param {String} msg @heitan@aben 123 * @param {Array} members * @result => {@start[10,9]end}@heitan@aben 123 */ export function encryptAtMsg (msg, members) { let ats = [] members.forEach(user => { let reg = new RegExp(`@${user.nick_name}`) if (reg.test(msg)) { ats.push(user.user_id) } }) if (ats.length) { msg = `{@start[${ats.toString()}]end}${msg}` } return msg } /** * * @param {String} msg * @return {Array|Null} */ export function decryptAtMsg (msg) { let reg = /^{@start\[(.*)\]end}/ let ret = reg.exec(msg) if (ret) { return ret[0].split(',') } } /** *判断是否是at我的信息 */ export function checkAtMe (msg, username) { if (!username) return false let reg = new RegExp(`@${username}`) return reg.test(msg) } export function scrollIntoView (node, offsetTop) { let distance = Math.abs(offsetTop - node.scrollTop) let time = distance > 500 ? 1000 : distance * 2 let tw = new TWEEN.Tween(node) .to({ scrollTop: offsetTop }, time) .easing(TWEEN.Easing.Quadratic.Out) return tw.start() } export function scrollMsgIntoView (node, offsetTop, targetNode, callback) { scrollIntoView(node, offsetTop) .onComplete(() => { targetNode.classList.toggle('active') callback && callback() }) setTimeout(() => { targetNode.classList.toggle('active') }, 3000) } export var noticeManager = { tabTimer: null, // tab切换定时器 askPermission () { return new Promise((resolve, reject) => { if (!('Notification' in window)) { reject(new Error('This browser does not support desktop notification')) } else if (Notification.permission === 'granted') { resolve() } else if (Notification.permission === 'default ') { Notification.requestPermission(function (permission) { // 如果用户同意,就可以向他们发送通知 if (permission === 'granted') { resolve() } else { reject(new Error()) } }) } }) }, showNotification (data) { // 开启全局消息免扰 if (this.getGlobalNotice() == 1) return // 已经打开页面了,就不需要额外通知 let userId = localStorage.getItem('user_id') // 自己发的消息不用通知 if (userId == data.from) return this.askPermission().then(() => { let notification = new Notification(data.name, { body: '你收到了一条消息', icon: `/dist/img/icons/meechat.png` }) // 打开页面 let path if (data.group_id) { path = `/group/${data.group_id}` } else { let sessionId = +data.to > +data.from ? `${data.from}-${data.to}` : `${data.to}-${data.from}` path = `/pm/${sessionId}` } notification.onclick = () => { window.$router.push({ path }) notification.close() window.focus() } setTimeout(() => { notification.close() }, 3500) }) }, changeTitle (num) { // 开启全局消息免扰 // if (this.getGlobalNotice() == 1) return let title = num ? `${num}条新消息 - MeeChat` : `MeeChat` document.title = title if (num) { num = num > 99 ? '99+' : num if (this.tabTimer) clearTimeout(this.tabTimer) } }, getGlobalNotice () { if (window.Notification && Notification.permission === 'granted') { // getUserOpt('mute') // let systemConfig = JSON.parse(localStorage.getItem('systemConfig') || '{}') // let mute = getUserOpt('mute') // return typeof systemConfig.mute === 'undefined' ? 1 : systemConfig.mute let mute = getUserOpt('mute') return mute || 0 } else { return 1 } }, /** * @des 设置通知提醒 * @param type {0:开启,1:关闭} * */ setGlobalNotice (type, that) { // let systemConfig = JSON.parse(localStorage.getItem('systemConfig') || '{}') if (type == 0) { Notification.requestPermission(function (permission) { // 如果用户同意,就可以向他们发送通知 let mute = permission === 'granted' ? 0 : 1 that.openGlobalNotice = mute setUserOpt('mute', mute) }) } else { that.openGlobalNotice = type setUserOpt('mute', type) } } } /** * @des 转换消息时间 * @param {timeStamp} 时间戳 * @param {type} 类型{1:会话列表,2:信息段} */ export function formatMsgTime (timeStamp, type = 1, that) { if (!timeStamp) return '' let lastDate = dayjs().subtract(1, 'days').format('YYYY-MM-DD') // 昨天 let inputDay = dayjs(timeStamp * 1) let inputDate = inputDay.format('YYYY-MM-DD') switch (type) { // 会话列表 case 1: if (inputDate < lastDate) { return inputDay.format('MM-DD') } else if (lastDate == inputDate) { return that.$t('chat.yesterday') } else { return inputDay.format('HH:mm') } // 信息段 case 2: if (inputDate < lastDate) { return inputDay.format('MM-DD HH:mm') } else if (lastDate == inputDate) { return that.$t('chat.yesterday') + ` ${inputDay.format('HH:mm')}` } else { return inputDay.format('HH:mm') } } } /** * @des 跳去h5登录页 * */ export function toLoginPage () { let url = encodeURIComponent(location.hash.replace('#', '')) location.replace(`${location.origin + location.pathname}#/login?from=${url}`) // rounter.replace(`/login?from=${url}`) } /** * @des 将图片地址转成base64 */ export function getBase64 (imgUrl, callback) { window.URL = window.URL || window.webkitURL var xhr = new XMLHttpRequest() xhr.open('get', imgUrl, true) // 至关重要 xhr.responseType = 'blob' xhr.onload = function () { if (this.status == 200) { // 得到一个blob对象 var blob = this.response // 至关重要 let oFileReader = new FileReader() oFileReader.onloadend = function (e) { let base64 = e.target.result callback && callback(base64) } oFileReader.readAsDataURL(blob) } } xhr.send() } /** * @des 图片懒加载 * @param {el} wrap 图片容器 * @param {HTMLCollection} imageArr 图片列表 * @param {String} derection 整体列表滑动方向['down','up'] */ export function lazyloadImage ({ wrap, imageArr, derection = 'down' }) { if (!imageArr || imageArr.length <= 0) return let imageLen = imageArr.length let listScrollTop = wrap.scrollTop let listClientHeight = wrap.clientHeight switch (derection) { case 'down': for (let i = 0; i < imageLen; i++) { let item = imageArr[i] let originUrl = item.getAttribute('originurl') let url = item.getAttribute('src') if (item.offsetTop - listScrollTop <= listClientHeight) { if (originUrl && originUrl != url) item.setAttribute('src', originUrl) } else { return i >= 1 ? i - 1 : 0 } } break case 'up': for (let i = imageLen - 1; i >= 0; i--) { let item = imageArr[i] let originUrl = item.getAttribute('originurl') let url = item.getAttribute('src') let top = item.getBoundingClientRect().top if (top >= -100 && top < listClientHeight + 300) { if (originUrl && originUrl != url) { // 消息图片 if (item.getAttribute('class').match('img-msg')) { if (!/^data:image/.test(originUrl)) { let img = new Image() img.src = originUrl img.onload = () => { item.nextSibling.classList.add('is-hide') item.setAttribute('src', originUrl) } img.onerror = () => { item.nextSibling.classList.add('is-error') } } } else { item.setAttribute('src', originUrl) } } } } break } } /** * @des 获取头像背景色 * @param {*} str 房间号 * @param {*} userId 当前用户id */ export function getAvatarBgColor (str, userId) { str += '' if (str.match('-')) { let num = 0 str.split('-').forEach(e => { if (e !== userId) { num = e % 9 } }) return num + '' } else { return str % 9 + '' } } /** * @des 打开一个空白窗口 * @param {String} url 链接 * @param {Number} iWidth 弹出窗口的宽度 * @param {Number} iHeight 弹出窗口的高度 */ export function openBlankWindow (url = '', iWidth = 500, iHeight = 600) { var iTop = (window.screen.availHeight - 30 - iHeight) / 2 // 获得窗口的垂直位置; var iLeft = (window.screen.availWidth - 10 - iWidth) / 2 // 获得窗口的水平位置; var winHandler = window.open(url, '_blank', 'height=' + iHeight + ', width=' + iWidth + ', top=' + iTop + ', left=' + iLeft + ', toolbar=no, menubar=no, scrollbars=no, resizable=no,location=no, status=no') return winHandler } /** * @des 本地存储用户操作 * [ * {'loginType':'登录类型'}, * {'isChangeIndentity':'是否修改scatter身份1,0'}, * {'eosType':'eos操作类型[eos,meechat]'}, * {'lang':'语言'} * ] */ export function getUserOpt (key) { let userOpt = localStorage.getItem('userOpt') || '{}' userOpt = JSON.parse(userOpt) return userOpt[key] || '' } export function setUserOpt (key, val) { let userOpt = localStorage.getItem('userOpt') || '{}' userOpt = JSON.parse(userOpt) userOpt[key] = val localStorage.setItem('userOpt', JSON.stringify(userOpt)) } // 针对input失焦将页面置顶 export function mobileInputBlur () { if (top != self && isMobile()) { window.postMessager.send({ action: 'meechat:inputBlur' }) } } // 获取登录方式 export function getShowLoginTyps () { let noMini = getMeechatType() != 'mini' let loginTypes = getUrlParam('loginTypes') let showLoginTypes = { eos: true, meetone: noMini, eth: noMini, tron: noMini, telegram: true } if (loginTypes) { let types = loginTypes.split(',') for (let i in showLoginTypes) { showLoginTypes[i] = types.some(item => item == i) } } return showLoginTypes }