近期需求 : 在项目里面开发IM 即时通讯 之前对接过融云 GoEasy 极光 这次在微信小程序里面集成 本着" 同源策略 " 采用了腾讯云IM 即时通讯 虽然腾讯方面的文档对我来说并不是那么的友好 ☺
即时通信 IM官网地址: cloud.tencent.com/document/pr…
鉴于小程序体积的限制外加本公司对UI还原程度 "高度重视" , 并没有采用含UI集成方案 , 采用了无UI集成方案
半成品
步骤:
集成 SDK
- 通过 npm 和 script 方式将 IM SDK 集成到您的 Web 项目中,推荐使用 npm 集成。
- 通过 npm 方式将 IM SDK 集成到您的小程序或者 uni-app 项目中。
- 通过集成上传插件 tim-upload-plugin,实现更快更安全的富文本消息资源上传。
- 通过集成本地审核插件 tim-profanity-filter-plugin,在客户端本地检测由即时通信 SDK 发送的文本内容,支持对已配置的敏感词进行拦截或者替换处理,为您的产品体验和业务安全保驾护航。本地审核功能的开通和配置方法,详情请参见 控制台指南。
// 从v2.11.2起,SDK 支持了 WebSocket,推荐接入;v2.10.2及以下版本,使用 HTTP
npm install tim-wx-sdk --save
// 发送图片、文件等消息需要腾讯云 即时通信 IM 上传插件
npm install tim-upload-plugin --save
// 拦截或替换敏感词需要本地审核插件
npm install tim-profanity-filter-plugin --save
初始化
初始化 SDK 需要操作以下步骤:
- 准备 SDKAppID。
- 调用
TIM.create初始化 SDK。 - 添加 SDK 事件监听器。
// 从v2.11.2起,SDK 支持了 WebSocket,推荐接入;v2.10.2及以下版本,使用 HTTP
// v2.24.0起,SDK 支持使用本地审核插件
import TIM from 'tim-js-sdk';
import TIMUploadPlugin from 'tim-upload-plugin';
import TIMProfanityFilterPlugin from 'tim-profanity-filter-plugin';
let options = {
SDKAppID: 0 // 接入时需要将0替换为您的即时通信 IM 应用的 SDKAppID
};
// 创建 SDK 实例,`TIM.create()`方法对于同一个 `SDKAppID` 只会返回同一份实例
let tim = TIM.create(options); // SDK 实例通常用 tim 表示
// 设置 SDK 日志输出级别,详细分级请参见 <a href="https://web.sdk.qcloud.com/im/doc/zh-cn/SDK.html#setLogLevel">setLogLevel 接口的说明</a>
tim.setLogLevel(0); // 普通级别,日志量较多,接入时建议使用
// tim.setLogLevel(1); // release 级别,SDK 输出关键信息,生产环境时建议使用
// 注册腾讯云即时通信 IM 上传插件
tim.registerPlugin({'tim-upload-plugin': TIMUploadPlugin});
// 注册腾讯云即时通信 IM 本地审核插件
tim.registerPlugin({'tim-profanity-filter-plugin': TIMProfanityFilterPlugin});
登录
let promise = tim.login({userID: 'your userID', userSig: 'your userSig'});
promise.then(function(imResponse) {
console.log(imResponse.data); // 登录成功
if (imResponse.data.repeatLogin === true) {
// 标识帐号已登录,本次登录操作为重复登录。v2.5.1 起支持
console.log(imResponse.data.errorInfo);
}
}).catch(function(imError) {
console.warn('login error:', imError); // 登录失败的相关信息
});
创建消息
接收消息 事件监听
let onMessageReceived = function(event) {
// event.data - 存储 Message 对象的数组 - [Message]
const messageList = event.data;
messageList.forEach((message) => {
if (message.type === TIM.TYPES.MSG_TEXT) {
// 文本消息 - https://web.sdk.qcloud.com/im/doc/zh-cn/Message.html#.TextPayload
} else if (message.type === TIM.TYPES.MSG_IMAGE) {
// 图片消息 - https://web.sdk.qcloud.com/im/doc/zh-cn/Message.html#.ImagePayload
} else if (message.type === TIM.TYPES.MSG_SOUND) {
// 音频消息 - https://web.sdk.qcloud.com/im/doc/zh-cn/Message.html#.AudioPayload
} else if (message.type === TIM.TYPES.MSG_VIDEO) {
// 视频消息 - https://web.sdk.qcloud.com/im/doc/zh-cn/Message.html#.VideoPayload
} else if (message.type === TIM.TYPES.MSG_FILE) {
// 文件消息 - https://web.sdk.qcloud.com/im/doc/zh-cn/Message.html#.FilePayload
} else if (message.type === TIM.TYPES.MSG_CUSTOM) {
// 自定义消息 - https://web.sdk.qcloud.com/im/doc/zh-cn/Message.html#.CustomPayload
} else if (message.type === TIM.TYPES.MSG_MERGER) {
// 合并消息 - https://web.sdk.qcloud.com/im/doc/zh-cn/Message.html#.MergerPayload
} else if (message.type === TIM.TYPES.MSG_LOCATION) {
// 地理位置消息 - https://web.sdk.qcloud.com/im/doc/zh-cn/Message.html#.LocationPayload
} else if (message.type === TIM.TYPES.MSG_GRP_TIP) {
// 群提示消息 - https://web.sdk.qcloud.com/im/doc/zh-cn/Message.html#.GroupTipPayload
} else if (message.type === TIM.TYPES.MSG_GRP_SYS_NOTICE) {
// 群系统通知 - https://web.sdk.qcloud.com/im/doc/zh-cn/Message.html#.GroupSystemNoticePayload
}
});
};
tim.on(TIM.EVENT.MESSAGE_RECEIVED, onMessageReceived);
历史消息
// 打开某个会话时,第一次拉取消息列表
let promise = tim.getMessageList({conversationID: 'C2Ctest'});
promise.then(function(imResponse) {
const messageList = imResponse.data.messageList; // 消息列表。
const nextReqMessageID = imResponse.data.nextReqMessageID; // 用于续拉,分页续拉时需传入该字段。
const isCompleted = imResponse.data.isCompleted; // 表示是否已经拉完所有消息。
});
// 下拉查看更多消息
let promise = tim.getMessageList({conversationID: 'C2Ctest', nextReqMessageID});
promise.then(function(imResponse) {
const messageList = imResponse.data.messageList; // 消息列表。
const nextReqMessageID = imResponse.data.nextReqMessageID; // 用于续拉,分页续拉时需传入该字段。
const isCompleted = imResponse.data.isCompleted; // 表示是否已经拉完所有消息。
});
获取会话列表
接入侧可通过调用 getConversationList 接口主动获取会话列表。
获取全量的会话列表
// 获取全量的会话列表
let promise = tim.getConversationList();
promise.then(function(imResponse) {
const conversationList = imResponse.data.conversationList; // 全量的会话列表,用该列表覆盖原有的会话列表
const isSyncCompleted = imResponse.data.isSyncCompleted; // 从云端同步会话列表是否完成
}).catch(function(imError) {
console.warn('getConversationList error:', imError); // 获取会话列表失败的相关信息
});
获取指定的会话列表
// 获取指定的会话列表
let promise = tim.getConversationList([conversationID1, conversationID2]);
promise.then(function(imResponse) {
const conversationList = imResponse.data.conversationList; // 缓存中已存在的指定的会话列表
}).catch(function(imError) {
console.warn('getConversationList error:', imError); // 获取会话列表失败的相关信息
});
获取所有的群会话
// 获取所有的群会话
let promise = tim.getConversationList({ type: TIM.TYPES.CONV_GROUP });
promise.then(function(imResponse) {
const conversationList = imResponse.data.conversationList; // 会话列表
});
获取所有的“标星”会话
// 获取所有的“标星”会话
let promise = tim.getConversationList({ markType: TIM.TYPES.CONV_MARK_TYPE_STAR });
promise.then(function(imResponse) {
const conversationList = imResponse.data.conversationList; // 会话列表
});
获取指定会话分组下的所有会话
// 获取指定会话分组下的所有会话
let promise = tim.getConversationList({ groupName: 'Suppliers' });
promise.then(function(imResponse) {
const conversationList = imResponse.data.conversationList; // 会话列表
});
监听会话列表更新事件
接入侧监听 TIM.EVENT.CONVERSATION_LIST_UPDATED 事件,获取会话列表更新的通知。
示例
let onConversationListUpdated = function(event) {
console.log(event.data); // 包含 Conversation 实例的数组
};
tim.on(TIM.EVENT.CONVERSATION_LIST_UPDATED, onConversationListUpdated);
置顶会话
功能描述
会话置顶,指的是把好友或者群会话固定在会话列表的最前面,方便用户查找。置顶状态会存储在服务器,切换终端设备后,置顶状态会同步到新设备上。
调用接口成功后会话列表重新排序,SDK 会派发事件 TIM.EVENT.CONVERSATION_LIST_UPDATED。
// 置顶会话,v2.14.0起支持
let promise = tim.pinConversation({ conversationID: 'C2CExample', isPinned: true });
promise.then(function(imResponse) {
// 置顶会话成功
const { conversationID } = imResponse.data; // 被置顶的会话 ID
}).catch(function(imError) {
console.warn('pinConversation error:', imError); // 置顶会话失败的相关信息
});
// 取消置顶会话,v2.14.0起支持
let promise = tim.pinConversation({ conversationID: 'C2CExample', isPinned: false });
promise.then(function(imResponse) {
// 取消置顶会话成功
const { conversationID } = imResponse.data; // 被取消置顶的会话 ID
}).catch(function(imError) {
console.warn('pinConversation error:', imError); // 取消置顶会话失败的相关信息
});
删除会话
功能描述
在删除好友或退出群组后,如果不需要查看好友或群会话的历史消息,可以选择删除会话。会话删除默认关闭多端同步,可在 即时通信 IM 控制台 开启多端同步。
let promise = tim.deleteConversation('C2CExample');
promise.then(function(imResponse) {
// 删除会话成功
const { conversationID } = imResponse.data; // 被删除的会话 ID
}).catch(function(imError) {
console.warn('deleteConversation error:', imError); // 删除会话失败的相关信息
});
具体建议还是打开官网进行开发 功能不难 本次记录只是方便后期相同需求开发快速迭代
chitchat.wxml
<!--pages/contact/contact.wxml-->
<custom-nav-bar backgroundColor="#000" color="#fff" isBottomBorder="{{false}}">
<view class="nav-box">
{{ navbarTitle }}
</view>
</custom-nav-bar>
<view class="msg-list-box">
<scroll-view scroll-y scroll-into-view='{{toView}}' scroll-with-animation bindscrolltoupper="bindscrolltoupperHandler" style='height: 80vh;' enable-passive bindtap="clickScrollView">
<!-- <view class='scrollMsg'> -->
<block wx:key="{{ index }}" wx:for='{{messageList}}'>
<view wx:if="{{ item.showMessageTime }}" class="message-time">
<text>{{ item.messageTime }}</text>
</view>
<!--(右) -->
<view wx:if="{{ item.flow == 'out' }}" id='msg-{{index}}' class="right-msg-list-box">
<view class="remoteAudioUrl" bindtap="innerAudioHandler" data-audio="{{ item.payload.remoteAudioUrl }}" style="justify-content: flex-end;padding-right: 30rpx;width: {{ item.payload.second*20 }}rpx;max-width: 650rpx;min-width: 132rpx;" wx:if="{{ item.type == 'TIMSoundElem' }}">
<text style="color: #fff;margin-right: 20rpx;">{{ item.payload.second }}''</text>
<image src="https://tongxuecool.oss-cn-beijing.aliyuncs.com/applet-tongxuecool/im-202302281147%E8%AF%AD%E9%9F%B3%20%E6%8B%B7%E8%B4%9D%402x.png" mode="widthFix" style="width: 22rpx;" />
</view>
<view class='rightMsg' wx:if="{{ item.type == 'TIMTextElem' }}">{{item.payload.text}}</view>
<image style="width: 300rpx;border-radius: 10rpx;" bindtap="getLookBigImage" data-imgurl="{{item.payload.imageInfoArray[0].imageUrl}}" mode="widthFix" wx:if="{{ item.type == 'TIMImageElem' }}" src="{{ item.payload.imageInfoArray[0].imageUrl }}"></image>
<!-- <view wx:if="{{ item.type == 'TIMTextElem' || item.type == 'TIMSoundElem' }}" style='width: 4vw; height: 11vw; margin-right: 16rpx; display: flex; align-items: center; z-index: 9;'>
<image style='width: 4vw;' src='https://tongxuecool.oss-cn-beijing.aliyuncs.com/applet-tongxuecool/im---%E4%B8%89%E8%A7%92%E5%BD%A2%201%20%E6%8B%B7%E8%B4%9D%207%402x.png' mode='widthFix'></image>
</view> -->
<view class="right-msg-list-avatar-box">
<image class="right-msg-list-avatar" src='https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F4582899c-cff4-4363-a04f-908be3513443%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1679795943&t=da989d03216a8a1f6ad8dc989399f1c0'></image>
</view>
</view>
<!-- (左) -->
<view wx:else id='msg-{{index}}' class="left-msg-list">
<view style='width: 70rpx; height: 70rpx;margin-right: 30rpx;'>
<image style='width: 70rpx; height: 70rpx; border-radius: 50%;' src='https://img1.baidu.com/it/u=4131860888,2773799558&fm=253&fmt=auto&app=120&f=JPEG?w=1280&h=800'></image>
</view>
<!-- <view wx:if="{{ item.type == 'TIMTextElem' || item.type == 'TIMSoundElem' }}" style='width: 4vw; height: 11vw; margin-left: 16rpx; display: flex; align-items: center; z-index: 9;'>
<image style='width: 4vw;' src='https://tongxuecool.oss-cn-beijing.aliyuncs.com/applet-tongxuecool/im---%E4%B8%89%E8%A7%92%E5%BD%A2%201%20%E6%8B%B7%E8%B4%9D%206%402x.png' mode='widthFix'></image>
</view> -->
<view class='leftMsg' wx:if="{{ item.type == 'TIMTextElem' }}">{{item.payload.text}}</view>
<image bindtap="getLookBigImage" data-imgurl="{{ item.payload.imageInfoArray[0].imageUrl }}" style="width: 300rpx;border-radius: 10rpx;" mode="widthFix" wx:if="{{ item.type == 'TIMImageElem' }}" src="{{ item.payload.imageInfoArray[0].imageUrl }}"></image>
<view class="remoteAudioUrl" bindtap="innerAudioHandler" data-audio="{{ item.payload.remoteAudioUrl }}" style="justify-content: flex-start;padding-left: 30rpx;width: {{ item.payload.second*20 }}rpx;max-width: 650rpx;min-width: 132rpx;" wx:if="{{ item.type == 'TIMSoundElem' }}">
<image src="https://tongxuecool.oss-cn-beijing.aliyuncs.com/applet-tongxuecool/im-20230228%E8%AF%AD%E9%9F%B3%402x.png" mode="widthFix" style="width: 22rpx;" />
<text style="color: #fff;margin-right: 20rpx;">{{ item.payload.second }}''</text>
</view>
</view>
</block>
<!-- </view> -->
<!-- 占位 -->
<view style='width: 100%; height: 18vw;'></view>
</scroll-view>
<!-- {{inputBottom}} -->
<view class="inputRoom" style='bottom: {{ inputBottom }};height: {{ inputRoomHeight }};'>
<view style="display: flex;align-items: center;box-sizing: border-box;padding: 20rpx;width: 100%;background-color: #000000;">
<image style='width: 7vw;' bindtap="changeAudioHandler" data-flag="{{ true }}" wx:if="{{ !audioFlag }}" src='https://tongxuecool.oss-cn-beijing.aliyuncs.com/applet-tongxuecool/im-20230224%E8%AF%AD%E9%9F%B3%20%281%29%402x.png' mode='widthFix'></image>
<image style='width: 7vw;' bindtap="changeAudioHandler" data-flag="{{ false }}" wx:else src='https://tongxuecool.oss-cn-beijing.aliyuncs.com/applet-tongxuecool/im-20230227%E9%94%AE%E7%9B%98%402x.png' mode='widthFix'></image>
<input bindconfirm='sendClick' wx:if="{{ !audioFlag }}" adjust-position='{{false}}' model:value='{{inputVal}}' confirm-type='send' bindfocus='focus' bindblur='blur'></input>
<view wx:else class="audio" bind:longpress="handleRecordStart" bind:touchmove="handleTouchMove" bind:touchend="handleRecordStop">
<text>按住</text>
<text>说话</text>
</view>
<image style='width: 7vw; margin-left: 3.2vw;' bindtap="changeEmoji" src='https://tongxuecool.oss-cn-beijing.aliyuncs.com/applet-tongxuecool/im-20230224%E8%A1%A8%E6%83%85%402x.png' mode='widthFix'></image>
<image style='width: 7vw; margin-left: 3.2vw;' bindtap="multimediaHandler" src='https://tongxuecool.oss-cn-beijing.aliyuncs.com/applet-tongxuecool/im-20230224icon_%E6%B7%BB%E5%8A%A0%402x.png' mode='widthFix'></image>
</view>
<!-- 表情 -->
<view wx:if="{{displayFlag === 'emoji'}}" class="TUI-Emoji-area">
<Emoji bind:enterEmoji="appendMessage" />
</view>
<!-- 图片 -->
<view wx:if="{{displayFlag === 'extension'}}" class="TUI-Extensions">
<view class="TUI-Extension-slot" style="margin-left: 40rpx;" bindtap="handleSendImage">
<view class="extension-box">
<image class="TUI-Extension-icon" mode="widthFix" src="https://tongxuecool.oss-cn-beijing.aliyuncs.com/applet-tongxuecool/im-20230227%E6%8B%8D%E6%91%84-%E9%80%89%E4%B8%AD%402x%20%282%29.png" />
</view>
<view class="TUI-Extension-slot-name">照片</view>
</view>
<view class="TUI-Extension-slot" style="margin-left: 90rpx;" bindtap="handleSendPicture">
<view class="extension-box">
<image class="TUI-Extension-icon" mode="widthFix" src="https://tongxuecool.oss-cn-beijing.aliyuncs.com/applet-tongxuecool/im-20230227%E6%8B%8D%E6%91%84-%E9%80%89%E4%B8%AD%402x%20%281%29.png" />
</view>
<view class="TUI-Extension-slot-name">拍摄</view>
</view>
</view>
</view>
<!-- 正在录音动效 -->
<view class="record-modal" wx:if="{{recordFlag}}" bind:longpress="handleRecordStart" bind:touchmove="handleTouchMove" bind:touchend="handleRecordStop">
<view class="wrapper">
<view class="modal-loading">
</view>
</view>
<view class="modal-title">
正在录音
</view>
</view>
</view>
chitchat.wxss
/* pages/contact/contact.wxss */
page {
background-color: #080808;
}
.msg-list-box {
margin-top: 150rpx;
background-color: #080808;
}
.nav-box {
display: flex;
justify-content: flex-start;
align-items: center;
font-size: 30rpx;
font-family: PingFang SC;
font-weight: 500;
color: #F5F5F5;
}
.inputRoom {
box-sizing: border-box;
width: 100vw;
background-color: #000000;
position: fixed;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
z-index: 20;
}
input {
/* width: 76vw; */
/* height: 9.33vw; */
width: 518rpx;
height: 64rpx;
background-color: #333333;
border-radius: 40rpx;
margin-left: 2vw;
padding: 0 3vw;
font-size: 28rpx;
color: #e6e6e6;
}
.right-msg-list-box {
display: flex;
justify-content: flex-end;
padding: 3vw 3vw 3vw 11vw;
}
.right-msg-list-avatar-box {
width: 70rpx;
height: 70rpx;
margin-left: 30rpx;
}
.right-msg-list-avatar {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
}
.left-msg-list {
display: flex;
padding: 3vw 11vw 3vw 3vw;
}
.leftMsg {
font-size: 35rpx;
color: #E6E6E6;
line-height: 7vw;
padding: 2vw 2.5vw;
background: #1F1F1F;
/* margin-left: -1.6vw; */
border-radius: 10rpx;
z-index: 10;
}
.rightMsg {
font-size: 35rpx;
color: #E6E6E6;
line-height: 7vw;
padding: 2vw 2.5vw;
background: #1F1F1F;
/* margin-right: -1.6vw; */
border-radius: 10rpx;
z-index: 10;
}
/* 表情 */
.TUI-Emoji-area {
width: 100vw;
height: 200px;
background-color: #000000;
padding-bottom: 100rpx;
}
/* 多媒体 */
.TUI-Extensions {
display: flex;
flex-wrap: wrap;
width: 100vw;
height: 400rpx;
background-color: #000000;
}
.TUI-Extension-slot {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 48rpx;
}
.extension-box {
width: 100rpx;
height: 100rpx;
background: #333333;
border-radius: 28rpx;
display: flex;
justify-content: center;
align-items: center;
}
.TUI-Extension-icon {
width: 51rpx;
border-radius: 0;
}
.TUI-Extension-slot-name {
font-size: 26rpx;
font-family: PingFang SC;
font-weight: 500;
color: #B3B3B3;
margin-top: 12rpx;
text-align: center;
}
.message-time {
font-size: 22rpx;
font-family: PingFang SC;
font-weight: 500;
color: #CCCCCC;
display: flex;
justify-content: center;
align-items: center;
padding: 30rpx;
}
.audio {
width: 518rpx;
height: 64rpx;
background-color: #333333;
border-radius: 40rpx;
margin-left: 2vw;
padding: 0 3vw;
font-size: 28rpx;
color: #e6e6e6;
display: flex;
justify-content: center;
align-items: center;
}
.audio:active {
background-color: #5e5c5c;
}
/* 正在录音 */
.record-modal {
height: 300rpx;
width: 60vw;
background-color: #333;
opacity: 0.8;
position: fixed;
top: 670rpx;
z-index: 9999;
left: 20vw;
border-radius: 24rpx;
display: flex;
flex-direction: column;
}
.record-modal .wrapper {
display: flex;
height: 200rpx;
box-sizing: border-box;
padding: 10vw;
}
.record-modal .wrapper .modal-loading {
opacity: 1;
width: 40rpx;
height: 16rpx;
border-radius: 4rpx;
background-color: #006fff;
animation: loading 2s cubic-bezier(0.17, 0.37, 0.43, 0.67) infinite;
}
.modal-title {
text-align: center;
color: #fff;
}
@keyframes loading {
0% {
transform: translate(0, 0)
}
50% {
transform: translate(30vw, 0);
background-color: #f5634a;
width: 40px;
}
100% {
transform: translate(0, 0);
}
}
.remoteAudioUrl {
box-sizing: border-box;
height: 75rpx;
background: #1F1F1F;
border-radius: 10rpx;
display: flex;
align-items: center;
font-size: 30rpx;
color: #e6e6e6;
}
chitchat.js
import dayjs from './../../../../utils/dayjs'
const app = getApp();
var windowHeight = wx.getSystemInfoSync().windowHeight;
var keyHeight = 0;
/**
* 计算msg总高度
*/
function calScrollHeight(that, keyHeight) {
var query = wx.createSelectorQuery();
query.select('.scrollMsg').boundingClientRect(function (rect) {}).exec();
}
const recorderManager = wx.getRecorderManager();
const innerAudioContext = wx.createInnerAudioContext({
useWebAudioImplement: true
})
Page({
/**
* 页面的初始数据
*/
data: {
navbarTitle: "",
audioFlag: false,
messageTime: "",
showMessageTime: false,
inputRoomHeight: '16vw',
scrollHeight: '',
inputBottom: 0,
toUserId: null, //被发送人的用户id
messageList: [], //历史记录的消息列表
displayFlag: '',
inputVal: '',
nextReqMessageID: null,
isCompleted: false,
recordFlag: false,
recordOptions: {
duration: 60000,
sampleRate: 44100,
numberOfChannels: 1,
encodeBitRate: 192000,
format: 'aac'
},
},
changeEmoji() {
this.setData({
scrollHeight: '',
inputBottom: 0,
audioFlag: false
})
this.setData({
displayFlag: 'emoji',
inputBottom: '0px',
inputRoomHeight: '200px'
})
},
multimediaHandler() {
this.setData({
scrollHeight: '',
inputBottom: 0,
})
this.setData({
displayFlag: 'extension',
inputBottom: '0px',
inputRoomHeight: '200px'
})
},
/**
* 点击空白处
* */
clickScrollView() {
this.setData({
displayFlag: '',
inputBottom: '0',
inputRoomHeight: '16vw',
})
},
/**
* 语音切换
* */
changeAudioHandler(e) {
let {
flag
} = e.currentTarget.dataset;
this.setData({
audioFlag: flag,
inputRoomHeight: '16vw',
displayFlag: ""
})
},
/**
* 语音播报
* */
innerAudioHandler(e) {
let {
audio
} = e.currentTarget.dataset;
innerAudioContext.src = audio;
if (innerAudioContext.paused) {
innerAudioContext.play();
} else {
innerAudioContext.stop();
}
},
/**
* 拍摄
* @param sourceType 选择图片的来源
* */
handleSendPicture() {
this.publicImageUpload('camera')
},
/**
*
* 从相册选取
* @param sourceType 选择图片的来源
*
* */
handleSendImage() {
this.publicImageUpload('album')
},
/**
*
* 选择图片和拍摄公共方法
*
* */
publicImageUpload(sourceType) {
let {
messageList
} = this.data
if (app.globalData.TIM.EVENT.SDK_READY == 'sdkStateReady') {
wx.chooseImage({
sourceType: [sourceType],
count: 1,
success: res => {
wx.showLoading({
title: '',
})
let message = app.globalData.tim.createImageMessage({
to: 'zhanggenyuan',
conversationType: app.globalData.TIM.TYPES.CONV_C2C,
payload: {
file: res
},
onProgress: event => {
console.log('file uploading:', event)
if (event == 1) {
wx.hideLoading()
}
}
});
let promise = app.globalData.tim.sendMessage(message);
promise.then(imResponse => {
this.getMessageListHandler()
this.clickScrollView()
}).catch(imError => {
console.warn('sendMessage error:', imError);
});
},
complete: () => {
wx.hideLoading()
}
})
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
console.log(options)
this.setData({
toUserId: options.toUserId,
navbarTitle: "谢霆锋"
})
if (app.globalData.TIM.EVENT.SDK_READY == 'sdkStateReady') {
this.getMessageListHandler(options.toUserId)
}
app.globalData.tim.on(app.globalData.TIM.EVENT.MESSAGE_RECEIVED, this.onMessageReceived);
setTimeout(() => {
this.setData({
toView: 'msg-' + (this.data.messageList.length - 1),
})
}, 800)
},
/**
*
* 表情接收事件
*
* */
appendMessage(emoji) {
let {
message
} = emoji.detail
let {
inputVal
} = this.data
this.setData({
inputVal: inputVal + message
})
},
/**
* 语音相关
* */
handleRecordStart() {
console.log("开始")
let _this = this;
wx.getSetting({
success: (res) => {
let auth = res.authSetting['scope.record']
if (auth === false) {
wx.openSetting({
success: function (res) {
let auth = res.authSetting['scope.record']
if (auth === true) {
wx.showToast({
title: '授权成功',
icon: 'success',
duration: 1500
})
} else {
wx.showToast({
title: '授权失败',
icon: 'none',
duration: 1500
})
}
}
})
} else if (auth === true) {
_this.setData({
recordFlag: true
})
recorderManager.start(this.data.recordOptions)
} else {
wx.authorize({
scope: 'scope.record',
success: () => {
wx.showToast({
title: '授权成功',
icon: 'success',
duration: 1500
})
}
})
}
},
fail: function () {
wx.showToast({
title: '授权失败',
icon: 'none',
duration: 1500
})
}
})
},
handleTouchMove() {
},
handleRecordStop() {
let _this = this
_this.setData({
recordFlag: false
})
recorderManager.stop()
recorderManager.onStop(res => {
console.log(res)
if (app.globalData.TIM.EVENT.SDK_READY == 'sdkStateReady') {
const message = app.globalData.tim.createAudioMessage({
to: 'zhanggenyuan',
conversationType: app.globalData.TIM.TYPES.CONV_C2C,
payload: {
file: res
},
});
let promise = app.globalData.tim.sendMessage(message);
promise.then(function (imResponse) {
console.log(imResponse);
_this.setData({
messageList: [..._this.data.messageList, imResponse.data.message]
})
setTimeout(() => {
_this.setData({
toView: 'msg-' + (_this.data.messageList.length - 1),
})
}, 300)
}).catch(function (imError) {
console.warn('sendMessage error:', imError);
});
}
})
},
/**
*
* 监听接收消息事件
*
* */
onMessageReceived(event) {
let {
messageList
} = this.data
let _this = this
const receiveMessageList = event.data;
receiveMessageList.forEach((message) => {
_this.messageTimeForShow(message)
if (message.type === app.globalData.TIM.TYPES.MSG_TEXT) {
// 文本消息
_this.setData({
messageList: [...messageList, message]
})
} else if (message.type === app.globalData.TIM.TYPES.MSG_IMAGE) {
_this.setData({
messageList: [...messageList, message]
})
// 图片消息
} else if (message.type === app.globalData.TIM.TYPES.MSG_SOUND) {
_this.setData({
messageList: [...messageList, message]
})
// 音频消息
} else if (message.type === app.globalData.TIM.TYPES.MSG_VIDEO) {
// 视频消息
} else if (message.type === app.globalData.TIM.TYPES.MSG_FILE) {
// 文件消息
} else if (message.type === app.globalData.TIM.TYPES.MSG_CUSTOM) {
// 自定义消息
} else if (message.type === app.globalData.TIM.TYPES.MSG_MERGER) {
// 合并消息
} else if (message.type === app.globalData.TIM.TYPES.MSG_LOCATION) {
// 地理位置消息
} else if (message.type === app.globalData.TIM.TYPES.MSG_GRP_TIP) {
// 群提示消息
} else if (message.type === app.globalData.TIM.TYPES.MSG_GRP_SYS_NOTICE) {
// 群系统通知
}
_this.setData({
toView: 'msg-' + (_this.data.messageList.length - 1),
})
});
},
/**
* 点击查看大图
* @param Event
* */
getLookBigImage(event) {
console.log(event)
let {
imgurl
} = event.currentTarget.dataset
console.log(imgurl)
wx.previewImage({
current: imgurl,
urls: [imgurl]
})
},
/**
*
* 拉取会话历史记录
*
* */
getMessageListHandler() {
console.log("进来了")
let promise = app.globalData.tim.getMessageList({
conversationID: `C2Czhanggenyuan`
});
promise.then((imResponse) => {
const messageList = imResponse.data.messageList;
const nextReqMessageID = imResponse.data.nextReqMessageID;
const isCompleted = imResponse.data.isCompleted;
this.setData({
messageList: messageList,
nextReqMessageID,
isCompleted
})
console.log(messageList, imResponse.data, "历史会话")
setTimeout(() => {
this.setData({
toView: 'msg-' + (this.data.messageList.length - 1),
})
}, 200)
});
},
/**
* 滚动到顶部
* */
bindscrolltoupperHandler() {
if (app.globalData.TIM.EVENT.SDK_READY == 'sdkStateReady') {
if (this.data.isCompleted) return
console.log("进去到徐拉 ", this.data.messageList, this.data.nextReqMessageID)
let _this = this;
let promise = app.globalData.tim.getMessageList({
conversationID: 'C2Czhanggenyuan',
nextReqMessageID: _this.data.nextReqMessageID
});
promise.then(function (imResponse) {
console.log(imResponse.data.messageList, imResponse.data, "********************")
const messageList = imResponse.data.messageList;
const nextReqMessageID = imResponse.data.nextReqMessageID;
const isCompleted = imResponse.data.isCompleted;
if (messageList.length == 0) {
return
}
_this.setData({
messageList: [...messageList, ..._this.data.messageList],
nextReqMessageID,
isCompleted
})
});
}
},
/**
* 发送消息
* */
sendClick() {
let _this = this
let {
toUserId
} = _this.data
if (app.globalData.TIM.EVENT.SDK_READY == 'sdkStateReady') {
let message = app.globalData.tim.createTextMessage({
to: "zhanggenyuan",
conversationType: app.globalData.TIM.TYPES.CONV_C2C,
payload: {
text: _this.data.inputVal
},
});
// 2. 发送消息
let promise = app.globalData.tim.sendMessage(message);
promise.then(function (imResponse) {
// 发送成功
console.log(imResponse, "发送成功");
_this.messageTimeForShow(imResponse.data.message)
setTimeout(() => {
_this.setData({
inputVal: "",
messageList: [..._this.data.messageList, imResponse.data.message]
})
_this.setData({
toView: 'msg-' + (_this.data.messageList.length - 1),
})
}, 300)
}).catch(function (imError) {
// 发送失败
console.warn('sendMessage error:', imError);
});
}
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 获取聚焦
*/
focus(e) {
keyHeight = e.detail.height;
this.setData({
scrollHeight: (windowHeight - keyHeight) + 'px'
});
this.setData({
toView: 'msg-' + (this.data.messageList.length - 1),
inputBottom: keyHeight + 'px',
displayFlag: "",
inputRoomHeight: '16vw',
})
},
//失去聚焦(软键盘消失)
blur(e) {
this.setData({
scrollHeight: '',
inputBottom: 0
})
this.setData({
toView: 'msg-' + (this.data.messageList.length - 1),
displayFlag: "",
})
},
/**
* 退回上一页
*/
toBackClick: function () {
wx.switchTab({
url: '/pages/tabBar/appContainer/appContainer?pageidx=3',
})
},
// 展示消息时间
messageTimeForShow(messageTime) {
const interval = 5 * 60 * 1000;
const nowTime = Math.floor(messageTime.time / 10) * 10 * 1000;
if (this.data.messageList.length > 0) {
const lastTime = this.data.messageList.slice(-1)[0].time * 1000;
if (nowTime - lastTime > interval) {
this.data.messageTime = dayjs(nowTime);
let timer = dayjs(nowTime).format('MM-DD HH:mm')
let ts = timer.split(" ")
let m = ts[0].split("-")[0]
let d = ts[0].split("-")[1]
messageTime.messageTime = `${m}月${d}日 ${ts[1]}`
messageTime.showMessageTime = true
} else {
messageTime.showMessageTime = false
}
}
},
})
emoji.wxml
<scroll-view scroll-y="true" enable-flex="true" class="TUI-Emoji">
<view class="TUI-emoji-image" wx:for="{{emojiList}}" wx:key="index" >
<view data-value="{{ item.value }}" bindtap="handleEnterEmoji" style="width: 100%;height: 100%;font-size: 48rpx;">
{{ item.value }}
</view>
</view>
</scroll-view>
emoji.wxss
.TUI-Emoji {
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
width: 100%;
height: 100%;
margin-left: 4vw;
}
.TUI-emoji-image {
width: 9vw;
height: 9vw;
margin: 2vw;
}
.TUI-emoji-image > image {
width: 100%;
height: 100%;
}
emoji.js
import emoji from './../../../../../../utils/emoji'
Component({
/**
* 组件的属性列表
*/
properties: {
},
/**
* 组件的初始数据
*/
data: {
emojiList: emoji.emoji,
},
lifetimes: {
},
/**
* 组件的方法列表
*/
methods: {
handleEnterEmoji(event) {
this.triggerEvent('enterEmoji', {
message: event.currentTarget.dataset.value,
});
},
},
});
emoji.js文件
var emoji = [{
name: "微笑",
value: "😀"
},
{
name: "呲牙",
value: "😁"
},
{
name: "撇嘴",
value: "😐"
},
{
name: "色",
value: "😍"
},
{
name: "发呆",
value: "😳"
},
{
name: "得意",
value: "😎"
},
{
name: "流泪",
value: "😭"
},
{
name: "害羞",
value: "🙈"
},
{
name: "闭嘴",
value: "🙊"
},
{
name: "睡觉",
value: "💤"
},
{
name: "大哭",
value: "😭"
},
{
name: "尴尬",
value: "😂"
},
{
name: "发怒",
value: "😡"
},
{
name: "调皮",
value: "😜"
},
{
name: "咄牙",
value: "😁"
},
{
name: "惊讶",
value: "😮"
},
{
name: "难过",
value: "😔"
},
{
name: "囹",
value: "😂"
},
{
name: "抓狂",
value: "😀"
},
{
name: "吐",
value: "🤮"
},
{
name: "偷笑",
value: "😄"
},
{
name: "愉快",
value: "😀"
},
{
name: "白眼",
value: "🙄"
},
{
name: "傲慢",
value: "😀"
},
{
name: "困",
value: "🥱"
},
{
name: "惊恐",
value: "😨"
},
{
name: "流汗",
value: "💦"
},
{
name: "憨笑",
value: "😀"
},
{
name: "悠闲",
value: "🆓"
},
{
name: "奋斗",
value: "😀"
},
{
name: "咒骂",
value: "🤬"
},
{
name: "疑问",
value: "❓"
},
{
name: "嘘",
value: "🤫"
},
{
name: "晕",
value: "😵"
},
{
name: "衰",
value: "😞"
},
{
name: "敲打",
value: "🤛"
},
{
name: "再见",
value: "👋🏻"
},
{
name: "擦汗",
value: "💦"
},
{
name: "抠鼻",
value: "抠👃"
},
{
name: "鼓掌",
value: "👏🏻"
},
{
name: "坏笑",
value: "😀"
},
{
name: "左哼哼",
value: "👈"
},
{
name: "右哼哼",
value: "👉🏻"
},
{
name: "鄙视",
value: "🖕🏻"
},
{
name: "委屈",
value: "🥺"
},
{
name: "快哭啦",
value: "😭"
},
{
name: "阴险",
value: "⛅"
},
{
name: "亲亲",
value: "😙"
},
{
name: "可怜",
value: "🥺"
},
{
name: "啤酒",
value: "🍺"
},
{
name: "咖啡",
value: "☕"
},
{
name: "猪头",
value: "🐷"
},
{
name: "凋谢",
value: "🥺"
},
{
name: "嘴唇",
value: "🥴"
},
{
name: "爱心",
value: "❤"
},
{
name: "心碎",
value: "💔"
},
{
name: "拥抱",
value: "🤗"
},
{
name: "强",
value: "💪"
},
{
name: "弱",
value: "😭"
},
{
name: "握手",
value: "🤝"
},
{
name: "胜利",
value: "✌"
},
{
name: "抱拳",
value: "👊"
},
{
name: "OK",
value: "👌"
},
{
name: "笑脸",
value: "😀"
},
{
name: "生病",
value: "😷"
},
{
name: "笑哭",
value: "😂"
},
{
name: "吐舌",
value: "😛"
},
{
name: "懵逼",
value: "❓"
},
{
name: "失望",
value: "😞"
},
{
name: "呵呵",
value: "🙂"
},
{
name: "嘿哈",
value: "😀"
},
{
name: "捂脸",
value: "🤦"
},
{
name: "奸笑",
value: "😼"
},
{
name: "机智",
value: "😀"
},
{
name: "皱眉",
value: "🙍"
},
{
name: "耶",
value: "✌"
},
{
name: "鬼脸",
value: "🤪"
},
{
name: "双手合十",
value: "🙏"
},
{
name: "加油",
value: "⛽"
},
{
name: "礼花",
value: "🎉"
},
{
name: "礼物",
value: "🧧"
},
{
name: "开心",
value: "🤤"
},
{
name: "自拍",
value: "🤳"
},
{
name: "叹气",
value: "🤫"
},
{
name: "爱",
value: "❤️"
},
{
name: "寿司",
value: "🍣"
},
{
name: "火锅",
value: "🍲"
},
{
name: "汉堡",
value: "🍔"
},
{
name: "牛排",
value: "🥩"
},
{
name: "面包",
value: "🍞"
},
{
name: "爆米花",
value: "🍿"
},
{
name: "糖果",
value: "🍬"
},
{
name: "拔草",
value: "☘︎"
},
{
name: "种草",
value: "🍀"
},
{
name: "一人食",
value: "🍜"
},
{
name: "鸭子",
value: "🦆"
},
{
name: "K歌",
value: "🎤"
},
{
name: "婴儿",
value: "👶"
},
{
name: "指甲油",
value: "💅"
},
{
name: "化妆镜",
value: "🔭"
},
{
name: "口红",
value: "💄"
},
{
name: "钻石",
value: "💎"
},
{
name: "发廊",
value: "💇🏼"
},
{
name: "吹风机",
value: "🌬️"
},
]
module.exports = {
emoji
}
dayjs 下载下来的压缩源码 有需要的自行下载
!(function (t, e) {
'object' === typeof exports && 'undefined' !== typeof module ? module.exports = e() : 'function' === typeof define && define.amd ? define(e) : (t = 'undefined' !== typeof globalThis ? globalThis : t || self).dayjs = e();
}(this, (() => {
'use strict';const t = 1e3; const e = 6e4; const n = 36e5; const r = 'millisecond'; const i = 'second'; const s = 'minute'; const u = 'hour'; const a = 'day'; const o = 'week'; const f = 'month'; const h = 'quarter'; const c = 'year'; const d = 'date'; const $ = 'Invalid Date'; const l = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/; const y = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g; const M = { name: 'en', weekdays: 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), months: 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_') }; const m = function (t, e, n) {
const r = String(t);return !r || r.length >= e ? t : `${Array(e + 1 - r.length).join(n)}${t}`;
}; const g = { s: m, z(t) {
const e = -t.utcOffset(); const n = Math.abs(e); const r = Math.floor(n / 60); const i = n % 60;return `${(e <= 0 ? '+' : '-') + m(r, 2, '0')}:${m(i, 2, '0')}`;
}, m: function t(e, n) {
if (e.date() < n.date()) return -t(n, e);const r = 12 * (n.year() - e.year()) + (n.month() - e.month()); const i = e.clone().add(r, f); const s = n - i < 0; const u = e.clone().add(r + (s ? -1 : 1), f);return +(-(r + (n - i) / (s ? i - u : u - i)) || 0);
}, a(t) {
return t < 0 ? Math.ceil(t) || 0 : Math.floor(t);
}, p(t) {
return { M: f, y: c, w: o, d: a, D: d, h: u, m: s, s: i, ms: r, Q: h }[t] || String(t || '').toLowerCase()
.replace(/s$/, '');
}, u(t) {
return void 0 === t;
} }; let D = 'en'; const v = {};v[D] = M;const p = function (t) {
return t instanceof _;
}; const S = function (t, e, n) {
let r;if (!t) return D;if ('string' === typeof t)v[t] && (r = t), e && (v[t] = e, r = t);else {
const i = t.name;v[i] = t, r = i;
} return !n && r && (D = r), r || !n && D;
}; const w = function (t, e) {
if (p(t)) return t.clone();const n = 'object' === typeof e ? e : {};return n.date = t, n.args = arguments, new _(n);
}; const O = g;O.l = S, O.i = p, O.w = function (t, e) {
return w(t, { locale: e.$L, utc: e.$u, x: e.$x, $offset: e.$offset });
};
var _ = (function () {
function M(t) {
this.$L = S(t.locale, null, !0), this.parse(t);
} const m = M.prototype;return m.parse = function (t) {
this.$d = (function (t) {
const e = t.date; const n = t.utc;if (null === e) return new Date(NaN);if (O.u(e)) return new Date;if (e instanceof Date) return new Date(e);if ('string' === typeof e && !/Z$/i.test(e)) {
const r = e.match(l);if (r) {
const i = r[2] - 1 || 0; const s = (r[7] || '0').substring(0, 3);return n ? new Date(Date.UTC(r[1], i, r[3] || 1, r[4] || 0, r[5] || 0, r[6] || 0, s)) : new Date(r[1], i, r[3] || 1, r[4] || 0, r[5] || 0, r[6] || 0, s);
}
} return new Date(e);
}(t)), this.$x = t.x || {}, this.init();
}, m.init = function () {
const t = this.$d;this.$y = t.getFullYear(), this.$M = t.getMonth(), this.$D = t.getDate(), this.$W = t.getDay(), this.$H = t.getHours(), this.$m = t.getMinutes(), this.$s = t.getSeconds(), this.$ms = t.getMilliseconds();
}, m.$utils = function () {
return O;
}, m.isValid = function () {
return !(this.$d.toString() === $);
}, m.isSame = function (t, e) {
const n = w(t);return this.startOf(e) <= n && n <= this.endOf(e);
}, m.isAfter = function (t, e) {
return w(t) < this.startOf(e);
}, m.isBefore = function (t, e) {
return this.endOf(e) < w(t);
}, m.$g = function (t, e, n) {
return O.u(t) ? this[e] : this.set(n, t);
}, m.unix = function () {
return Math.floor(this.valueOf() / 1e3);
}, m.valueOf = function () {
return this.$d.getTime();
}, m.startOf = function (t, e) {
const n = this; const r = !!O.u(e) || e; const h = O.p(t); const $ = function (t, e) {
const i = O.w(n.$u ? Date.UTC(n.$y, e, t) : new Date(n.$y, e, t), n);return r ? i : i.endOf(a);
}; const l = function (t, e) {
return O.w(n.toDate()[t].apply(n.toDate('s'), (r ? [0, 0, 0, 0] : [23, 59, 59, 999]).slice(e)), n);
}; const y = this.$W; const M = this.$M; const m = this.$D; const g = `set${this.$u ? 'UTC' : ''}`;switch (h) {
// eslint-disable-next-line no-var
case c:return r ? $(1, 0) : $(31, 11);case f:return r ? $(1, M) : $(0, M + 1);case o:var D = this.$locale().weekStart || 0; var v = (y < D ? y + 7 : y) - D;return $(r ? m - v : m + (6 - v), M);case a:case d:return l(`${g}Hours`, 0);case u:return l(`${g}Minutes`, 1);case s:return l(`${g}Seconds`, 2);case i:return l(`${g}Milliseconds`, 3);default:return this.clone();
}
}, m.endOf = function (t) {
return this.startOf(t, !1);
}, m.$set = function (t, e) {
let n; const o = O.p(t); const h = `set${this.$u ? 'UTC' : ''}`; const $ = (n = {}, n[a] = `${h}Date`, n[d] = `${h}Date`, n[f] = `${h}Month`, n[c] = `${h}FullYear`, n[u] = `${h}Hours`, n[s] = `${h}Minutes`, n[i] = `${h}Seconds`, n[r] = `${h}Milliseconds`, n)[o]; const l = o === a ? this.$D + (e - this.$W) : e;if (o === f || o === c) {
const y = this.clone().set(d, 1);y.$d[$](l), y.init(), this.$d = y.set(d, Math.min(this.$D, y.daysInMonth())).$d;
} else $ && this.$d[$](l);return this.init(), this;
}, m.set = function (t, e) {
return this.clone().$set(t, e);
}, m.get = function (t) {
return this[O.p(t)]();
}, m.add = function (r, h) {
let d; const $ = this;r = Number(r);const l = O.p(h); const y = function (t) {
const e = w($);return O.w(e.date(e.date() + Math.round(t * r)), $);
};if (l === f) return this.set(f, this.$M + r);if (l === c) return this.set(c, this.$y + r);if (l === a) return y(1);if (l === o) return y(7);const M = (d = {}, d[s] = e, d[u] = n, d[i] = t, d)[l] || 1; const m = this.$d.getTime() + r * M;return O.w(m, this);
}, m.subtract = function (t, e) {
return this.add(-1 * t, e);
}, m.format = function (t) {
const e = this; const n = this.$locale();if (!this.isValid()) return n.invalidDate || $;const r = t || 'YYYY-MM-DDTHH:mm:ssZ'; const i = O.z(this); const s = this.$H; const u = this.$m; const a = this.$M; const o = n.weekdays; const f = n.months; const h = function (t, n, i, s) {
return t && (t[n] || t(e, r)) || i[n].substr(0, s);
}; const c = function (t) {
return O.s(s % 12 || 12, t, '0');
}; const d = n.meridiem || function (t, _e, n) {
const r = t < 12 ? 'AM' : 'PM';return n ? r.toLowerCase() : r;
}; const l = { YY: String(this.$y).slice(-2), YYYY: this.$y, M: a + 1, MM: O.s(a + 1, 2, '0'), MMM: h(n.monthsShort, a, f, 3), MMMM: h(f, a), D: this.$D, DD: O.s(this.$D, 2, '0'), d: String(this.$W), dd: h(n.weekdaysMin, this.$W, o, 2), ddd: h(n.weekdaysShort, this.$W, o, 3), dddd: o[this.$W], H: String(s), HH: O.s(s, 2, '0'), h: c(1), hh: c(2), a: d(s, u, !0), A: d(s, u, !1), m: String(u), mm: O.s(u, 2, '0'), s: String(this.$s), ss: O.s(this.$s, 2, '0'), SSS: O.s(this.$ms, 3, '0'), Z: i };return r.replace(y, ((t, e) => e || l[t] || i.replace(':', '')));
}, m.utcOffset = function () {
return 15 * -Math.round(this.$d.getTimezoneOffset() / 15);
}, m.diff = function (r, d, $) {
let l; const y = O.p(d); const M = w(r); const m = (M.utcOffset() - this.utcOffset()) * e; const g = this - M; let D = O.m(this, M);return D = (l = {}, l[c] = D / 12, l[f] = D, l[h] = D / 3, l[o] = (g - m) / 6048e5, l[a] = (g - m) / 864e5, l[u] = g / n, l[s] = g / e, l[i] = g / t, l)[y] || g, $ ? D : O.a(D);
}, m.daysInMonth = function () {
return this.endOf(f).$D;
}, m.$locale = function () {
return v[this.$L];
}, m.locale = function (t, e) {
if (!t) return this.$L;const n = this.clone(); const r = S(t, e, !0);return r && (n.$L = r), n;
}, m.clone = function () {
return O.w(this.$d, this);
}, m.toDate = function () {
return new Date(this.valueOf());
}, m.toJSON = function () {
return this.isValid() ? this.toISOString() : null;
}, m.toISOString = function () {
return this.$d.toISOString();
}, m.toString = function () {
return this.$d.toUTCString();
}, M;
}()); const b = _.prototype;return w.prototype = b, [['$ms', r], ['$s', i], ['$m', s], ['$H', u], ['$W', a], ['$M', f], ['$y', c], ['$D', d]].forEach(((t) => {
b[t[1]] = function (e) {
return this.$g(e, t[0], t[1]);
};
})), w.extend = function (t, e) {
return t.$i || (t(e, _, w), t.$i = !0), w;
}, w.locale = S, w.isDayjs = p, w.unix = function (t) {
return w(1e3 * t);
}, w.en = v[D], w.Ls = v, w.p = {}, w;
})));
app.js
/**
*
* 腾讯云IM
*
* */
import TIM from 'tim-wx-sdk';
import TIMUploadPlugin from 'tim-upload-plugin';
import TIMProfanityFilterPlugin from 'tim-profanity-filter-plugin';
import {
genTestUserSig
} from './utils/debug/GenerateTestUserSig';
/**
*
* 腾讯云IM
*
* */
const config = {
userID: '3099', //User ID
SDKAPPID: xxxxx, // Your SDKAppID
SECRETKEY: 'xxxxxx', // Your secretKey
EXPIRETIME: 604800,
}
let options = {
SDKAppID: config.SDKAPPID
};
let tim = TIM.create(options);
tim.setLogLevel(0);
// 注册腾讯云即时通信 IM 上传插件
tim.registerPlugin({
'tim-upload-plugin': TIMUploadPlugin
});
// 注册腾讯云即时通信 IM 本地审核插件
tim.registerPlugin({
'tim-profanity-filter-plugin': TIMProfanityFilterPlugin
});
const userSig = genTestUserSig(config).userSig
let promise = tim.login({
userID: config.userID,
userSig
});
promise.then((imResponse) => {
console.log(imResponse.data, "登录");
if (imResponse.data.repeatLogin === true) {
console.log(imResponse.data.errorInfo);
}
}).catch(function (imError) {
console.warn('login error:', imError);
});
this.globalData.tim = tim
this.globalData.TIM = TIM
目前只是初步实现功能 正在做相关优化 记录一下 毕竟同一个功能不想写两遍 [捂眼] 懒人一个