This commit is contained in:
2022-12-28 10:08:51 +08:00
parent 0fa93d545e
commit 3881370b6e
151 changed files with 17044 additions and 0 deletions

67
im/src/utils/auth.js Normal file
View File

@@ -0,0 +1,67 @@
import JsBase64 from 'js-base64'
const USER_TOKEN = 'LILI-TOKEN'
const USER_INFO = 'LILI-USERINFO'
const USER_SETTING = 'LILI-SETTING'
/**
* 设置用户授权token
*
* @param {String} token
*/
export function setToken(token) {
return localStorage.setItem(
USER_TOKEN,
token
)
}
/**
* 获取授权token
*/
export function getToken() {
return localStorage.getItem(USER_TOKEN)
}
/**
* 设置用户信息
*
* @param {Object} data
*/
export function setUserInfo(data) {
localStorage.setItem(USER_INFO, JsBase64.Base64.encode(JSON.stringify(data)))
}
/**
* 获取用户信息
*/
export function getUserInfo() {
const data = JsBase64.Base64.decode(localStorage.getItem(USER_INFO) || '')
return data ? JSON.parse(data) : {}
}
/**
* 获取用户本地缓存的设置信息
*/
export function getUserSettingCache() {
const data = localStorage.getItem(USER_SETTING)
return data ? JSON.parse(data) : {}
}
/**
* 用户设置保存到浏览器缓存中
*
* @param {Object} state 用户设置相关信息
*/
export function setUserSettingCache(state) {
localStorage.setItem(USER_SETTING, JSON.stringify(state))
}
/**
* 删除用户相关缓存信息
*/
export function removeAll() {
localStorage.removeItem(USER_TOKEN)
localStorage.removeItem(USER_INFO)
localStorage.removeItem(USER_SETTING)
}

144
im/src/utils/date.js Normal file
View File

