9.16下午更新 包含im

This commit is contained in:
zoujiandong
2025-09-16 16:19:29 +08:00
parent 215b3298d0
commit b884a8f6fc
127 changed files with 59972 additions and 182 deletions
+79
View File
@@ -0,0 +1,79 @@
import { V2NIMConst, NIM } from '../dist/nim.js'
let appConfig = {
// 用户的appkey
// 用于在web demo中注册账号异步请求demo 服务器中使用
test: {
appkey: 'fe416640c8e8a72734219e1847ad2547',
postUrl: 'https://apptest.netease.im'
},
online: {
appkey: '885dea390870814acf3ba8558c717572',//885dea390870814acf3ba8558c717572 //45c6af3c98409b18a84451215d0bdd6e
postUrl: 'https://app.netease.im'
}
}
const imLogin = async ()=>{
let storage_token='';
let storage_accid='';
if (process.env.UNI_PLATFORM == "h5") {
if (window.location.href.indexOf('dev') > -1) {
storage_accid=uni.getetStorageSync('AUTH_YX_ACCID_App');
storage_token=uni.setStorageSync('AUTH_YX_TOKEN_App');
} else {
storage_accid=uni.getStorageSync('DEV_AUTH_YX_ACCID_App');
storage_token=uni.getStorageSync('DEV_AUTH_YX_TOKEN_App');
}
} else if(process.env.UNI_PLATFORM == "mp-weixin") {
const {
envVersion
} = uni.getAccountInfoSync().miniProgram;
if (envVersion == "release") {
storage_accid=uni.getetStorageSync('AUTH_YX_ACCID_App');
storage_token=uni.setStorageSync('AUTH_YX_TOKEN_App');
} else {
storage_accid=uni.getStorageSync('DEV_AUTH_YX_ACCID_App');
storage_token=uni.getStorageSync('DEV_AUTH_YX_TOKEN_App');
}
}else{
if (BASE_URL.indexOf('dev') == -1) {
storage_accid=uni.getStorageSync('DEV_AUTH_YX_ACCID_App');
storage_token=uni.getStorageSync('DEV_AUTH_YX_TOKEN_App');
} else {
storage_accid=uni.getetStorageSync('AUTH_YX_ACCID_App');
storage_token=uni.setStorageSync('AUTH_YX_TOKEN_App');
}
}
const account =storage_accid;
const token = storage_token;
const appkey = appConfig.online.appkey
const params1 = {
appkey: appkey,
debugLevel: 'log',
apiVersion: 'v2'
}
console.log('NIM', NIM)
const nim = NIM.getInstance(params1)
console.log('V2NIMConst', V2NIMConst.V2NIMTeamUpdateInfoMode.V2NIM_TEAM_UPDATE_INFO_MODE_MANAGER)
console.log('V2NIMConst', V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P)
try {
console.log('正在 login...')
await nim.V2NIMLoginService.login(account, token, {
retryCount: 3,
timeout: 60000,
forceMode: false,
authType: 0
})
console.log('login 成功')
}catch(error){
console.log('login 失败', error)
}
}
export default imLogin
+90
View File
@@ -0,0 +1,90 @@
import { t } from './i18n'
import { V2NIMConst } from './nim'
// 自定义的一些事件常量
export const events = {
// 自己发出一条消息
SEND_MSG: 'sendMsg',
// 发送消息失败
SEND_MSG_FAILED: 'sendMsgFailed',
// 收到一条消息
ON_MSG: 'onMsg',
// 加载更多
ON_LOAD_MORE: 'onLoadMore',
// 首次进入聊天页并获取历史记录
ON_CHAT_MOUNTED: 'onChatMounted',
// 重新编辑撤回消息
ON_REEDIT_MSG: 'onReEditMsg',
// 页面触底
ON_REACH_BOTTOM: 'onReachBottom',
// 页面触顶
ON_REACH_TOP: 'onReactTop',
// 回复消息
REPLY_MSG: 'replyMsg',
// input框聚焦
ON_INPUT_FOCUS_CHANGE: 'onInputFocusChange',
// input框失焦
ON_INPUT_BLUR: 'onInputBlur',
// 滚动到底部
ON_SCROLL_BOTTOM: 'onScrollBottom',
// @群成员
AIT_TEAM_MEMBER: 'aitTeamMember',
// 表情框弹起与收起变化
EMOJI_AREA_CHANGE: 'emojiAreaChange',
// 获取历史消息
GET_HISTORY_MSG: 'getHistoryMsg',
// 取消转发消息
CANCEL_FORWARD_MSG: 'cancelForwardMsg',
// 确认转发消息
CONFIRM_FORWARD_MSG: 'confirmForwardMsg',
// @消息 @群成员
HANDLE_AIT_MEMBER: 'handleAitMember',
// 关闭@弹窗
CLOSE_AIT_POPUP: 'closeAitPopup',
// 表情点击
EMOJI_CLICK: 'emojiClick',
// 表情删除
EMOJI_DELETE: 'emojiDelete',
// 表情发送
EMOJI_SEND: 'emojiSend',
// 好友选择
FRIEND_SELECT: 'friendSelect',
// 处理滚动穿透
HANDLE_MOVE_THROUGH: 'handleMoveThrough',
// 关闭表情、语音面板
CLOSE_PANEL: 'closePanel',
// 语音消息url改变
AUDIO_URL_CHANGE: 'audioUrlChange',
}
export const HISTORY_LIMIT = 15
export const MSG_ID_FLAG = 'message-item-'
export const AT_ALL_ACCOUNT = 'ait_all'
export const ALLOW_AT = 'yxAllowAt'
export const REPLY_MSG_TYPE_MAP = {
[V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_IMAGE]: t('imgMsgText'),
[V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_AUDIO]: t('audioMsgText'),
[V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_VIDEO]: t('videoMsgText'),
[V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_FILE]: t('fileMsgText'),
}
export const STORAGE_KEY = '__yx_im_options__'
export const callTypeMap = {
audio: '1',
video: '2',
}
export const g2StatusMap= {
1: t('callDurationText'),
2: t('callCancelText'),
3: t('callRejectedText'),
4: t('callTimeoutText'),
5: t('callBusyText'),
}
export const msgRecallTime = 2 * 60 * 1000
+17
View File
@@ -0,0 +1,17 @@
const preUrl = ''
export function customNavigateTo(options) {
uni.navigateTo({ ...options, url: preUrl + options.url })
}
export function customRedirectTo(options) {
uni.redirectTo({ ...options, url: preUrl + options.url })
}
export function customSwitchTab(options) {
uni.switchTab({ ...options, url: preUrl + options.url })
}
export function customReLaunch(options) {
uni.reLaunch({ ...options, url: preUrl + options.url })
}
+58
View File
@@ -0,0 +1,58 @@
import { t } from './i18n'
export function caculateTimeago(dateTimeStamp) {
const minute = 1000 * 60 // 把分,时,天,周,半个月,一个月用毫秒表示
const hour = minute * 60
const day = hour * 24
const week = day * 7
const now = new Date().getTime() // 获取当前时间毫秒
const diffValue = now - dateTimeStamp // 时间差
let result = ''
if (diffValue < 0) {
return t('nowText')
}
const minC = Math.floor(diffValue / minute) // 计算时间差的分,时,天,周,月
const hourC = Math.floor(diffValue / hour)
const dayC = Math.floor(diffValue / day)
const weekC = Math.floor(diffValue / week)
if (weekC >= 1 && weekC <= 4) {
result = ` ${weekC}${t('weekText')}`
} else if (dayC >= 1 && dayC <= 6) {
result = ` ${dayC}${t('dayText')}`
} else if (hourC >= 1 && hourC <= 23) {
result = ` ${hourC}${t('hourText')}`
} else if (minC >= 1 && minC <= 59) {
result = ` ${minC}${t('minuteText')}`
} else if (diffValue >= 0 && diffValue <= minute) {
result = t('nowText')
} else {
const datetime = new Date()
datetime.setTime(dateTimeStamp)
const Nyear = datetime.getFullYear()
const Nmonth =
datetime.getMonth() + 1 < 10
? `0${datetime.getMonth() + 1}`
: datetime.getMonth() + 1
const Ndate =
datetime.getDate() < 10 ? `0${datetime.getDate()}` : datetime.getDate()
result = `${Nyear}-${Nmonth}-${Ndate}`
}
return result
}
export const formatDateRange = (type) => {
const date = new Date()
let year = date.getFullYear()
let month= date.getMonth() + 1
let day= date.getDate()
if (type === 'start') {
year = year - 100
} else if (type === 'end') {
year = year
}
month = month > 9 ? month : '0' + month
day = day > 9 ? day : '0' + day
return `${year}-${month}-${day}`
}
+160
View File
@@ -0,0 +1,160 @@
import { t } from './i18n'
export const EMOJI_ICON_MAP_CONFIG = {
'[大笑]': 'icon-a-1',
'[开心]': 'icon-a-2',
'[色]': 'icon-a-3',
'[酷]': 'icon-a-4',
'[奸笑]': 'icon-a-5',
'[亲]': 'icon-a-6',
'[伸舌头]': 'icon-a-7',
'[眯眼]': 'icon-a-8',
'[可爱]': 'icon-a-9',
'[鬼脸]': 'icon-a-10',
'[偷笑]': 'icon-a-11',
'[喜悦]': 'icon-a-12',
'[狂喜]': 'icon-a-13',
'[惊讶]': 'icon-a-14',
'[流泪]': 'icon-a-15',
'[流汗]': 'icon-a-16',
'[天使]': 'icon-a-17',
'[笑哭]': 'icon-a-18',
'[尴尬]': 'icon-a-19',
'[惊恐]': 'icon-a-20',
'[大哭]': 'icon-a-21',
'[烦躁]': 'icon-a-22',
'[恐怖]': 'icon-a-23',
'[两眼冒星]': 'icon-a-24',
'[害羞]': 'icon-a-25',
'[睡着]': 'icon-a-26',
'[冒星]': 'icon-a-27',
'[口罩]': 'icon-a-28',
'[OK]': 'icon-a-29',
'[好吧]': 'icon-a-30',
'[鄙视]': 'icon-a-31',
'[难受]': 'icon-a-32',
'[不屑]': 'icon-a-33',
'[不舒服]': 'icon-a-34',
'[愤怒]': 'icon-a-35',
'[鬼怪]': 'icon-a-36',
'[发怒]': 'icon-a-37',
'[生气]': 'icon-a-38',
'[不高兴]': 'icon-a-39',
'[皱眉]': 'icon-a-40',
'[心碎]': 'icon-a-41',
'[心动]': 'icon-a-42',
'[好的]': 'icon-a-43',
'[低级]': 'icon-a-44',
'[赞]': 'icon-a-45',
'[鼓掌]': 'icon-a-46',
'[给力]': 'icon-a-47',
'[打你]': 'icon-a-48',
'[阿弥陀佛]': 'icon-a-49',
'[拜拜]': 'icon-a-50',
'[第一]': 'icon-a-51',
'[拳头]': 'icon-a-52',
'[手掌]': 'icon-a-53',
'[剪刀]': 'icon-a-54',
'[招手]': 'icon-a-55',
'[不要]': 'icon-a-56',
'[举着]': 'icon-a-57',
'[思考]': 'icon-a-58',
'[猪头]': 'icon-a-59',
'[不听]': 'icon-a-60',
'[不看]': 'icon-a-61',
'[不说]': 'icon-a-62',
'[猴子]': 'icon-a-63',
'[炸弹]': 'icon-a-64',
'[睡觉]': 'icon-a-65',
'[筋斗云]': 'icon-a-66',
'[火箭]': 'icon-a-67',
'[救护车]': 'icon-a-68',
'[便便]': 'icon-a-70',
}
export const emojiMap = {
[t('Laugh')]: 'icon-a-1',
[t('Happy')]: 'icon-a-2',
[t('Sexy')]: 'icon-a-3',
[t('Cool')]: 'icon-a-4',
[t('Mischievous')]: 'icon-a-5',
[t('Kiss')]: 'icon-a-6',
[t('Spit')]: 'icon-a-7',
[t('Squint')]: 'icon-a-8',
[t('Cute')]: 'icon-a-9',
[t('Grimace')]: 'icon-a-10',
[t('Snicker')]: 'icon-a-11',
[t('Joy')]: 'icon-a-12',
[t('Ecstasy')]: 'icon-a-13',
[t('Surprise')]: 'icon-a-14',
[t('Tears')]: 'icon-a-15',
[t('Sweat')]: 'icon-a-16',
[t('Angle')]: 'icon-a-17',
[t('Funny')]: 'icon-a-18',
[t('Awkward')]: 'icon-a-19',
[t('Thrill')]: 'icon-a-20',
[t('Cry')]: 'icon-a-21',
[t('Fretting')]: 'icon-a-22',
[t('Terrorist')]: 'icon-a-23',
[t('Halo')]: 'icon-a-24',
[t('Shame')]: 'icon-a-25',
[t('Sleep')]: 'icon-a-26',
[t('Tired')]: 'icon-a-27',
[t('Mask')]: 'icon-a-28',
[t('ok')]: 'icon-a-29',
[t('AllRight')]: 'icon-a-30',
[t('Despise')]: 'icon-a-31',
[t('Uncomfortable')]: 'icon-a-32',
[t('Disdain')]: 'icon-a-33',
[t('ill')]: 'icon-a-34',
[t('Mad')]: 'icon-a-35',
[t('Ghost')]: 'icon-a-36',
[t('Angry')]: 'icon-a-37',
[t('Angry')]: 'icon-a-38',
[t('Unhappy')]: 'icon-a-39',
[t('Frown')]: 'icon-a-40',
[t('Broken')]: 'icon-a-41',
[t('Beckoning')]: 'icon-a-42',
[t('Ok')]: 'icon-a-43',
[t('Low')]: 'icon-a-44',
[t('Nice')]: 'icon-a-45',
[t('Applause')]: 'icon-a-46',
[t('GoodJob')]: 'icon-a-47',
[t('Hit')]: 'icon-a-48',
[t('Please')]: 'icon-a-49',
[t('Bye')]: 'icon-a-50',
[t('First')]: 'icon-a-51',
[t('Fist')]: 'icon-a-52',
[t('GiveMeFive')]: 'icon-a-53',
[t('Knife')]: 'icon-a-54',
[t('Hi')]: 'icon-a-55',
[t('No')]: 'icon-a-56',
[t('Hold')]: 'icon-a-57',
[t('Think')]: 'icon-a-58',
[t('Pig')]: 'icon-a-59',
[t('NoListen')]: 'icon-a-60',
[t('NoLook')]: 'icon-a-61',
[t('NoWords')]: 'icon-a-62',
[t('Monkey')]: 'icon-a-63',
[t('Bomb')]: 'icon-a-64',
[t('Sleep')]: 'icon-a-65',
[t('Cloud')]: 'icon-a-66',
[t('Rocket')]: 'icon-a-67',
[t('Ambulance')]: 'icon-a-68',
[t('Poop')]: 'icon-a-70',
}
export const emojiRegExp = new RegExp(
'(' +
Object.keys(EMOJI_ICON_MAP_CONFIG)
.map((item) => {
const left = `\\${item.slice(0, 1)}`
const right = `\\${item.slice(-1)}`
const mid = item.slice(1, -1)
return `${left}${mid}${right}`
})
.join('|') +
')',
'g'
)
+26
View File
@@ -0,0 +1,26 @@
export default (data)=> {
let text = ''
for (let i = 0; i < data.length; i++) {
switch (data.charCodeAt(i)) {
case 34: // "
text += '&quot;'
break
case 38: // &
text += '&amp;'
break
case 39: // '
text += '&#x27;' // modified from escape-html; used to be '&#39'
break
case 60: // <
text += '&lt;'
break
case 62: // >
text += '&gt;'
break
default:
text += data[i]
break
}
}
return text
}
+72
View File
@@ -0,0 +1,72 @@
// #ifdef APP-PLUS
import pinyin from 'pinyin'
// #endif
export const friendGroupByPy = (
arr,
keys,
isLowerCase = true
) => {
const res = {}
const OTHER_TAG = '#'
const add = (k, v) => {
const _k = isLowerCase ? k.toLowerCase() : k.toUpperCase()
if (!res[_k]) {
res[_k] = [v]
} else {
res[_k].push(v)
}
}
arr.forEach((item) => {
const v =
item[keys.firstKey] ||
item[keys.secondKey || ''] ||
item[keys.thirdKey || '']
if (!!v && typeof v === 'string') {
const str = v[0]
if (/^[a-zA-Z]$/.test(str)) {
add(str.toLowerCase(), item)
} else if (/^[\u4e00-\u9fa5]$/.test(str)) {
const en = '*abcdefghjklmnopqrstwxyz'.split('')
let resolvedK
// #ifdef APP-PLUS
// @ts-ignore
resolvedK = en.find((k) => {
const py = pinyin(str, {
style: 'FIRST_LETTER',
})
return py[0][0] === k
})
// #endif
// #ifndef APP-PLUS
const zh = '阿八嚓哒妸发旮哈讥咔垃痳拏噢妑七呥扨它穵夕丫帀'.split('')
// @ts-ignore
resolvedK = en.find(
(currentK, ki) =>
(!zh[ki - 1] || zh[ki - 1].localeCompare(str, 'zh') <= 0) &&
str.localeCompare(zh[ki], 'zh') == -1
)
// #endif
if (resolvedK && resolvedK !== '*') {
add(resolvedK, item)
} else {
add(OTHER_TAG, item)
}
} else {
add(OTHER_TAG, item)
}
} else {
add(OTHER_TAG, item)
}
})
const data = Object.keys(res).map((k) => ({ key: k, data: res[k] }))
const sortData = data
.filter((item) => item.key !== OTHER_TAG)
.sort((a, b) => a.key.localeCompare(b.key, 'en'))
const otherData = data.filter((item) => item.key === OTHER_TAG)
return sortData.concat(otherData)
}
+16
View File
@@ -0,0 +1,16 @@
import en from '../../locale/en'
import zh from '../../locale/zh-Hans'
const i18nData = {
en,
zh,
}
let currentLanguage= 'zh'
export function setLanguage(language) {
currentLanguage = language
}
export const t = (key) => {
return i18nData[currentLanguage][key] || key
}
+442
View File
@@ -0,0 +1,442 @@
import { events } from './constants'
export const getUniPlatform = () => {
// @ts-ignore
return uni.getSystemInfoSync().uniPlatform
}
export const getPlatform = () => {
// @ts-ignore
return uni.getSystemInfoSync().platform
}
// 是否是android app
export const isAndroidApp =
uni.getSystemInfoSync().platform == 'android' &&
uni.getSystemInfoSync().uniPlatform == 'app'
// 是否是Ios app
export const isIosApp =
uni.getSystemInfoSync().platform == 'ios' &&
uni.getSystemInfoSync().uniPlatform == 'app'
// 是否是鸿蒙app
export const isHarmonyOs =
uni.getSystemInfoSync().platform == 'harmonyos' &&
uni.getSystemInfoSync().uniPlatform === 'app'
// 是否是Android app 或者 Ios app
export const isAndroidOrIosApp =
uni.getSystemInfoSync().uniPlatform == 'app' &&
(uni.getSystemInfoSync().platform == 'android' ||
uni.getSystemInfoSync().platform == 'ios')
// 是否是Ios web
export const isIosWeb =
uni.getSystemInfoSync().uniPlatform === 'web' &&
uni.getSystemInfoSync().platform === 'ios'
// 是否是App
export const isApp = uni.getSystemInfoSync().uniPlatform == 'app'
// 是否是微信小程序
export const isWxApp = uni.getSystemInfoSync().uniPlatform == 'mp-weixin'
// 是否是web
export const isWeb = uni.getSystemInfoSync().uniPlatform === 'web'
// @ts-ignore
export function deepClone(source, visited = new WeakMap()) {
if (source === null || typeof source !== 'object') {
return source
}
if (visited.has(source)) {
return visited.get(source)
}
// @ts-ignore
let clone
if (source instanceof Date) {
clone = new Date(source.getTime())
} else if (source instanceof RegExp) {
clone = new RegExp(source)
} else if (source instanceof Map) {
clone = new Map()
visited.set(source, clone)
source.forEach((value, key) => {
// @ts-ignore
clone.set(key, deepClone(value, visited))
})
} else if (source instanceof Set) {
clone = new Set()
visited.set(source, clone)
source.forEach((value) => {
// @ts-ignore
clone.add(deepClone(value, visited))
})
} else if (Array.isArray(source)) {
clone = []
// @ts-ignore
visited.set(source, clone)
for (let i = 0; i < source.length; i++) {
clone[i] = deepClone(source[i], visited)
}
} else {
clone = Object.create(Object.getPrototypeOf(source))
visited.set(source, clone)
for (const prop in source) {
if (source.hasOwnProperty(prop)) {
clone[prop] = deepClone(source[prop], visited)
}
}
}
return clone
}
export function stopAllAudio() {
uni.$emit(events.AUDIO_URL_CHANGE, '')
}
/**
* 秒转换为时分秒
*/
export const convertSecondsToTime = (seconds)=> {
if (!seconds) {
return null
}
const hours = Math.floor(seconds / 3600)
const minutes = Math.floor((seconds - hours * 3600) / 60)
const remainingSeconds= seconds - hours * 3600 - minutes * 60
let timeString = ''
const includeHours = seconds >= 3600
if (includeHours) {
if (hours < 10) {
timeString += '0'
}
timeString += hours.toString() + ':'
}
if (minutes < 10) {
timeString += '0'
}
timeString += minutes.toString() + ':'
if (remainingSeconds < 10) {
timeString += '0'
}
timeString += remainingSeconds.toString()
return timeString
}
export const startCall = (options) => {
try {
// @ts-ignore
uni.$UIKitCallKit.toCallPage(
{
calledAccount: options.remoteUserAccid,
// currentUserAccid: options.currentUserAccid,
type: options.type,
calledShowName: options.remoteShowName,
},
() => {
console.log('toCallPage: ', {
remoteUserAccid: options.remoteUserAccid,
currentUserAccid: options.currentUserAccid,
type: options.type,
remoteShowName: options.remoteShowName,
})
}
)
} catch (error) {
console.log('handleVideoCall error', error)
}
}
export const EMOJI_ICON_MAP_CONFIG= {
'[大笑]': 'icon-a-1',
'[开心]': 'icon-a-2',
'[色]': 'icon-a-3',
'[酷]': 'icon-a-4',
'[奸笑]': 'icon-a-5',
'[亲]': 'icon-a-6',
'[伸舌头]': 'icon-a-7',
'[眯眼]': 'icon-a-8',
'[可爱]': 'icon-a-9',
'[鬼脸]': 'icon-a-10',
'[偷笑]': 'icon-a-11',
'[喜悦]': 'icon-a-12',
'[狂喜]': 'icon-a-13',
'[惊讶]': 'icon-a-14',
'[流泪]': 'icon-a-15',
'[流汗]': 'icon-a-16',
'[天使]': 'icon-a-17',
'[笑哭]': 'icon-a-18',
'[尴尬]': 'icon-a-19',
'[惊恐]': 'icon-a-20',
'[大哭]': 'icon-a-21',
'[烦躁]': 'icon-a-22',
'[恐怖]': 'icon-a-23',
'[两眼冒星]': 'icon-a-24',
'[害羞]': 'icon-a-25',
'[睡着]': 'icon-a-26',
'[冒星]': 'icon-a-27',
'[口罩]': 'icon-a-28',
'[OK]': 'icon-a-29',
'[好吧]': 'icon-a-30',
'[鄙视]': 'icon-a-31',
'[难受]': 'icon-a-32',
'[不屑]': 'icon-a-33',
'[不舒服]': 'icon-a-34',
'[愤怒]': 'icon-a-35',
'[鬼怪]': 'icon-a-36',
'[发怒]': 'icon-a-37',
'[生气]': 'icon-a-38',
'[不高兴]': 'icon-a-39',
'[皱眉]': 'icon-a-40',
'[心碎]': 'icon-a-41',
'[心动]': 'icon-a-42',
'[好的]': 'icon-a-43',
'[低级]': 'icon-a-44',
'[赞]': 'icon-a-45',
'[鼓掌]': 'icon-a-46',
'[给力]': 'icon-a-47',
'[打你]': 'icon-a-48',
'[阿弥陀佛]': 'icon-a-49',
'[拜拜]': 'icon-a-50',
'[第一]': 'icon-a-51',
'[拳头]': 'icon-a-52',
'[手掌]': 'icon-a-53',
'[剪刀]': 'icon-a-54',
'[招手]': 'icon-a-55',
'[不要]': 'icon-a-56',
'[举着]': 'icon-a-57',
'[思考]': 'icon-a-58',
'[猪头]': 'icon-a-59',
'[不听]': 'icon-a-60',
'[不看]': 'icon-a-61',
'[不说]': 'icon-a-62',
'[猴子]': 'icon-a-63',
'[炸弹]': 'icon-a-64',
'[睡觉]': 'icon-a-65',
'[筋斗云]': 'icon-a-66',
'[火箭]': 'icon-a-67',
'[救护车]': 'icon-a-68',
'[便便]': 'icon-a-70',
}
export function replaceEmoji(text, emojiMap) {
const defaultEmojiMap = {
'[Laugh]': '[大笑]',
'[Happy]': '[开心]',
'[Sexy]': '[色]',
'[Cool]': '[酷]',
'[Mischievous]': '[奸笑]',
'[Kiss]': '[亲]',
'[Spit]': '[伸舌头]',
'[Squint]': '[眯眼]',
'[Cute]': '[可爱]',
'[Grimace]': '[鬼脸]',
'[Snicker]': '[偷笑]',
'[Joy]': '[喜悦]',
'[Ecstasy]': '[狂喜]',
'[Surprise]': '[惊讶]',
'[Tears]': '[流泪]',
'[Sweat]': '[流汗]',
'[Angle]': '[天使]',
'[Funny]': '[笑哭]',
'[Awkward]': '[尴尬]',
'[Thrill]': '[惊恐]',
'[Cry]': '[大哭]',
'[Fretting]': '[烦躁]',
'[Terrorist]': '[恐怖]',
'[Halo]': '[两眼冒星]',
'[Shame]': '[害羞]',
'[Sleep]': '[睡着]',
'[Tired]': '[睡觉]',
'[Mask]': '[口罩]',
'[Ok]': '[OK]',
'[All right]': '[好吧]',
'[Despise]': '[鄙视]',
'[Uncomfortable]': '[难受]',
'[Disdain]': '[不屑]',
'[ill]': '[不舒服]',
'[Mad]': '[愤怒]',
'[Ghost]': '[鬼怪]',
'[Huff]': '[发怒]',
'[Angry]': '[生气]',
'[Unhappy]': '[不高兴]',
'[Frown]': '[皱眉]',
'[Broken]': '[心碎]',
'[Beckoning]': '[心动]',
'[Low]': '[低级]',
'[Nice]': '[赞]',
'[Applause]': '[鼓掌]',
'[Good job]': '[给力]',
'[Hit]': '[打你]',
'[Please]': '[阿弥陀佛]',
'[Bye]': '[拜拜]',
'[First]': '[第一]',
'[Fist]': '[拳头]',
'[Give me five]': '[招手]',
'[Knife]': '[剪刀]',
'[Hi]': '[招手]',
'[No]': '[不要]',
'[Hold]': '[举着]',
'[Think]': '[思考]',
'[Pig]': '[猪头]',
'[No listen]': '[不听]',
'[No look]': '[不看]',
'[No words]': '[不说]',
'[Monkey]': '[猴子]',
'[Bomb]': '[炸弹]',
'[Sleeping]': '[睡觉]',
'[Cloud]': '[筋斗云]',
'[Rocket]': '[火箭]',
'[Ambulance]': '[救护车]',
'[Poop]': '[便便]',
}
const emojiMapConfig = emojiMap || defaultEmojiMap
const emojiMapRegex = new RegExp(
'(' +
Object.keys(emojiMapConfig)
.map((item) => {
const left = `\\${item.slice(0, 1)}`
const right = `\\${item.slice(-1)}`
const mid = item.slice(1, -1)
return `${left}${mid}${right}`
})
.join('|') +
')',
'g'
)
return text.replace(emojiMapRegex, function (matched) {
return emojiMapConfig[matched]
})
}
export const handleEmojiTranslate = (t) => {
const SHOW_EMOJI_ICON_MAP_CONFIG = {
[t('Laugh')]: 'icon-a-1',
[t('Happy')]: 'icon-a-2',
[t('Sexy')]: 'icon-a-3',
[t('Cool')]: 'icon-a-4',
[t('Mischievous')]: 'icon-a-5',
[t('Kiss')]: 'icon-a-6',
[t('Spit')]: 'icon-a-7',
[t('Squint')]: 'icon-a-8',
[t('Cute')]: 'icon-a-9',
[t('Grimace')]: 'icon-a-10',
[t('Snicker')]: 'icon-a-11',
[t('Joy')]: 'icon-a-12',
[t('Ecstasy')]: 'icon-a-13',
[t('Surprise')]: 'icon-a-14',
[t('Tears')]: 'icon-a-15',
[t('Sweat')]: 'icon-a-16',
[t('Angle')]: 'icon-a-17',
[t('Funny')]: 'icon-a-18',
[t('Awkward')]: 'icon-a-19',
[t('Thrill')]: 'icon-a-20',
[t('Cry')]: 'icon-a-21',
[t('Fretting')]: 'icon-a-22',
[t('Terrorist')]: 'icon-a-23',
[t('Halo')]: 'icon-a-24',
[t('Shame')]: 'icon-a-25',
[t('Sleep')]: 'icon-a-26',
[t('Tired')]: 'icon-a-27',
[t('Mask')]: 'icon-a-28',
[t('ok')]: 'icon-a-29',
[t('AllRight')]: 'icon-a-30',
[t('Despise')]: 'icon-a-31',
[t('Uncomfortable')]: 'icon-a-32',
[t('Disdain')]: 'icon-a-33',
[t('ill')]: 'icon-a-34',
[t('Mad')]: 'icon-a-35',
[t('Ghost')]: 'icon-a-36',
[t('huff')]: 'icon-a-37',
[t('Angry')]: 'icon-a-38',
[t('Unhappy')]: 'icon-a-39',
[t('Frown')]: 'icon-a-40',
[t('Broken')]: 'icon-a-41',
[t('Beckoning')]: 'icon-a-42',
[t('Ok')]: 'icon-a-43',
[t('Low')]: 'icon-a-44',
[t('Nice')]: 'icon-a-45',
[t('Applause')]: 'icon-a-46',
[t('GoodJob')]: 'icon-a-47',
[t('Hit')]: 'icon-a-48',
[t('Please')]: 'icon-a-49',
[t('Bye')]: 'icon-a-50',
[t('First')]: 'icon-a-51',
[t('Fist')]: 'icon-a-52',
[t('GiveMeFive')]: 'icon-a-53',
[t('Knife')]: 'icon-a-54',
[t('Hi')]: 'icon-a-55',
[t('No')]: 'icon-a-56',
[t('Hold')]: 'icon-a-57',
[t('Think')]: 'icon-a-58',
[t('Pig')]: 'icon-a-59',
[t('NoListen')]: 'icon-a-60',
[t('NoLook')]: 'icon-a-61',
[t('NoWords')]: 'icon-a-62',
[t('Monkey')]: 'icon-a-63',
[t('Bomb')]: 'icon-a-64',
[t('Sleeping')]: 'icon-a-65',
[t('Cloud')]: 'icon-a-66',
[t('Rocket')]: 'icon-a-67',
[t('Ambulance')]: 'icon-a-68',
[t('Poop')]: 'icon-a-70',
}
const INPUT_EMOJI_SYMBOL_REG = new RegExp(
'(' +
Object.keys(EMOJI_ICON_MAP_CONFIG)
.map((item) => {
const left = `\\${item.slice(0, 1)}`
const right = `\\${item.slice(-1)}`
const mid = item.slice(1, -1)
return `${left}${mid}${right}`
})
.join('|') +
')',
'g'
)
return {
SHOW_EMOJI_ICON_MAP_CONFIG,
INPUT_EMOJI_SYMBOL_REG,
}
}
// emoji正则
export const emojiRegExp = new RegExp(
'(' +
Object.keys(EMOJI_ICON_MAP_CONFIG)
.map((item) => {
const left = `\\${item.slice(0, 1)}`
const right = `\\${item.slice(-1)}`
const mid = item.slice(1, -1)
return `${left}${mid}${right}`
})
.join('|') +
')',
'g'
)
/** 是否是讨论组 */
export const isDiscussionFunc = (serverExtension) => {
try {
return JSON.parse(serverExtension || '{}').im_ui_kit_group
} catch (error) {
console.warn('parse serverExtension error', error)
return false
}
}
+16
View File
@@ -0,0 +1,16 @@
export const calculateMatrix =(arr, colNum)=> {
if (colNum < 1) {
throw Error('colNum cound not be smaller than 1')
}
const len = arr.length
const rowNum = Math.ceil(len / colNum)
let curIndex = 0
let curRow = 0
const res = []
while (curIndex <= len && curRow < rowNum) {
res[curRow] = arr.slice(curIndex, curIndex + colNum)
curIndex += colNum
curRow++
}
return res
}
+123
View File
@@ -0,0 +1,123 @@
import { V2NIMConst } from './nim'
import { t } from './i18n'
const translate = (key)=> {
const text =
{
textMsgText: t('textMsgText'),
customMsgText: t('customMsgText'),
audioMsgText: t('audioMsgText'),
videoMsgText: t('videoMsgText'),
fileMsgText: t('fileMsgText'),
callMsgText: t('callMsgText'),
geoMsgText: t('geoMsgText'),
imgMsgText: t('imgMsgText'),
notiMsgText: t('notiMsgText'),
robotMsgText: t('robotMsgText'),
tipMsgText: t('tipMsgText'),
unknowMsgText: t('unknowMsgText'),
}[key] || ''
return `[${text}]`
}
export const getMsgContentTipByType = (msg)=> {
const { messageType, text } = msg
switch (messageType) {
case V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_TEXT:
return text || translate('textMsgText')
case V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_FILE:
return translate('fileMsgText')
case V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_IMAGE:
return translate('imgMsgText')
case V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_CUSTOM:
return text || translate('customMsgText')
case V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_AUDIO:
return translate('audioMsgText')
case V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_CALL:
return translate('callMsgText')
case V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_LOCATION:
return translate('geoMsgText')
case V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_NOTIFICATION:
return translate('notiMsgText')
case V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_ROBOT:
return translate('robotMsgText')
case V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_TIPS:
return translate('tipMsgText')
case V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_VIDEO:
return translate('videoMsgText')
default:
return translate('unknowMsgText')
}
}
export const setTabUnread = ()=> {
const routes = getCurrentPages()
const curRoute = routes[routes.length - 1].route
const tabPaths = [
'pages/User/my/index',
'pages/Contact/index',
'pages/Conversation/index',
]
if (curRoute && !tabPaths.includes(curRoute)) {
// 不是首页和聊天页,不需要设置tabbar的badge
return
}
const store = uni.$UIKitStore
/**是否是云端会话 */
const enableV2CloudConversation = store?.sdkOptions?.enableV2CloudConversation
const unread =
(enableV2CloudConversation
? store?.conversationStore?.totalUnreadCount
: store?.localConversationStore?.totalUnreadCount) || 0
if (unread === 0) {
uni.hideTabBarRedDot({
//隐藏数字
index: 0, //tabbar下标
})
} else {
uni.showTabBarRedDot({
index: 0, //tabbar下标
})
}
/**
* 此为展示tabbar的未读消息数字的写法
if (unread === 0) {
uni.removeTabBarBadge({
//隐藏数字
index: 0, //tabbar下标
})
} else {
uni.setTabBarBadge({
//显示数字
index: 0, //tabbar下标
text: unread > 99 ? '99+' : unread.toString(), //显示的数字
})
}
*/
}
export const setContactTabUnread = ()=> {
const routes = getCurrentPages()
const curRoute = routes[routes.length - 1].route
const tabPaths = [
'pages/User/my/index',
'pages/Contact/index',
'pages/Conversation/index',
]
if (curRoute && !tabPaths.includes(curRoute)) {
// 不是首页和聊天页,不需要设置tabbar的badge
return
}
const unread = uni.$UIKitStore?.sysMsgStore?.getTotalUnreadMsgsCount()
if (unread === 0) {
uni.hideTabBarRedDot({
//隐藏数字
index: 1, //tabbar下标
})
} else {
uni.showTabBarRedDot({
index: 1, //tabbar下标
})
}
}
+15
View File
@@ -0,0 +1,15 @@
// 常规集成的V2NIMConst
import { V2NIMConst } from "nim-web-sdk-ng/dist/esm/nim";
// ESM 版本集成的V2NIMConst
/**
* NIM SDK 默认导出 UMDUniversal Module Definition)格式的产物,这种格式的文件可以在多种环境中使用。
* 当应用对包体积有更严格的要求,例如小程序应用,则可以使用 ESM(ECMAScript Module)格式的 SDK。ESM 是 JavaScript 的原生模块系统,
* 允许更细粒度的模块控制和更有效的打包。再配合 webpack 实现 tree-shaking(一种去除代码中未引用部分的方式),从而进一步降低 NIM SDK 产物体积。
* 具体参考文档 https://doc.yunxin.163.com/messaging2/guide/DcyMjA1Njk?platform=client
*/
//@ts-ignore
// import { V2NIMConst } from '../esmNim.js'
export { V2NIMConst };
+160
View File
@@ -0,0 +1,160 @@
import { emojiRegExp } from './emoji'
/* eslint-disable vars-on-top, no-var, prefer-template */
// @ts-nocheck
var isRegExp = function (re) {
return re instanceof RegExp
}
var escapeRegExp = function escapeRegExp(string) {
var reRegExpChar = /[\\^$.*+?()[\]{}|]/g,
reHasRegExpChar = RegExp(reRegExpChar.source)
return string && reHasRegExpChar.test(string)
? string.replace(reRegExpChar, '\\$&')
: string
}
var isString = function (value) {
return typeof value === 'string'
}
var flatten = function (array) {
var newArray= []
array.forEach(function (item) {
if (Array.isArray(item)) {
newArray = newArray.concat(item)
} else {
newArray.push(item)
}
})
return newArray
}
/**
* Given a string, replace every substring that is matched by the `match` regex
* with the result of calling `fn` on matched substring. The result will be an
* array with all odd indexed elements containing the replacements. The primary
* use case is similar to using String.prototype.replace except for React.
*
* React will happily render an array as children of a react element, which
* makes this approach very useful for tasks like surrounding certain text
* within a string with react elements.
*
* Example:
* matchReplace(
* 'Emphasize all phone numbers like 884-555-4443.',
* /([\d|-]+)/g,
* (number, i) => <strong key={i}>{number}</strong>
* );
* // => ['Emphasize all phone numbers like ', <strong>884-555-4443</strong>, '.'
*
* @param {string} str
* @param {RegExp|str} match Must contain a matching group
* @param {function} fn
* @return {array}
*/
function replaceString(str, match, fn) {
var curCharStart = 0
var curCharLen = 0
if (str === '') {
return str
} else if (!str || !isString(str)) {
throw new TypeError(
'First argument to react-string-replace#replaceString must be a string'
)
}
var re = match
if (!isRegExp(re)) {
// @ts-ignore
re = new RegExp('(' + escapeRegExp(re) + ')', 'gi')
}
// @ts-ignore
var result = str.split(re)
// Apply fn to all odd elements
for (var i = 1, length = result.length; i < length; i += 2) {
/** @see {@link https://github.com/iansinnott/react-string-replace/issues/74} */
if (result[i] === undefined || result[i - 1] === undefined) {
console.warn(
'reactStringReplace: Encountered undefined value during string replacement. Your RegExp may not be working the way you expect.'
)
continue
}
curCharLen = result[i].length
curCharStart += result[i - 1].length
result[i] = fn(result[i], i, curCharStart)
curCharStart += curCharLen
}
return result
}
// 字符串替换
function stringReplace(source, match, fn) {
if (!Array.isArray(source)) source = [source]
return flatten(
source.map(function (x) {
return isString(x) ? replaceString(x, match, fn) : x
})
)
}
export function parseText(text, ext){
if (!text) return []
const regexLink = /(https?:\/\/\S+)/gi
const yxAitMsg = ext ? JSON.parse(ext).yxAitMsg : null
const emojiArr = stringReplace(text, emojiRegExp, (item) => {
return {
type: 'emoji',
value: item,
}
})
const emojiAndLinkArr = stringReplace(emojiArr, regexLink, (item) => {
return {
type: 'link',
value: item,
}
})
let emojiAndLinkAndAitArr = emojiAndLinkArr
if (yxAitMsg) {
Object.keys(yxAitMsg)?.forEach((key) => {
const item = yxAitMsg[key]
emojiAndLinkAndAitArr = stringReplace(
emojiAndLinkAndAitArr,
item.text,
(item) => {
return {
type: 'Ait',
value: item,
}
}
)
})
}
const result = emojiAndLinkAndAitArr.map((item, index) => {
if (typeof item == 'string') {
return {
type: 'text',
value: item,
key: index + 'text',
}
} else {
return {
...item,
key: index + item.type,
}
}
})
return result
}
+27
View File
@@ -0,0 +1,27 @@
import { t } from './i18n'
export const handleNoPermission = (res)=> {
if (
uni.getSystemInfoSync().platform == 'android' ||
uni.getSystemInfoSync().platform == 'ios'
) {
const appAuthorizeSetting = uni.getAppAuthorizeSetting()
//点击取消时不出现弹窗
const isCameraDenied = appAuthorizeSetting.cameraAuthorized === 'denied'
const isUserCancelled =
res.errMsg === 'chooseImage:fail User cancelled' ||
res.errMsg === 'chooseImage:fail cancel' ||
res.errMsg === 'chooseVideo:fail cancel' ||
res.errMsg === 'chooseVideo:fail User cancelled'
if (
(res.code === 11 && isCameraDenied && !isUserCancelled) ||
(res.code === 12 && isCameraDenied && !isUserCancelled) ||
(res.code === 2 && !isUserCancelled)
) {
uni.showToast({
icon: 'none',
title: t('addPermissionText'),
})
}
}
}
+23
View File
@@ -0,0 +1,23 @@
import { EventTracking } from '@xkit-yx/utils/dist/uniapp'
//@ts-ignore
import imPkg from 'nim-web-sdk-ng/package.json'
export const trackInit = (
component
) => {
if (uni.$UIKitNIM?.options?.appkey) {
const eventTracking = new EventTracking({
appKey: uni.$UIKitNIM.options.appkey,
version: '10.4.0',
component,
imVersion: imPkg.version,
platform: 'UniApp',
os: uni.getSystemInfoSync().platform.toUpperCase(),
framework: 'UniApp',
language: 'Vue3',
container: uni.getSystemInfoSync().uniPlatform.toUpperCase(),
})
eventTracking.track('init', '')
}
}
+12
View File
@@ -0,0 +1,12 @@
let vueVersion
// #ifndef VUE3
export * from '@vue/composition-api'
vueVersion = 2
// #endif
// #ifdef VUE3
export * from 'vue'
vueVersion = 3
// #endif
console.log(`vue version is ${vueVersion}`)
export { vueVersion }
+1808
View File
File diff suppressed because it is too large Load Diff