@@ -0,0 +1,144 @@
/**
* 对Date的扩展将 Date 转化为指定格式的String。
*
* 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符,
* 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)。
*
* 【示例】:
* formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss.S') ==> 2006-07-02 08:09:04.423
* formatDate(new Date(), 'yyyy-M-d h:m:s.S') ==> 2006-7-2 8:9:4.18
* formatDate(new Date(), 'hh:mm:ss.S') ==> 08:09:04.423
*/
export function formatDate(date, fmt) {
const o = {
'M+': date.getMonth() + 1, //月份
'd+': date.getDate(), //日
'h+': date.getHours(), //小时
'm+': date.getMinutes(), //分
's+': date.getSeconds(), //秒
'q+': Math.floor((date.getMonth() + 3) / 3), //季度
S: date.getMilliseconds(), //毫秒
}
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(
RegExp.$1,
(date.getFullYear() + '').substr(4 - RegExp.$1.length)
)
}
for (let k in o) {
if (new RegExp('(' + k + ')').test(fmt)) {
let value =
RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)
fmt = fmt.replace(RegExp.$1, value)
}
}
return fmt
}
/**
* 仿照微信中的消息时间显示逻辑,将时间戳(单位:毫秒)转换为友好的显示格式.
*
* 17天之内的日期显示逻辑是今天、昨天(-1d)、前天(-2d)、星期只显示总计7天之内的星期数即<=-4d
* 27天之外即>7天的逻辑直接显示完整日期时间。
*
* @param {[long]} timestamp 时间戳单位毫秒形如1550789954260
* @param {boolean} mustIncludeTime true表示输出的格式里一定会包含“时间:分钟”
* ,否则不包含(参考微信,不包含时分的情况,用于首页“消息”中显示时)
*
* @return {string} 输出格式形如“刚刚”、“10:30”、“昨天 12:04”、“前天 20:51”、“星期二”、“2019/2/21 12:09”等形式
*/
export function formatDateShort(timestamp, mustIncludeTime) {
// 当前时间
let currentDate = new Date()
// 目标判断时间
let srcDate = new Date(parseInt(timestamp))
let currentYear = currentDate.getFullYear()
let currentMonth = currentDate.getMonth() + 1
let currentDateD = currentDate.getDate()
let srcYear = srcDate.getFullYear()
let srcMonth = srcDate.getMonth() + 1
let srcDateD = srcDate.getDate()
let ret = ''
// 要额外显示的时间分钟
let timeExtraStr = mustIncludeTime ? ' ' + formatDate(srcDate, 'hh:mm') : ''
// 当年
if (currentYear == srcYear) {
let currentTimestamp = currentDate.getTime()
let srcTimestamp = timestamp
// 相差时间(单位:毫秒)
let deltaTime = currentTimestamp - srcTimestamp
// 当天(月份和日期一致才是)
if (currentMonth == srcMonth && currentDateD == srcDateD) {
// 时间相差60秒以内
if (deltaTime < 60 * 1000) ret = '刚刚'
// 否则当天其它时间段的,直接显示“时:分”的形式
else ret = formatDate(srcDate, 'hh:mm')
}
// 当年 && 当天之外的时间(即昨天及以前的时间)
else {
// 昨天(以“现在”的时候为基准-1天
let yesterdayDate = new Date()
yesterdayDate.setDate(yesterdayDate.getDate() - 1)
// 前天(以“现在”的时候为基准-2天
let beforeYesterdayDate = new Date()
beforeYesterdayDate.setDate(beforeYesterdayDate.getDate() - 2)
// 用目标日期的“月”和“天”跟上方计算出来的“昨天”进行比较,是最为准确的(如果用时间戳差值
// 的形式是不准确的比如现在时刻是2019年02月22日1:00、而srcDate是2019年02月21日23:00
// 这两者间只相差2小时直接用“deltaTime/(3600 * 1000)” > 24小时来判断是否昨天就完全是扯蛋的逻辑了
if (
srcMonth == yesterdayDate.getMonth() + 1 &&
srcDateD == yesterdayDate.getDate()
)
ret = '昨天' + timeExtraStr
// -1d
// “前天”判断逻辑同上
else if (
srcMonth == beforeYesterdayDate.getMonth() + 1 &&
srcDateD == beforeYesterdayDate.getDate()
)
ret = '前天' + timeExtraStr
// -2d
else {
// 跟当前时间相差的小时数
let deltaHour = deltaTime / (3600 * 1000)
// 如果小于或等 7*24小时就显示星期几
if (deltaHour <= 7 * 24) {
let weekday = new Array(7)
weekday[0] = '星期日'
weekday[1] = '星期一'
weekday[2] = '星期二'
weekday[3] = '星期三'
weekday[4] = '星期四'
weekday[5] = '星期五'
weekday[6] = '星期六'
// 取出当前是星期几
let weedayDesc = weekday[srcDate.getDay()]
ret = weedayDesc + timeExtraStr
}
// 否则直接显示完整日期时间
else {
ret = formatDate(srcDate, 'yyyy/M/d') + timeExtraStr
}
}
}
}
// 往年
else {
ret = formatDate(srcDate, 'yyyy/M/d') + timeExtraStr
}
return ret
}

180
im/src/utils/editor.js Normal file
View File

@@ -0,0 +1,180 @@
/**
* 遍历对象
*
* @param {Object} obj
* @param {Object} fn
*/
export function objForEach(obj, fn) {
let key = void 0,
result = void 0
for (key in obj) {
if (obj.hasOwnProperty(key)) {
result = fn.call(obj, key, obj[key])
if (result === false) {
break
}
}
}
}
/**
* 遍历类数组
*
* @param {Object} fakeArr
* @param {Object} fn
*/
export function arrForEach(fakeArr, fn) {
let i = void 0,
item = void 0,
result = void 0
let length = fakeArr.length || 0
for (i = 0; i < length; i++) {
item = fakeArr[i]
result = fn.call(fakeArr, item, i)
if (result === false) {
break
}
}
}
/**
* 替换 html 特殊字符
*
* @param {Object} html
*/
export function replaceHtmlSymbol(html) {
if (html == null) {
return ''
}
return html
.replace(/</gm, '&lt;')
.replace(/>/gm, '&gt;')
.replace(/"/gm, '&quot;')
.replace(/(\r\n|\r|\n)/g, '<br/>')
}
/**
* 获取粘贴的纯文本
*
* @param {Object} e
*/
export function getPasteText(e) {
let clipboardData =
e.clipboardData || (e.originalEvent && e.originalEvent.clipboardData)
let pasteText = void 0
if (clipboardData == null) {
pasteText = window.clipboardData && window.clipboardData.getData('text')
} else {
pasteText = clipboardData.getData('text/plain')
}
return replaceHtmlSymbol(pasteText)
}
/**
* 获取粘贴的html
*
* @param {Object} e
* @param {Object} filterStyle
* @param {Object} ignoreImg
*/
export function getPasteHtml(e, filterStyle, ignoreImg) {
let clipboardData =
e.clipboardData || (e.originalEvent && e.originalEvent.clipboardData)
let pasteText = void 0,
pasteHtml = void 0
if (clipboardData == null) {
pasteText = window.clipboardData && window.clipboardData.getData('text')
} else {
pasteText = clipboardData.getData('text/plain')
pasteHtml = clipboardData.getData('text/html')
}
if (!pasteHtml && pasteText) {
pasteHtml = '<p>' + replaceHtmlSymbol(pasteText) + '</p>'
}
if (!pasteHtml) {
return
}
// 过滤word中状态过来的无用字符
let docSplitHtml = pasteHtml.split('</html>')
if (docSplitHtml.length === 2) {
pasteHtml = docSplitHtml[0]
}
// 过滤无用标签
pasteHtml = pasteHtml.replace(/<(meta|script|link).+?>/gim, '')
// 去掉注释
pasteHtml = pasteHtml.replace(/<!--.*?-->/gm, '')
// 过滤 data-xxx 属性
pasteHtml = pasteHtml.replace(/\s?data-.+?=('|").+?('|")/gim, '')
if (ignoreImg) {
// 忽略图片
pasteHtml = pasteHtml.replace(/<img.+?>/gim, '')
}
if (filterStyle) {
// 过滤样式
pasteHtml = pasteHtml.replace(/\s?(class|style)=('|").*?('|")/gim, '')
} else {
// 保留样式
pasteHtml = pasteHtml.replace(/\s?class=('|").*?('|")/gim, '')
}
return pasteHtml
}
/**
* 获取粘贴的图片文件
*
* @param {Object} e
*/
export function getPasteImgs(e) {
let result = []
let txt = getPasteText(e)
if (txt) {
// 有文字,就忽略图片
return result
}
let clipboardData =
e.clipboardData || (e.originalEvent && e.originalEvent.clipboardData) || {}
let items = clipboardData.items
if (!items) {
return result
}
objForEach(items, function(key, value) {
let type = value.type
if (/image/i.test(type)) {
result.push(value.getAsFile())
}
})
return result
}
/**
* 获取拖拽的图片
*
* @param {Object} e
*/
export function getDragPasteImg(e) {
let result = []
let dataTransfer =
e.dataTransfer || (e.originalEvent && e.originalEvent.dataTransfer) || {}
let items = dataTransfer.items
if (!items) {
return result
}
objForEach(items, function(key, value) {
let type = value.type
if (/image/i.test(type)) {
result.push(value.getAsFile())
}
})
return result
}

144
im/src/utils/emojis.js Normal file
View File

@@ -0,0 +1,144 @@
/**
* 动态表情
*/
const emojis = {
"[微笑]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/0.gif'>",
"[撇嘴]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/1.gif'>",
"[色]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/2.gif'>",
"[发呆]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/3.gif'>",
"[得意]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/4.gif'>",
"[流泪]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/5.gif'>",
"[害羞]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/6.gif'>",
"[闭嘴]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/7.gif'>",
"[睡]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/8.gif'>",
"[大哭]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/9.gif'>",
"[尴尬]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/10.gif'>",
"[发怒]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/11.gif'>",
"[调皮]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/12.gif'>",
"[呲牙]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/13.gif'>",
"[惊讶]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/14.gif'>",
"[难过]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/15.gif'>",
"[酷]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/16.gif'>",
"[冷汗]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/17.gif'>",
"[抓狂]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/18.gif'>",
"[吐]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/19.gif'>",
"[偷笑]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/20.gif'>",
"[可爱]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/21.gif'>",
"[白眼]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/22.gif'>",
"[傲慢]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/23.gif'>",
"[饥饿]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/24.gif'>",
"[困]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/25.gif'>",
"[惊恐]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/26.gif'>",
"[流汗]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/27.gif'>",
"[憨笑]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/28.gif'>",
"[大兵]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/29.gif'>",
"[奋斗]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/30.gif'>",
"[咒骂]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/31.gif'>",
"[疑问]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/32.gif'>",
"[嘘]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/33.gif'>",
"[晕]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/34.gif'>",
"[折磨]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/35.gif'>",
"[衰]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/36.gif'>",
"[骷髅]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/37.gif'>",
"[敲打]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/38.gif'>",
"[再见]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/39.gif'>",
"[擦汗]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/40.gif'>",
"[抠鼻]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/41.gif'>",
"[鼓掌]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/42.gif'>",
"[糗大了]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/43.gif'>",
"[坏笑]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/44.gif'>",
"[左哼哼]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/45.gif'>",
"[右哼哼]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/46.gif'>",
"[哈欠]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/47.gif'>",
"[鄙视]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/48.gif'>",
"[委屈]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/49.gif'>",
"[快哭了]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/50.gif'>",
"[阴险]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/51.gif'>",
"[亲亲]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/52.gif'>",
"[吓]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/53.gif'>",
"[可怜]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/54.gif'>",
"[菜刀]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/55.gif'>",
"[西瓜]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/56.gif'>",
"[啤酒]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/57.gif'>",
"[篮球]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/58.gif'>",
"[乒乓]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/59.gif'>",
"[咖啡]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/60.gif'>",
"[饭]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/61.gif'>",
"[猪头]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/62.gif'>",
"[玫瑰]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/63.gif'>",
"[凋谢]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/64.gif'>",
"[示爱]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/65.gif'>",
"[爱心]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/66.gif'>",
"[心碎]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/67.gif'>",
"[蛋糕]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/68.gif'>",
"[闪电]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/69.gif'>",
"[炸弹]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/70.gif'>",
"[刀]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/71.gif'>",
"[足球]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/72.gif'>",
"[瓢虫]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/73.gif'>",
"[便便]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/74.gif'>",
"[月亮]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/75.gif'>",
"[太阳]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/76.gif'>",
"[礼物]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/77.gif'>",
"[拥抱]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/78.gif'>",
"[强]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/79.gif'>",
"[弱]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/80.gif'>",
"[握手]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/81.gif'>",
"[胜利]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/82.gif'>",
"[抱拳]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/83.gif'>",
"[勾引]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/84.gif'>",
"[拳头]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/85.gif'>",
"[差劲]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/86.gif'>",
"[爱你]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/87.gif'>",
"[NO]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/88.gif'>",
"[OK]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/89.gif'>",
"[爱情]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/90.gif'>",
"[飞吻]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/91.gif'>",
"[跳跳]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/92.gif'>",
"[发抖]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/93.gif'>",
"[怄火]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/94.gif'>",
"[转圈]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/95.gif'>",
"[磕头]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/96.gif'>",
"[回头]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/97.gif'>",
"[跳绳]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/98.gif'>",
"[挥手]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/99.gif'>",
"[激动]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/100.gif'>",
"[街舞]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/101.gif'>",
"[献吻]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/102.gif'>",
"[左太极]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/103.gif'>",
"[右太极]": "<img src='https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/104.gif'>",
};
/**
* 符号表情
*/
const symbol = [
"😠", "😩", "😲", "😞", "😵", "😰", "😒", "😍", "😤", "😜", "😝", "😋", "😘", "😚", "😷",
"😳", "😃", "😅", "😆", "😁", "😂", "😊", "☺", "😄", "😢",
"😭", "😨", "😣", "😡", "😌", "😖", "😔", "😱", "😪", "😏", "😓", "😥", "😫", "😉",
"✊", "✋", "✌", "👊", "👍", "☝", "👆", "👇", "👈", "👉",
"👋", "👏", "👌", "👎"
];
const emojisKeys = Object.keys(emojis);
export const emojiList = {
symbol,
emojis
}
const regEmoji = emojisKeys.map((value) => '|\\' + value).join('').replace('|', '')
/**
* 替换表情文字
*
* @param {String} content 需要替换的字符串
*/
export function textReplaceEmoji(content) {
if(!content){
return ""
}
return content.replace(new RegExp(`(${regEmoji})`, 'gi'), ($0, $1) => {
return emojis[$1];
});
}

524
im/src/utils/functions.js Normal file
View File

@@ -0,0 +1,524 @@
/** 公共方法类 */
import { getToken } from "@/utils/auth";
import config from "@/config/config";
/**
* 人性化时间显示
*
* @param {Object} datetime
*/
export function formatTime(datetime) {
if (datetime == null) return "";
datetime = datetime.replace(/-/g, "/");
let time = new Date();
let outTime = new Date(datetime);
if (/^[1-9]\d*$/.test(datetime)) {
outTime = new Date(parseInt(datetime) * 1000);
}
if (
time.getTime() < outTime.getTime() ||
time.getFullYear() != outTime.getFullYear()
) {
return parseTime(outTime, "{y}-{m}-{d} {h}:{i}");
}
if (time.getMonth() != outTime.getMonth()) {
return parseTime(outTime, "{m}-{d} {h}:{i}");
}
if (time.getDate() != outTime.getDate()) {
let day = outTime.getDate() - time.getDate();
if (day == -1) {
return parseTime(outTime, "昨天 {h}:{i}");
}
if (day == -2) {
return parseTime(outTime, "前天 {h}:{i}");
}
return parseTime(outTime, "{m}-{d} {h}:{i}");
}
if (time.getHours() != outTime.getHours()) {
return parseTime(outTime, "{h}:{i}");
}
let minutes = outTime.getMinutes() - time.getMinutes();
if (minutes == 0) {
return "刚刚";
}
minutes = Math.abs(minutes);
return `${minutes}分钟前`;
}
/**
* 格式化文件大小
*
* @param {String} value 文件大小(字节)
*/
export function formatSize(value) {
if (null == value || value == "") {
return "0";
}
let unitArr = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
let index = 0;
let srcsize = parseFloat(value);
index = Math.floor(Math.log(srcsize) / Math.log(1024));
let size = srcsize / Math.pow(1024, index);
size = size.toFixed(2); //保留的小数位数
return size + unitArr[index];
}
/**
* 获取文件后缀名
*
* @param {String} fileName
*/
export function getFileExt(fileName) {
let ext = fileName.split(".");
ext = ext[ext.length - 1]; // 获取文件后缀名
return ext;
}
/**
* 根据图片url下载图片
* @param {String} imgsrc
* @param {String} name
*/
export function downloadIamge(imgsrc, name) {
//下载图片地址和图片名
let image = new Image();
// 解决跨域 Canvas 污染问题
image.setAttribute("crossOrigin", "anonymous");
image.onload = function () {
let canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
let context = canvas.getContext("2d");
context.drawImage(image, 0, 0, image.width, image.height);
let url = canvas.toDataURL("image/png"); //得到图片的base64编码数据
let a = document.createElement("a"); // 生成一个a元素
let event = new MouseEvent("click"); // 创建一个单击事件
a.download = name || "photo"; // 设置图片名称
a.href = url; // 将生成的URL设置为a.href属性
a.dispatchEvent(event); // 触发a的单击事件
};
image.src = imgsrc;
}
/**
* 通过图片url获取图片大小
*
* @param {String} imgsrc 例如图片名: D8x5f13a53dbc4b9_350x345.png
*/
export function getImageInfo(imgsrc) {
let data = {
width: 0,
height: 0,
};
let arr = imgsrc.split("_");
if (arr.length == 1) return data;
let info = arr[arr.length - 1].match(/\d+x\d+/g);
if (info == null) return data;
info = info[0].split("x");
return {
width: parseInt(info[0]),
height: parseInt(info[1]),
};
}
/**
* 文件下载方法
*
* @param {Number} cr_id
*/
export function download(cr_id) {
let api = config.BASE_API_URL;
let token = getToken();
try {
let link = document.createElement("a");
link.href = `${api}/download/user-chat-file?cr_id=${cr_id}&token=${token}`;
link.click();
} catch (e) {}
}
/**
* 时间格式化方法
*
* @param {(Object|string|number)} time
* @param {String} cFormat
* @returns {String | null}
*/
export function parseTime(time, cFormat) {
if (arguments.length === 0) {
return null;
}
let date;
const format = cFormat || "{y}-{m}-{d} {h}:{i}:{s}";
if (typeof time === "object") {
date = time;
} else {
if (typeof time === "string" && /^[0-9]+$/.test(time)) {
time = parseInt(time);
}
if (typeof time === "number" && time.toString().length === 10) {
time = time * 1000;
console.log("时间判断为number");
}
date = new Date(time.replace(/-/g, "/"));
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay(),
};
const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
const value = formatObj[key];
// Note: getDay() returns 0 on Sunday
if (key === "a") {
return ["日", "一", "二", "三", "四", "五", "六"][value];
}
return value.toString().padStart(2, "0");
});
return time_str;
}
/**
* 去除字符串控制
*
* @param {String} str
*/
export function trim(str, type = null) {
if (type) {
return str.replace(/(^\s*)|(\s*$)/g, "");
} else if (type == "l") {
return str.replace(/(^\s*)/g, "");
} else {
return str.replace(/(\s*$)/g, "");
}
}
/**
* 解析url中参数
*
* @param {String} url
* @returns {Object}
*/
export function param2Obj(url) {
const search = url.split("?")[1];
if (!search) return {};
return JSON.parse(
'{"' +
decodeURIComponent(search)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"')
.replace(/\+/g, " ") +
'"}'
);
}
/**
* @param {Object} json
* @returns {Array}
*/
export function param(json) {
if (!json) return "";
return cleanArray(
Object.keys(json).map((key) => {
if (json[key] === undefined) return "";
return encodeURIComponent(key) + "=" + encodeURIComponent(json[key]);
})
).join("&");
}
/**
* @param {Array} actual
* @returns {Array}
*/
export function cleanArray(actual) {
const newArray = [];
for (let i = 0; i < actual.length; i++) {
if (actual[i]) {
newArray.push(actual[i]);
}
}
return newArray;
}
/**
* @param {HTMLElement} element
* @param {String} className
*/
export function toggleClass(element, className) {
if (!element || !className) {
return;
}
let classString = element.className;
let nameIndex = classString.indexOf(className);
if (nameIndex === -1) {
classString += "" + className;
} else {
classString =
classString.substr(0, nameIndex) +
classString.substr(nameIndex + className.length);
}
element.className = classString;
}
/**
* Check if an element has a class
*
* @param {HTMLElement} elm
* @param {String} cls
* @returns {Boolean}
*/
export function hasClass(ele, cls) {
return !!ele.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)"));
}
/**
* Add class to element
*
* @param {HTMLElement} elm
* @param {String} cls
*/
export function addClass(ele, cls) {
if (!hasClass(ele, cls)) ele.className += " " + cls;
}
/**
* Remove class from element
*
* @param {HTMLElement} elm
* @param {String} cls
*/
export function removeClass(ele, cls) {
if (hasClass(ele, cls)) {
const reg = new RegExp("(\\s|^)" + cls + "(\\s|$)");
ele.className = ele.className.replace(reg, " ");
}
}
/**
* 通过图片Url获取图片等比例缩放的宽度和高度信息
*
* @param {String} src
* @param {Number} width
*/
export function imgZoom(src, width = 200) {
const info = getImageInfo(src);
if (info.width < width) {
return {
width: `${info.width}px`,
height: `${info.height}px`,
};
}
return {
width: width + "px",
height: parseInt(info.height / (info.width / width)) + "px",
};
}
/**
* 获取浏览器光标选中内容
*
* @export
* @returns
*/
export function getSelection() {
return window.getSelection
? window.getSelection().toString()
: document.selection.createRange().text;
}
/**
* 剪贴板复制功能
*
* @param {String} value 复制内容
* @param {Function} callback 复制成功回调方法
*/
export const copyTextToClipboard = (value, callback) => {
let textArea = document.createElement("textarea");
textArea.style.background = "transparent";
textArea.value = value;
document.body.appendChild(textArea);
textArea.select();
try {
document.execCommand("copy");
if (callback) callback();
} catch (err) {
this.$message.error("Oops, unable to copy");
}
document.body.removeChild(textArea);
};
/**
* 隐藏用户手机号中间四位
*
* @param {String} phone 手机号
*/
export function hidePhone(phone) {
return phone.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2");
}
/**
* 人性化显示时间
*
* @param {Object} datetime
*/
export function beautifyTime(datetime = "") {
if (datetime == null) {
return "";
}
datetime = datetime.replace(/-/g, "/");
let time = new Date();
let outTime = new Date(datetime);
if (/^[1-9]\d*$/.test(datetime)) {
outTime = new Date(parseInt(datetime) * 1000);
}
if (time.getTime() < outTime.getTime()) {
return parseTime(outTime, "{y}/{m}/{d}");
}
if (time.getFullYear() != outTime.getFullYear()) {
return parseTime(outTime, "{y}/{m}/{d}");
}
if (time.getMonth() != outTime.getMonth()) {
return parseTime(outTime, "{m}/{d}");
}
if (time.getDate() != outTime.getDate()) {
let day = outTime.getDate() - time.getDate();
if (day == -1) {
return parseTime(outTime, "昨天 {h}:{i}");
}
if (day == -2) {
return parseTime(outTime, "前天 {h}:{i}");
}
return parseTime(outTime, "{m}-{d}");
}
if (time.getHours() != outTime.getHours()) {
return parseTime(outTime, "{h}:{i}");
}
let minutes = outTime.getMinutes() - time.getMinutes();
if (minutes == 0) {
return "刚刚";
}
minutes = Math.abs(minutes);
return `${minutes}分钟前`;
}
export function getSort(fn) {
return function (a, b) {
let ret = 0;
if (fn.call(this, a, b)) {
ret = -1;
} else if (fn.call(this, b, a)) {
ret = 1;
}
return ret;
};
}
/**
* 批量排序
*
* @param {*} arr
*/
export function getMutipSort(arr) {
return function (a, b) {
let tmp;
let i = 0;
do {
tmp = arr[i++](a, b);
} while (tmp == 0 && i < arr.length);
return tmp;
};
}
/**
* Url 替换超链接
*
* @param {String} text 文本
* @param {String} color 超链接颜色
*/
export function textReplaceLink(text, color = "#409eff") {
let exp =
/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi;
return text.replace(
exp,
`<a href='$1' target="_blank" style="color:${color};text-decoration: revert;">$1</a >`
);
}
/**
* 防抖
*
* @param {*} func
* @param {*} wait
* @param {*} immediate
* @returns
*/
export function debounce(func, wait, immediate) {
let timeout;
return function () {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout); // timeout 不为null
if (immediate) {
let callNow = !timeout; // 第一次会立即执行,以后只有事件执行后才会再次触发
timeout = setTimeout(function () {
timeout = null;
}, wait);
if (callNow) func.apply(context, args);
} else {
timeout = setTimeout(function () {
func.apply(context, args);
}, wait);
}
};
}

120
im/src/utils/request.js Normal file
View File

@@ -0,0 +1,120 @@
import axios from "axios";
import config from "@/config/config";
import { getToken, removeAll } from "@/utils/auth";
import { Notification } from "element-ui";
import qs from "qs";
// 创建 axios 实例
const request = axios.create({
// API 请求的默认前缀
baseURL: config.BASE_API_URL,
// 请求超时时间
timeout: 20000,
});
/**
* 异常拦截处理器
*
* @param {*} error
*/
const errorHandler = (error) => {
// 判断是否是响应错误信息
if (error.response) {
if (error.response.status == 401) {
removeAll();
location.reload();
} else {
Notification({
message: "网络异常,请稍后再试...",
position: "top-right",
});
}
}
return Promise.reject(error);
};
// 请求拦截器
request.interceptors.request.use((config) => {
const token = getToken();
if (token) {
config.headers["accessToken"] = `${token}`;
return config;
}
}, errorHandler);
// 响应拦截器
request.interceptors.response.use((response) => {
return response.data;
}, errorHandler);
/**
* GET 请求
*
* @param {String} url
* @param {Object} data
* @param {Object} options
* @returns {Promise<any>}
*/
export const get = (url, data = {}, options = {}) => {
return request({
url,
params: data,
method: "get",
...options,
});
};
/**
* POST 请求
*
* @param {String} url
* @param {Object} data
* @param {Object} options
* @returns {Promise<any>}
*/
export const post = (url, data = {}, options = {}) => {
return request({
url,
method: "post",
data: qs.stringify(data),
...options,
headers: { "Content-Type": "application/x-www-form-urlencoded" },
});
};
/**
* del 请求
*
* @param {String} url
* @param {Object} data
* @param {Object} options
* @returns {Promise<any>}
*/
export const del = (url, data = {}, options = {}) => {
return request({
url,
method: "delete",
data: data,
...options,
});
};
/**
* 上传文件 POST 请求
*
* @param {String} url
* @param {Object} data
* @param {Object} options
* @returns {Promise<any>}
*/
export const upload = (url, data = {}, options = {}) => {
return request({
url: config.BASE_COMMON + url,
method: "post",
data: data,
...options,
});
};

88
im/src/utils/talk.js Normal file
View File

@@ -0,0 +1,88 @@
import store from "@/store";
import router from "@/router";
import { parseTime } from "@/utils/functions";
import { ServeCreateTalkList } from "@/api/chat";
const KEY_INDEX_NAME = "send_message_index_name";
/**
* 通过对话索引查找对话列表下标
*
* @param {String} index_name
*/
export function findTalkIndex(index_name) {
return store.state.talks.items.findIndex(
(item) => item.index_name == index_name
);
}
/**
* 通过对话索引查找对话列表
*
* @param {String} index_name
*/
export function findTalk(index_name) {
return store.state.talks.items.find((item) => item.index_name == index_name);
}
/**
* 格式化聊天对话列表数据
*
* @param {Object} params
*/
export function formatTalkItem(params) {
console.log(params);
let options = {
id: "",
disable: false,
face: "",
lastTalkTime: "",
name: "",
top: false,
unread: 0, //未读消息
is_online: 0, //是否在线
draft_text: "", //草稿
msg_text: "", //存储的消息
userId: "",
};
Object.assign(options, params);
options.index_name = `${options.talk_type}_${options.receiver_id}`;
return options;
}
/**
* 打开指定对话窗口
*
* @param {Integer} talk_type 对话类型[1:私聊;2:群聊;]
* @param {Integer} receiver_id 接收者ID
*/
export function toTalk(talk_type, receiver_id) {
ServeCreateTalkList(receiver_id).then(({ code, data }) => {
console.log("ServeCreateTalkList", data);
if (code == 200) {
sessionStorage.setItem(KEY_INDEX_NAME, `${talk_type}_${receiver_id}`);
router.push({
path: "/message",
query: {
v: new Date().getTime(),
},
});
}
});
}
/**
* 获取需要打开的对话索引值
*
* @returns
*/
export function getCacheIndexName() {
let index_name = sessionStorage.getItem(KEY_INDEX_NAME);
if (index_name) {
sessionStorage.removeItem(KEY_INDEX_NAME);
}
return index_name;
}

54
im/src/utils/validate.js Normal file
View File

@@ -0,0 +1,54 @@
/**
* 检测是否是字邮箱地址
*
* @param {String} value
*/
export const isEmail = value => {
return /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((.[a-zA-Z0-9_-]{2,3}){1,2})$/.test(
value
)
}
/**
* 检测是否是手机号
*
* @param {String} value
*/
export const isMobile = value => {
return /^1[0-9]{10}$/.test(value)
}
/**
* 检测是否为url
*
* @param {String} value
*/
export const isURL = value => {
return /^http[s]?:\/\/.*/.test(value)
}
/**
* 检测是否为数字类型
*
* @param {*} value
*/
export const isNumber = value => {
return Object.prototype.toString.call(value).slice(8, -1) === 'Number'
}
/**
* 检测是否为 Booleanl 类型
*
* @param {*} value
*/
export const isBoolean = value => {
return Object.prototype.toString.call(value).slice(8, -1) === 'Boolean'
}
/**
* 检测是非是微信浏览器
*/
export const isWeiXin = () => {
let ua = navigator.userAgent.toLowerCase()
return ua.match(/microMessenger/i) == 'micromessenger'
}