im ok
3
app.js
@@ -1,5 +1,6 @@
|
|||||||
// app.js
|
// app.js
|
||||||
import GoEasyIM from './utils/goeasy-im-1.5.1.js';
|
import GoEasyIM from './static/lib/goeasy-im-1.5.1.js';
|
||||||
|
|
||||||
App({
|
App({
|
||||||
onLaunch: function () {
|
onLaunch: function () {
|
||||||
wx.im = GoEasyIM.getInstance({
|
wx.im = GoEasyIM.getInstance({
|
||||||
|
|||||||
17
app.json
@@ -2,7 +2,14 @@
|
|||||||
"pages":[
|
"pages":[
|
||||||
"pages/index/index",
|
"pages/index/index",
|
||||||
"pages/ltjm/ltjm",
|
"pages/ltjm/ltjm",
|
||||||
"pages/liaotian/liaotian"
|
"pages/liaotian/liaotian",
|
||||||
|
"pages/conversations/conversations",
|
||||||
|
"pages/login/login",
|
||||||
|
"pages/contacts/contacts",
|
||||||
|
"pages/mine/mine",
|
||||||
|
"pages/chat/groupChat/groupChat",
|
||||||
|
"pages/chat/privateChat/privateChat",
|
||||||
|
"pages/chat/groupMember/groupMember"
|
||||||
],
|
],
|
||||||
"tabBar":{
|
"tabBar":{
|
||||||
"color": "#f00",
|
"color": "#f00",
|
||||||
@@ -12,8 +19,12 @@
|
|||||||
"pagePath": "pages/index/index",
|
"pagePath": "pages/index/index",
|
||||||
"text": "首页"
|
"text": "首页"
|
||||||
},{
|
},{
|
||||||
"pagePath": "pages/liaotian/liaotian",
|
"pagePath" : "pages/conversations/conversations",
|
||||||
"text": "聊天"
|
"text":"信息"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pagePath" : "pages/contacts/contacts",
|
||||||
|
"text" : "通讯录"
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
"window":{
|
"window":{
|
||||||
|
|||||||
63
components/GoEasyAudioPlayer/goEasyAudioPlayer.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
Component({
|
||||||
|
options: {
|
||||||
|
addGlobalClass: true,
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
src: {
|
||||||
|
type: String,
|
||||||
|
value: ""
|
||||||
|
},
|
||||||
|
duration: {
|
||||||
|
type: Number,
|
||||||
|
value: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
width: "",
|
||||||
|
play: false,
|
||||||
|
finalDuration: "",
|
||||||
|
audioContext: null
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
playAudio() {
|
||||||
|
// 播放时才创建audioContext,播放完毕销毁
|
||||||
|
var self = this;
|
||||||
|
this.setData({
|
||||||
|
audioContext: wx.createInnerAudioContext()
|
||||||
|
});
|
||||||
|
this.data.audioContext.src = this.data.src;
|
||||||
|
this.switchAudioState();
|
||||||
|
setTimeout(() => {
|
||||||
|
self.switchAudioState();
|
||||||
|
self.data.audioContext.destroy();
|
||||||
|
}, self.data.finalDuration*1000);
|
||||||
|
|
||||||
|
this.data.audioContext.play();
|
||||||
|
this.data.audioContext.onPlay(()=>{
|
||||||
|
console.log("正在播放......");
|
||||||
|
});
|
||||||
|
this.data.audioContext.onError((res) => {
|
||||||
|
console.log("audio error:",res)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
switchAudioState(){
|
||||||
|
this.setData({
|
||||||
|
play: !this.data.play
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
attached: function() {
|
||||||
|
// 在组件实例进入页面节点树时执行
|
||||||
|
this.setData({
|
||||||
|
width: Math.ceil(this.data.duration)*7+80,
|
||||||
|
finalDuration: Math.ceil(this.data.duration),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
detached: function() {
|
||||||
|
// 在组件实例被从页面节点树移除时执行
|
||||||
|
// 语音还在播放时退出该界面时audioContext还没有被销毁,因此调用该方法清空audioContext
|
||||||
|
if(this.data.audioContext != null){
|
||||||
|
this.data.audioContext.destroy();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
4
components/GoEasyAudioPlayer/goEasyAudioPlayer.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"component": true,
|
||||||
|
"usingComponents": {}
|
||||||
|
}
|
||||||
7
components/GoEasyAudioPlayer/goEasyAudioPlayer.wxml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<view class="goeasy-audio-player" bindtap="playAudio">
|
||||||
|
<view class="audio-facade" style="width:{{width}}rpx">
|
||||||
|
<image wx:if="{{!play}}" class="audio-facade-bg" src="/static/images/audioImage/voice.png"></image>
|
||||||
|
<image wx:else class="audio-facade-bg audio-play-icon" src="/static/images/audioImage/play.gif"></image>
|
||||||
|
<view class="record-second">{{finalDuration}}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
34
components/GoEasyAudioPlayer/goEasyAudioPlayer.wxss
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
.goeasy-audio-player {
|
||||||
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-facade {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 80rpx;
|
||||||
|
max-width: 300rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
padding: 6rpx 10rpx;
|
||||||
|
border-radius: 14rpx;
|
||||||
|
line-height: 30rpx;
|
||||||
|
background: #D02129;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-facade .audio-play-icon {
|
||||||
|
-moz-transform: rotate(180deg);
|
||||||
|
-webkit-transform: rotate(180deg);
|
||||||
|
-o-transform: rotate(180deg);
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-facade-bg {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 35rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-second {
|
||||||
|
padding-left: 14rpx;
|
||||||
|
}
|
||||||
49
components/GoEasyCustomMessage/customMessage.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/* customMessage.js */
|
||||||
|
Component({
|
||||||
|
data: {
|
||||||
|
to: null,//接收方
|
||||||
|
type: "", //私聊还是群聊
|
||||||
|
show: false,//是否展示自定义消息组件
|
||||||
|
|
||||||
|
goods : '',
|
||||||
|
price : '',
|
||||||
|
number : ''
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
setNumber(e){
|
||||||
|
this.setData({
|
||||||
|
number: e.detail.value
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setGoods(e){
|
||||||
|
this.setData({goods: e.detail.value});
|
||||||
|
},
|
||||||
|
setPrice(e){
|
||||||
|
this.setData({
|
||||||
|
price: e.detail.value
|
||||||
|
});
|
||||||
|
},
|
||||||
|
createCustomMessage () {
|
||||||
|
let customMessage = wx.im.createCustomMessage({
|
||||||
|
type : 'order',
|
||||||
|
payload : {
|
||||||
|
number : this.data.number,
|
||||||
|
goods : this.data.goods,
|
||||||
|
price : this.data.price
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
id : this.data.to.uuid,
|
||||||
|
type : this.data.type,
|
||||||
|
data : {name : this.data.to.name, avatar: this.data.to.avatar}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.triggerEvent("sendCustomMessage",customMessage);
|
||||||
|
this.close();
|
||||||
|
},
|
||||||
|
close () {
|
||||||
|
this.setData({
|
||||||
|
show: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
5
components/GoEasyCustomMessage/customMessage.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"component": true,
|
||||||
|
"usingComponents":{},
|
||||||
|
"navigationBarTitleText": "自定义消息"
|
||||||
|
}
|
||||||
29
components/GoEasyCustomMessage/customMessage.wxml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<view>
|
||||||
|
<view wx:if="{{show}}" class="goeasy-custom-message">
|
||||||
|
<view class="custom-message-box">
|
||||||
|
<view class="goeasy-custom-message-title">发送订单</view>
|
||||||
|
<view class="content">
|
||||||
|
<view>
|
||||||
|
<view class="order-item">编号:</view>
|
||||||
|
<view class="order-item">商品:</view>
|
||||||
|
<view class="order-item">金额:</view>
|
||||||
|
</view>
|
||||||
|
<view>
|
||||||
|
<view class="order-input">
|
||||||
|
<input class="input" type="text" bindinput="setNumber" maxlength="20"/>
|
||||||
|
</view>
|
||||||
|
<view class="order-input">
|
||||||
|
<input class="input" type="text" bindinput="setGoods" maxlength="20"/>
|
||||||
|
</view>
|
||||||
|
<view class="order-input">
|
||||||
|
<input class="input" type="text" bindinput="setPrice" maxlength="10" />
|
||||||
|
</view>
|
||||||
|
<view class="action-btn">
|
||||||
|
<view class="cancel-btn" bindtap="close">取消</view>
|
||||||
|
<view class="send-btn" bindtap="createCustomMessage">发送</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
78
components/GoEasyCustomMessage/customMessage.wxss
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/* customMessage.wxss */
|
||||||
|
.goeasy-custom-message {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 10000;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-message-box {
|
||||||
|
padding: 0 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goeasy-custom-message-title {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 40rpx;
|
||||||
|
line-height: 200rpx;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 80rpx;
|
||||||
|
margin-top: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-input {
|
||||||
|
height: 80rpx;
|
||||||
|
margin-top: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
width: 500rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
padding: 10rpx;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 28rpx;
|
||||||
|
background: #EFEFEF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
margin-top: 80rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-btn {
|
||||||
|
width: 240rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
background: #618DFF;
|
||||||
|
line-height: 80rpx;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-size: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-btn {
|
||||||
|
width: 240rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
background: #FFFFFF;
|
||||||
|
line-height: 80rpx;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
color: #666666;
|
||||||
|
font-size: 32rpx;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1)
|
||||||
|
}
|
||||||
67
components/GoEasyRecorder/goEasyRecorder.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
const recorderManager = wx.getRecorderManager();
|
||||||
|
Component({
|
||||||
|
options: {
|
||||||
|
addGlobalClass: true, // 加载组件css文件,需在app.wxss中引入组件css文件
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
recording: false,
|
||||||
|
stopSignaled: false,
|
||||||
|
clickLongPress: false,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
startRecord: function() {
|
||||||
|
console.log('start');
|
||||||
|
this.setData({
|
||||||
|
clickLongPress: true
|
||||||
|
});
|
||||||
|
recorderManager.start();
|
||||||
|
},
|
||||||
|
stopRecord: function() {
|
||||||
|
console.log('end');
|
||||||
|
|
||||||
|
if (!this.data.recording && this.data.clickLongPress) {
|
||||||
|
console.log('in1', this.data.clickLongPress);
|
||||||
|
|
||||||
|
this.setData({
|
||||||
|
stopSignaled: true,
|
||||||
|
clickLongPress: false
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setData({
|
||||||
|
recording: false,
|
||||||
|
});
|
||||||
|
recorderManager.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
attached() {
|
||||||
|
var self = this;
|
||||||
|
recorderManager.onStart(function() {
|
||||||
|
self.setData({
|
||||||
|
recording: true,
|
||||||
|
clickLongPress: false
|
||||||
|
});
|
||||||
|
if (self.data.stopSignaled) {
|
||||||
|
self.setData({
|
||||||
|
stopSignaled: false
|
||||||
|
});
|
||||||
|
recorderManager.stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
recorderManager.onStop(function(res) {
|
||||||
|
|
||||||
|
self.setData({
|
||||||
|
recording: false
|
||||||
|
});
|
||||||
|
if(res.duration < 100) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.triggerEvent('onStop', res);
|
||||||
|
});
|
||||||
|
recorderManager.onError(function() {
|
||||||
|
self.setData({
|
||||||
|
recording: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
4
components/GoEasyRecorder/goEasyRecorder.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"component": true,
|
||||||
|
"usingComponents": {}
|
||||||
|
}
|
||||||
6
components/GoEasyRecorder/goEasyRecorder.wxml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<view class="goeasy-recorder">
|
||||||
|
<view bindtouchstart="startRecord" bindtouchend="stopRecord" class="record-msg-box">
|
||||||
|
{{recording ? '松开发送' : '按下录音'}}
|
||||||
|
</view>
|
||||||
|
<image wx:if="{{recording}}" class="record-icon" src="../../static/images/recordImage/loading.gif"></image>
|
||||||
|
</view>
|
||||||
32
components/GoEasyRecorder/goEasyRecorder.wxss
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
.goeasy-recorder {
|
||||||
|
height: 80rpx;
|
||||||
|
background-color: #ffffff;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.record-msg-box {
|
||||||
|
flex: 1;
|
||||||
|
height: 80rpx;
|
||||||
|
padding-left: 20rpx;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
line-height: 80rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
text-align: center;
|
||||||
|
color: #FFFFFF;
|
||||||
|
background: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-icon {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 158rpx;
|
||||||
|
margin: auto;
|
||||||
|
width: 316rpx;
|
||||||
|
height: 308rpx;
|
||||||
|
}
|
||||||
48
components/GoEasyVideoPlayer/goEasyVideoPlayer.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
|
||||||
|
Component({
|
||||||
|
options: {
|
||||||
|
multipleSlots: true, // 在组件定义时的选项中启用多slot支持
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
videoContext: null,
|
||||||
|
show : false,
|
||||||
|
src : '',
|
||||||
|
duration : 0
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
play({url='', duration=0}) {
|
||||||
|
this.setData({
|
||||||
|
show : true,
|
||||||
|
src : url,
|
||||||
|
duration : duration,
|
||||||
|
videoContext: wx.createVideoContext('videoPlayer', this)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onPlay () {
|
||||||
|
console.log('onplay');
|
||||||
|
|
||||||
|
this.data.videoContext.requestFullScreen({
|
||||||
|
direction : 0
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onFullScreenChange(e) {
|
||||||
|
// 视频的全屏与退出全屏都会执行
|
||||||
|
//当退出全屏播放时,隐藏播放器
|
||||||
|
if(this.data.show && !e.detail.fullScreen){
|
||||||
|
this.setData({
|
||||||
|
show : false
|
||||||
|
})
|
||||||
|
this.data.videoContext.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
attached: function() {
|
||||||
|
// 在组件实例进入页面节点树时执行
|
||||||
|
},
|
||||||
|
detached: function() {
|
||||||
|
// 在组件实例被从页面节点树移除时执行
|
||||||
|
if(this.data.videoContext != null){
|
||||||
|
this.data.videoContext.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
4
components/GoEasyVideoPlayer/goEasyVideoPlayer.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"component": true,
|
||||||
|
"usingComponents": {}
|
||||||
|
}
|
||||||
3
components/GoEasyVideoPlayer/goEasyVideoPlayer.wxml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<view class="goeasy-video-player" style="width0;height:0;overflow:hidden">
|
||||||
|
<video id="videoPlayer" wx:if="{{show}}" src="{{src}}" autoplay="true" custom-cache="{{false}}" bindfullscreenchange="onFullScreenChange" bindplay="onPlay" controls duration="{{duration}}"></video>
|
||||||
|
</view>
|
||||||
11
components/GoEasyVideoPlayer/goEasyVideoPlayer.wxss
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
.goeasy-video-player {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.mask {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
|
opacity: 0.6;
|
||||||
|
background: #333;
|
||||||
|
}
|
||||||
332
pages/chat/groupChat/groupChat.js
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
/* groupChat.js */
|
||||||
|
// 定义表情
|
||||||
|
import EmojiDecoder from "../../../static/lib/EmojiDecoder";
|
||||||
|
let emojiUrl = 'https://imgcache.qq.com/open/qcloud/tim/assets/emoji/';
|
||||||
|
let emojiMap = {
|
||||||
|
'[么么哒]': 'emoji_3@2x.png',
|
||||||
|
'[乒乓]': 'emoji_4@2x.png',
|
||||||
|
'[便便]': 'emoji_5@2x.png',
|
||||||
|
'[信封]': 'emoji_6@2x.png',
|
||||||
|
'[偷笑]': 'emoji_7@2x.png',
|
||||||
|
'[傲慢]': 'emoji_8@2x.png'
|
||||||
|
};
|
||||||
|
const app = getApp();
|
||||||
|
Page({
|
||||||
|
data: {
|
||||||
|
content: '',
|
||||||
|
group: null,
|
||||||
|
messages: [],
|
||||||
|
|
||||||
|
//默认为false展示输入框, 为true时显示录音按钮
|
||||||
|
recordVisible: false,
|
||||||
|
|
||||||
|
currentUser: null,
|
||||||
|
groupMemberNum: 0,
|
||||||
|
groupMembers: {},
|
||||||
|
allHistoryLoaded: false,
|
||||||
|
// 表情
|
||||||
|
emoji : {
|
||||||
|
url : emojiUrl,
|
||||||
|
map : emojiMap,
|
||||||
|
show : false,
|
||||||
|
decoder : new EmojiDecoder(emojiUrl,emojiMap)
|
||||||
|
},
|
||||||
|
more : {//更多按钮
|
||||||
|
show : false
|
||||||
|
},
|
||||||
|
imService: null,
|
||||||
|
// 群名称
|
||||||
|
groupTitle: ""
|
||||||
|
},
|
||||||
|
onPullDownRefresh () {
|
||||||
|
this.loadMoreHistoryMessage();
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
// 初始化群数据
|
||||||
|
let groupId = options.to;
|
||||||
|
let imService = app.globalData.imService;
|
||||||
|
let currentUser = imService.currentUser;
|
||||||
|
let group = imService.findGroupById(groupId);
|
||||||
|
let groupMembers = imService.getGroupMembers(groupId);
|
||||||
|
let groupTitle = group.name + "(" + Object.keys(groupMembers).length + ")";
|
||||||
|
|
||||||
|
this.setData({
|
||||||
|
group: group,
|
||||||
|
imService: imService,
|
||||||
|
groupTitle: groupTitle,
|
||||||
|
currentUser: currentUser,
|
||||||
|
groupMembers: groupMembers,
|
||||||
|
});
|
||||||
|
// 获取群消息
|
||||||
|
let messages = this.data.imService.getGroupMessages(groupId);
|
||||||
|
// 渲染表情与消息间隔5分钟显示时间
|
||||||
|
this.renderMessages(messages);
|
||||||
|
this.scrollToBottom();
|
||||||
|
// 收到的消息设置为已读
|
||||||
|
if(this.data.messages.length !==0){
|
||||||
|
this.markGroupMessageAsRead(groupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听群消息
|
||||||
|
this.data.imService.onNewGroupMessageReceive = (groupId, message)=> {
|
||||||
|
if (groupId === this.data.group.uuid) {
|
||||||
|
// 渲染messages
|
||||||
|
this.renderMessages(this.data.messages);
|
||||||
|
this.scrollToBottom();
|
||||||
|
// 如果收到当前群消息则清除当前群的未读消息
|
||||||
|
this.markGroupMessageAsRead(groupId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
onUnload() {
|
||||||
|
// 退出聊天页面之前,清空页面传入的监听器
|
||||||
|
if(this.data.imService){
|
||||||
|
this.data.imService.onNewGroupMessageReceive = function () {};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRecordStop(res) {
|
||||||
|
// 发送语音
|
||||||
|
let audioMessage = wx.im.createAudioMessage({
|
||||||
|
to: {
|
||||||
|
id : this.data.group.uuid,
|
||||||
|
type : wx.GoEasyIM.SCENE.GROUP,
|
||||||
|
data : {name:this.data.group.name, avatar:this.data.group.avatar}
|
||||||
|
},
|
||||||
|
file: res.detail,
|
||||||
|
onProgress :function (progress) {
|
||||||
|
console.log(progress)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.sendMessage(audioMessage);
|
||||||
|
},
|
||||||
|
sendTextMessage() {
|
||||||
|
// 发送文本与表情
|
||||||
|
if (this.data.content.trim() !== '') {
|
||||||
|
let textMessage = wx.im.createTextMessage({
|
||||||
|
text: this.data.content,
|
||||||
|
to : {
|
||||||
|
id : this.data.group.uuid,
|
||||||
|
type : wx.GoEasyIM.SCENE.GROUP,
|
||||||
|
data : {name:this.data.group.name, avatar:this.data.group.avatar}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.sendMessage(textMessage);
|
||||||
|
}
|
||||||
|
this.setData({
|
||||||
|
content: ""
|
||||||
|
});
|
||||||
|
},
|
||||||
|
sendImage(){
|
||||||
|
// 发送图片
|
||||||
|
let self = this;
|
||||||
|
wx.chooseImage({
|
||||||
|
count: 1,
|
||||||
|
sizeType: ['original', 'compressed'],
|
||||||
|
sourceType: ['album', 'camera'],
|
||||||
|
success (res) {
|
||||||
|
let imageMessage = wx.im.createImageMessage({
|
||||||
|
to : {
|
||||||
|
id : self.data.group.uuid,
|
||||||
|
type : wx.GoEasyIM.SCENE.GROUP,
|
||||||
|
data : {name:self.data.group.name, avatar:self.data.group.avatar}
|
||||||
|
},
|
||||||
|
file: res,
|
||||||
|
onProgress :function (progress) {
|
||||||
|
console.log(progress)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.sendMessage(imageMessage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
sendVideo(){
|
||||||
|
// 发送视频
|
||||||
|
let self = this;
|
||||||
|
wx.chooseVideo({
|
||||||
|
sourceType: ['album','camera'],
|
||||||
|
maxDuration: 60,
|
||||||
|
camera: 'back',
|
||||||
|
success(res) {
|
||||||
|
let videoMessage = wx.im.createVideoMessage({
|
||||||
|
to : {
|
||||||
|
id : self.data.group.uuid,
|
||||||
|
type : wx.GoEasyIM.SCENE.GROUP,
|
||||||
|
data : {name:self.data.group.name, avatar:self.data.group.avatar}
|
||||||
|
},
|
||||||
|
file: res,
|
||||||
|
onProgress :function (progress) {
|
||||||
|
console.log(progress)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.sendMessage(videoMessage);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
sendMessage(message){
|
||||||
|
let self = this;
|
||||||
|
this.data.messages.push(message);
|
||||||
|
this.renderMessages(this.data.messages);
|
||||||
|
self.scrollToBottom();
|
||||||
|
let promise = wx.im.sendMessage(message);
|
||||||
|
promise.then((res) => {
|
||||||
|
console.log('发送消息成功');
|
||||||
|
self.renderMessages(self.data.messages);
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
console.log('发送失败',e)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showCustomMessageForm(){
|
||||||
|
// 展示自定义消息页面
|
||||||
|
let self = this;
|
||||||
|
let customMessage = this.selectComponent("#customMessage");
|
||||||
|
customMessage.setData({
|
||||||
|
show: true,
|
||||||
|
to: self.data.group,
|
||||||
|
type: wx.GoEasyIM.SCENE.GROUP
|
||||||
|
});
|
||||||
|
},
|
||||||
|
sendCustomMessage(event){
|
||||||
|
let customMessage = event.detail;
|
||||||
|
this.sendMessage(customMessage);
|
||||||
|
// 发送自定义消息关闭更多菜单栏
|
||||||
|
this.setData({
|
||||||
|
["more.show"]: false,
|
||||||
|
["emoji.show"]: false,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadMoreHistoryMessage() { //历史消息
|
||||||
|
//历史消息
|
||||||
|
let lastMessageTimeStamp = Date.now();
|
||||||
|
let lastMessage = this.data.messages[0];
|
||||||
|
if (lastMessage) {
|
||||||
|
lastMessageTimeStamp = lastMessage.timestamp;
|
||||||
|
}
|
||||||
|
let currentLength = this.data.messages.length;
|
||||||
|
let promise = this.data.imService.loadGroupHistoryMessage(this.data.group.uuid, lastMessageTimeStamp);
|
||||||
|
promise.then(messages => {
|
||||||
|
if (messages.length === currentLength) {
|
||||||
|
this.setData({
|
||||||
|
allHistoryLoaded: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.renderMessages(this.data.messages);
|
||||||
|
wx.stopPullDownRefresh();
|
||||||
|
}).catch(e => {
|
||||||
|
console.log(e);
|
||||||
|
wx.stopPullDownRefresh();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
renderMessages(messages){
|
||||||
|
messages.forEach((message,index)=>{
|
||||||
|
if(index === 0){
|
||||||
|
// 当页面只有一条消息时,显示发送时间
|
||||||
|
message.showTime = app.formatDate(message.timestamp);
|
||||||
|
}else {
|
||||||
|
// 当前消息与上条消息的发送时间进行比对,超过5分钟则显示当前消息的发送时间
|
||||||
|
if (message.timestamp - messages[index - 1].timestamp > 5 * 60 * 1000) {
|
||||||
|
message.showTime = app.formatDate(message.timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(message.type === 'text'){
|
||||||
|
// 渲染表情与文本消息
|
||||||
|
let text = this.data.emoji.decoder.decode(message.payload.text);
|
||||||
|
message.node= text;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.setData({
|
||||||
|
messages: messages
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showMembers() { //显示群成员
|
||||||
|
wx.navigateTo({
|
||||||
|
url: '../groupMember/groupMember?group=' + JSON.stringify(this.data.group)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
markGroupMessageAsRead (groupId) {
|
||||||
|
wx.im.markGroupMessageAsRead(groupId)
|
||||||
|
.then(() => {
|
||||||
|
console.log('标记为已读成功')
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
console.log('标记为已读失败', e)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setContent(e) {
|
||||||
|
// 监听输入的消息
|
||||||
|
let content = e.detail.value;
|
||||||
|
this.setData({
|
||||||
|
content: content
|
||||||
|
});
|
||||||
|
},
|
||||||
|
switchAudioKeyboard() {
|
||||||
|
// 语音录制按钮和键盘输入的切换
|
||||||
|
this.setData({
|
||||||
|
recordVisible: !this.data.recordVisible
|
||||||
|
});
|
||||||
|
if(this.data.more.show || this.data.emoji.show){
|
||||||
|
this.setData({
|
||||||
|
["more.show"]: false,
|
||||||
|
["emoji.show"]: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(this.data.recordVisible){
|
||||||
|
// 录音授权
|
||||||
|
wx.authorize({
|
||||||
|
scope: 'scope.record',
|
||||||
|
success() {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
playVideo (e) {
|
||||||
|
//播放视频
|
||||||
|
this.selectComponent("#videoPlayer").play({
|
||||||
|
url : e.currentTarget.dataset.url,
|
||||||
|
duration : e.currentTarget.dataset.duration
|
||||||
|
})
|
||||||
|
},
|
||||||
|
previewImage(event) {
|
||||||
|
// 预览图片
|
||||||
|
let imagesUrl = [event.currentTarget.dataset.src];
|
||||||
|
wx.previewImage({
|
||||||
|
urls: imagesUrl // 需要预览的图片http链接列表
|
||||||
|
})
|
||||||
|
},
|
||||||
|
selectEmoji(e){
|
||||||
|
// 选择表情
|
||||||
|
let emojiKey = e.currentTarget.dataset.emojikey;
|
||||||
|
emojiKey = this.data.content + emojiKey;
|
||||||
|
this.setData({
|
||||||
|
content: emojiKey
|
||||||
|
});
|
||||||
|
},
|
||||||
|
messageInputFocusin(){
|
||||||
|
this.setData({
|
||||||
|
["more.show"]: false,
|
||||||
|
["emoji.show"]: false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showEmoji(){
|
||||||
|
this.setData({
|
||||||
|
["emoji.show"]: true,
|
||||||
|
["more.show"]: false,
|
||||||
|
recordVisible: false
|
||||||
|
});
|
||||||
|
// 关闭手机键盘
|
||||||
|
wx.hideKeyboard();
|
||||||
|
},
|
||||||
|
showMore(){
|
||||||
|
this.setData({
|
||||||
|
["more.show"]: true,
|
||||||
|
["emoji.show"]: false
|
||||||
|
});
|
||||||
|
// 关闭手机键盘
|
||||||
|
wx.hideKeyboard();
|
||||||
|
},
|
||||||
|
scrollToBottom() { // 滑动到最底部
|
||||||
|
wx.pageScrollTo({
|
||||||
|
scrollTop : 200000,
|
||||||
|
duration :10
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
11
pages/chat/groupChat/groupChat.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "",
|
||||||
|
"enablePullDownRefresh" : true,
|
||||||
|
"backgroundTextStyle" : "dark",
|
||||||
|
"usingComponents": {
|
||||||
|
"GoEasyRecorder": "/components/GoEasyRecorder/goEasyRecorder",
|
||||||
|
"GoEasyAudioPlayer": "/components/GoEasyAudioPlayer/goEasyAudioPlayer",
|
||||||
|
"GoEasyVideoPlayer": "/components/GoEasyVideoPlayer/goEasyVideoPlayer",
|
||||||
|
"GoEasyCustomMessage": "/components/GoEasyCustomMessage/customMessage"
|
||||||
|
}
|
||||||
|
}
|
||||||
91
pages/chat/groupChat/groupChat.wxml
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<page-meta>
|
||||||
|
<navigation-bar background-color="#D02129" title="{{groupTitle}}"/>
|
||||||
|
</page-meta>
|
||||||
|
<view class="groupChat">
|
||||||
|
<view>
|
||||||
|
<image class="group-member-icon" src="/static/images/group-icon.png" bindtap="showMembers"/>
|
||||||
|
<view scroll-y class="scroll-view">
|
||||||
|
<view class="header">
|
||||||
|
<text>{{allHistoryLoaded ? '已经没有更多的历史消息' : '下拉获取历史消息'}}</text>
|
||||||
|
</view>
|
||||||
|
<!--已经收到的消息-->
|
||||||
|
<view wx:for="{{messages || []}}" wx:for-index="index" wx:key="index" wx:for-item="message">
|
||||||
|
<!--时间显示,类似于微信,隔5分钟不发言,才显示时间-->
|
||||||
|
<view class="time-lag">
|
||||||
|
{{message.showTime}}
|
||||||
|
</view>
|
||||||
|
<view class="{{message.senderId == currentUser.uuid ? 'message-item self' : 'message-item'}}">
|
||||||
|
<view class="avatar other-icon" wx:if="{{message.senderId !== currentUser.uuid}}">
|
||||||
|
<image class="avatar" src="{{message.senderData.avatar}}" />
|
||||||
|
</view>
|
||||||
|
<view class="avatar self-icon" wx:else>
|
||||||
|
<image class="avatar" src="{{currentUser.avatar}}" />
|
||||||
|
</view>
|
||||||
|
<view class="content">
|
||||||
|
<image src="/static/images/pending.gif" class="pending" wx:if="{{ message.status === 'new'}}"></image>
|
||||||
|
<image src="/static/images/failed.png" class="send-fail" wx:if="{{message.status === 'fail'}}"></image>
|
||||||
|
<rich-text class="text-content" nodes="{{message.node}}" wx:if="{{message.type ==='text'}}"></rich-text>
|
||||||
|
<image class="image-content" wx:if="{{message.type === 'image'}}" src="{{message.payload.url}}" bindtap="previewImage"
|
||||||
|
data-src="{{message.payload.url}}" mode="aspectFit"/>
|
||||||
|
<GoEasyAudioPlayer id="goEasyAudio" wx:if="{{message.type =='audio'}}" src="{{message.payload.url}}" duration="{{message.payload.duration}}" />
|
||||||
|
<view class="video-snapshot" bindtap="playVideo" data-url="{{message.payload.video.url}}" data-duration="{{message.payload.video.duration}}" wx:if="{{message.type == 'video'}}">
|
||||||
|
<image class="thumbnail-image" src="{{message.payload.thumbnail.url}}" mode="aspectFit"></image>
|
||||||
|
<image class="play-icon" src="/static/images/videoImage/play.png" mode="aspectFit"></image>
|
||||||
|
</view>
|
||||||
|
<view class="custom-message" wx:if="{{message.type === 'order'}}">
|
||||||
|
<view class="title">
|
||||||
|
<image src="../../../static/images/dingdan.png"></image>
|
||||||
|
<text>自定义消息</text>
|
||||||
|
</view>
|
||||||
|
<view class="custom-message-item">编号: {{message.payload.number}}</view>
|
||||||
|
<view class="custom-message-item">商品: {{message.payload.goods}}</view>
|
||||||
|
<view class="custom-message-item">金额: {{message.payload.price}}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!--发送消息,视频,语音,自定义消息操作-->
|
||||||
|
<view class="action-box">
|
||||||
|
<view class="action-top">
|
||||||
|
<view bindtap="switchAudioKeyboard" class="action-icon">
|
||||||
|
<image wx:if="{{!recordVisible}}" class="microphone-icon" src="/static/images/record-appearance-icon.png"></image>
|
||||||
|
<image wx:else class="keyboard-icon" src="/static/images/jianpan.png"></image>
|
||||||
|
</view>
|
||||||
|
<!-- 录音 -->
|
||||||
|
<GoEasyRecorder style="flex: 1;" wx:if="{{recordVisible}}" bind:onStop="onRecordStop"></GoEasyRecorder>
|
||||||
|
<!-- GoEasyIM最大支持3k的文本消息,如需发送长文本,需调整输入框maxlength值 -->
|
||||||
|
<input wx:else type="text" maxlength="700" placeholder="发送消息" confirm-hold hold-keyboard="{{true}}" adjust-position="{{true}}" class="msg-input-box"
|
||||||
|
data-content="content" bindinput="setContent" bindfocus="messageInputFocusin" value="{{content}}" />
|
||||||
|
<view class="action-icon">
|
||||||
|
<image src="/static/images/emoji.png" class="emoji-icon" bindtap="showEmoji"></image>
|
||||||
|
</view>
|
||||||
|
<view class="action-icon">
|
||||||
|
<image src="/static/images/more.png" class="more-icon" bindtap="showMore"></image>
|
||||||
|
</view>
|
||||||
|
<view class="send-btn-box" bindtap="sendTextMessage">发送</view>
|
||||||
|
</view>
|
||||||
|
<!--展示表情列表-->
|
||||||
|
<view class="action-bottom" wx:if="{{emoji.show}}" style="justify-content: space-around">
|
||||||
|
<image class="image" wx:for="{{emoji.map}}" wx:for-item="emojiItem" wx:for-index="key" wx:key="key" src="{{emoji.url + emojiItem}}" bindtap="selectEmoji" data-emojiKey="{{key}}"></image>
|
||||||
|
</view>
|
||||||
|
<!--更多-->
|
||||||
|
<view class="action-bottom" wx:if="{{more.show}}">
|
||||||
|
<view class="more-item" bindtap="sendImage">
|
||||||
|
<image class="image" src="../../../static/images/tupian.png"></image>
|
||||||
|
<text class="text">图片</text>
|
||||||
|
</view>
|
||||||
|
<view class="more-item" bindtap="sendVideo">
|
||||||
|
<image class="image" src="../../../static/images/shipin.png"></image>
|
||||||
|
<text class="text">视频</text>
|
||||||
|
</view>
|
||||||
|
<view class="more-item" bindtap="showCustomMessageForm">
|
||||||
|
<image class="image" src="../../../static/images/zidingyi.png"></image>
|
||||||
|
<text class="text">自定义消息</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<GoEasyVideoPlayer id="videoPlayer"></GoEasyVideoPlayer>
|
||||||
|
<GoEasyCustomMessage id="customMessage" bind:sendCustomMessage="sendCustomMessage"></GoEasyCustomMessage>
|
||||||
|
</view>
|
||||||
276
pages/chat/groupChat/groupChat.wxss
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
/* groupChat.wxss */
|
||||||
|
page {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.groupChat {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.group-member-icon {
|
||||||
|
width: 60rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
position: fixed;
|
||||||
|
top: 60rpx;
|
||||||
|
right: 20rpx;
|
||||||
|
background-color: #C4C4C4;
|
||||||
|
z-index: 2;
|
||||||
|
border-radius: 60rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-view {
|
||||||
|
overflow-x: hidden;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
padding-bottom: 130rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
height: 90rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: gray !important;
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-lag{
|
||||||
|
font-size: 20rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-item {
|
||||||
|
max-height: 400rpx;
|
||||||
|
padding: 20rpx 0;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.self{
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar{
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.other-icon {
|
||||||
|
margin: 0 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.self-icon {
|
||||||
|
margin: 0 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content{
|
||||||
|
font-size: 34rpx;
|
||||||
|
line-height: 44rpx;
|
||||||
|
max-height: 400rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: right;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pending {
|
||||||
|
width: 30rpx;
|
||||||
|
height: 30rpx;
|
||||||
|
padding: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-fail{
|
||||||
|
width: 30rpx;
|
||||||
|
height: 30rpx;
|
||||||
|
margin-right: 10rpx;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-content{
|
||||||
|
padding: 16rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
background:#D02129;
|
||||||
|
word-break: break-all;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: center;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-content{
|
||||||
|
padding: 16rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
width: 300rpx;
|
||||||
|
height: 300rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-snapshot {
|
||||||
|
position: relative;
|
||||||
|
max-height: 240rpx;
|
||||||
|
max-width: 300rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-image{
|
||||||
|
max-height: 240rpx;
|
||||||
|
max-width: 300rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.play-icon {
|
||||||
|
position: absolute;
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -40rpx;
|
||||||
|
margin-top: -40rpx;
|
||||||
|
z-index: 1000;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-message{
|
||||||
|
width: 400rpx;
|
||||||
|
height: 260rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: flex-start;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 10rpx 30rpx;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title{
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title text {
|
||||||
|
padding: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-message-item{
|
||||||
|
text-align: left;
|
||||||
|
font-size: 28rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
text-overflow:ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-box {
|
||||||
|
width: 100%;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
background-color: #FAFAFA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-top {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
height: 130rpx;
|
||||||
|
padding: 20rpx 10rpx 20rpx 10rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #FAFAFA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.microphone-icon {
|
||||||
|
width: 45rpx;
|
||||||
|
height: 50rpx;
|
||||||
|
padding: 0 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.keyboard-icon {
|
||||||
|
width: 50rpx;
|
||||||
|
height: 50rpx;
|
||||||
|
padding: 0 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-input-box {
|
||||||
|
flex: 1;
|
||||||
|
height: 80rpx;
|
||||||
|
padding-left: 20rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
line-height: 80rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
background-color: #efefef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-icon {
|
||||||
|
width: 50rpx;
|
||||||
|
height: 50rpx;
|
||||||
|
padding-left: 15rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-icon {
|
||||||
|
width: 58rpx;
|
||||||
|
height: 58rpx;
|
||||||
|
padding-left: 15rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-btn-box {
|
||||||
|
width: 80rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 80rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #95949A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-bottom {
|
||||||
|
display: flex;
|
||||||
|
padding: 20rpx 10rpx 20rpx 10rpx;
|
||||||
|
height: 320rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
width: 100rpx;
|
||||||
|
height: 100rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-item{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 150rpx;
|
||||||
|
height: 150rpx;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font-size: 20rpx;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 50rpx;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title image {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
}
|
||||||
20
pages/chat/groupMember/groupMember.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/* groupMember.js */
|
||||||
|
|
||||||
|
const app = getApp()
|
||||||
|
|
||||||
|
Page({
|
||||||
|
data: {
|
||||||
|
currentUser : null,
|
||||||
|
groupMembersMap : {},
|
||||||
|
groupMemberNum: 0
|
||||||
|
},
|
||||||
|
onLoad(options){
|
||||||
|
let group = JSON.parse(options.group);
|
||||||
|
let groupMemberMap = app.globalData.imService.getGroupMembers(group.uuid);
|
||||||
|
let groupMemberNum = Object.keys(groupMemberMap).length;
|
||||||
|
this.setData({
|
||||||
|
groupMemberNum: groupMemberNum,
|
||||||
|
groupMembersMap: groupMemberMap,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})
|
||||||
3
pages/chat/groupMember/groupMember.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": ""
|
||||||
|
}
|
||||||
15
pages/chat/groupMember/groupMember.wxml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<!--groupMember.wxml-->
|
||||||
|
<page-meta>
|
||||||
|
<navigation-bar
|
||||||
|
front-color="#FFFFFF"
|
||||||
|
background-color="#D02129"
|
||||||
|
title="群成员({{groupMemberNum}})"
|
||||||
|
/>
|
||||||
|
</page-meta>
|
||||||
|
<view class="groupMember">
|
||||||
|
<view class="member-layer">
|
||||||
|
<view class="member">
|
||||||
|
<image src="{{member.avatar}}" class="group-member-avatar avatar" wx:for="{{groupMembersMap}}" wx:key="key" wx:for-item="member"/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
51
pages/chat/groupMember/groupMember.wxss
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/* groupMember.wxss */
|
||||||
|
|
||||||
|
page {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
font-family: Source Han Sans CN;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-layer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
height: 100%;
|
||||||
|
background: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exit-icon {
|
||||||
|
padding-left: 20rpx;
|
||||||
|
line-height: 120rpx;
|
||||||
|
font-size: 36rpx;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-group-name {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member {
|
||||||
|
padding: 10rpx;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 96rpx;
|
||||||
|
height: 96rpx;
|
||||||
|
min-width: 96rpx;
|
||||||
|
min-height: 96rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-member-avatar {
|
||||||
|
margin-right: 10rpx;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
323
pages/chat/privateChat/privateChat.js
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
/* privateChat.js */
|
||||||
|
import EmojiDecoder from "../../../static/lib/EmojiDecoder";
|
||||||
|
let emojiUrl = 'https://imgcache.qq.com/open/qcloud/tim/assets/emoji/';
|
||||||
|
let emojiMap = {
|
||||||
|
'[么么哒]': 'emoji_3@2x.png',
|
||||||
|
'[乒乓]': 'emoji_4@2x.png',
|
||||||
|
'[便便]': 'emoji_5@2x.png',
|
||||||
|
'[信封]': 'emoji_6@2x.png',
|
||||||
|
'[偷笑]': 'emoji_7@2x.png',
|
||||||
|
'[傲慢]': 'emoji_8@2x.png'
|
||||||
|
};
|
||||||
|
const app = getApp();
|
||||||
|
Page({
|
||||||
|
data: {
|
||||||
|
content: '',
|
||||||
|
friend: null,
|
||||||
|
currentUser: null,
|
||||||
|
messages: [],
|
||||||
|
|
||||||
|
//默认为false展示输入框, 为true时显示录音按钮
|
||||||
|
recordVisible: false,
|
||||||
|
|
||||||
|
//所有历史消息加载完成标识
|
||||||
|
allHistoryLoaded: false,
|
||||||
|
//定义表情列表
|
||||||
|
emoji : {
|
||||||
|
url : emojiUrl,
|
||||||
|
map : emojiMap,
|
||||||
|
show: false,
|
||||||
|
decoder: new EmojiDecoder(emojiUrl, emojiMap),
|
||||||
|
},
|
||||||
|
more : {//更多按钮
|
||||||
|
show : false
|
||||||
|
},
|
||||||
|
imService : null,
|
||||||
|
},
|
||||||
|
onPullDownRefresh () {
|
||||||
|
this.loadMoreHistoryMessage();
|
||||||
|
},
|
||||||
|
onLoad: function(options) {
|
||||||
|
// 获取初始数据并加载
|
||||||
|
let friendId = options.to;
|
||||||
|
let imService = app.globalData.imService;
|
||||||
|
let currentUser = imService.currentUser;
|
||||||
|
let friend = imService.findFriendById(friendId);
|
||||||
|
|
||||||
|
this.setData({
|
||||||
|
friend: friend,
|
||||||
|
imService: imService,
|
||||||
|
currentUser: currentUser,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取消息
|
||||||
|
let messages = this.data.imService.getPrivateMessages(friendId);
|
||||||
|
// 渲染表情与消息间隔5分钟显示时间
|
||||||
|
this.renderMessages(messages);
|
||||||
|
this.scrollToBottom();
|
||||||
|
// 收到的消息设置为已读
|
||||||
|
if(this.data.messages.length !==0){
|
||||||
|
this.markPrivateMessageAsRead(friendId);
|
||||||
|
}
|
||||||
|
|
||||||
|
//传入监听器,收到一条私聊消息总是滚到到页面底部
|
||||||
|
this.data.imService.onNewPrivateMessageReceive = (friendId, message)=> {
|
||||||
|
if (friendId === this.data.friend.uuid) {
|
||||||
|
this.renderMessages(this.data.messages);
|
||||||
|
this.scrollToBottom();
|
||||||
|
// 如果是好友发送则清除未读消息
|
||||||
|
this.markPrivateMessageAsRead(friendId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
onUnload () {
|
||||||
|
//退出聊天页面之前,清空页面传入的监听器
|
||||||
|
if(this.data.imService) {
|
||||||
|
this.data.imService.onNewPrivateMessageReceive = (friendId, message)=> {};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRecordStop(res) {
|
||||||
|
// 发送语音
|
||||||
|
let audioMessage = wx.im.createAudioMessage({
|
||||||
|
to: {
|
||||||
|
id : this.data.friend.uuid,
|
||||||
|
type : wx.GoEasyIM.SCENE.PRIVATE,
|
||||||
|
data : {name:this.data.friend.name, avatar:this.data.friend.avatar}
|
||||||
|
},
|
||||||
|
file: res.detail,
|
||||||
|
onProgress :function (progress) {
|
||||||
|
console.log(progress)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.sendMessage(audioMessage);
|
||||||
|
},
|
||||||
|
sendTextMessage() {
|
||||||
|
// 发送文本与表情
|
||||||
|
if (this.data.content.trim() !== '') {
|
||||||
|
let textMessage = wx.im.createTextMessage({
|
||||||
|
text: this.data.content,
|
||||||
|
to : {
|
||||||
|
id : this.data.friend.uuid,
|
||||||
|
type : wx.GoEasyIM.SCENE.PRIVATE,
|
||||||
|
data : {name:this.data.friend.name, avatar:this.data.friend.avatar}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.sendMessage(textMessage);
|
||||||
|
}
|
||||||
|
this.setData({
|
||||||
|
content: ""
|
||||||
|
});
|
||||||
|
},
|
||||||
|
sendImage(){
|
||||||
|
// 发送图片
|
||||||
|
let self = this;
|
||||||
|
wx.chooseImage({
|
||||||
|
count: 1,
|
||||||
|
sizeType: ['original', 'compressed'],
|
||||||
|
sourceType: ['album', 'camera'],
|
||||||
|
success (res) {
|
||||||
|
let imageMessage = wx.im.createImageMessage({
|
||||||
|
to : {
|
||||||
|
id : self.data.friend.uuid,
|
||||||
|
type : wx.GoEasyIM.SCENE.PRIVATE,
|
||||||
|
data : {name:self.data.friend.name, avatar:self.data.friend.avatar}
|
||||||
|
},
|
||||||
|
file: res,
|
||||||
|
onProgress :function (progress) {
|
||||||
|
console.log(progress)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.sendMessage(imageMessage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
sendVideo(){
|
||||||
|
// 发送视频
|
||||||
|
let self = this;
|
||||||
|
wx.chooseVideo({
|
||||||
|
sourceType: ['album','camera'],
|
||||||
|
maxDuration: 60,
|
||||||
|
camera: 'back',
|
||||||
|
success(res) {
|
||||||
|
let videoMessage = wx.im.createVideoMessage({
|
||||||
|
to : {
|
||||||
|
id : self.data.friend.uuid,
|
||||||
|
type : wx.GoEasyIM.SCENE.PRIVATE,
|
||||||
|
data : {name:self.data.friend.name, avatar:self.data.friend.avatar}
|
||||||
|
},
|
||||||
|
file: res,
|
||||||
|
onProgress :function (progress) {
|
||||||
|
console.log(progress)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.sendMessage(videoMessage);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
sendMessage(message){
|
||||||
|
let self = this;
|
||||||
|
this.data.messages.push(message);
|
||||||
|
this.renderMessages(this.data.messages);
|
||||||
|
this.scrollToBottom();
|
||||||
|
let promise = wx.im.sendMessage(message);
|
||||||
|
promise.then((res) => {
|
||||||
|
console.log('发送消息成功');
|
||||||
|
self.renderMessages(self.data.messages);
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
console.log('发送失败',e)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
showCustomMessageForm(){
|
||||||
|
let self = this;
|
||||||
|
let customMessage = this.selectComponent("#customMessage");
|
||||||
|
customMessage.setData({
|
||||||
|
show: true,
|
||||||
|
to: self.data.friend,
|
||||||
|
type: wx.GoEasyIM.SCENE.PRIVATE
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
sendCustomMessage(event){
|
||||||
|
let customerMessage = event.detail;
|
||||||
|
this.sendMessage(customerMessage);
|
||||||
|
// 发送自定义消息关闭更多菜单栏
|
||||||
|
this.setData({
|
||||||
|
["more.show"]: false,
|
||||||
|
["emoji.show"]: false,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadMoreHistoryMessage() {
|
||||||
|
//历史消息
|
||||||
|
let friendId = this.data.friend.uuid;
|
||||||
|
let lastMessageTimeStamp = Date.now();
|
||||||
|
let lastMessage = this.data.messages[0];
|
||||||
|
if (lastMessage) {
|
||||||
|
lastMessageTimeStamp = lastMessage.timestamp;
|
||||||
|
}
|
||||||
|
let currentLength = this.data.messages.length;
|
||||||
|
let promise = app.globalData.imService.loadPrivateHistoryMessage(friendId, lastMessageTimeStamp);
|
||||||
|
promise.then(messages => {
|
||||||
|
if (messages.length === currentLength) {
|
||||||
|
this.setData({
|
||||||
|
allHistoryLoaded: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.renderMessages(this.data.messages);
|
||||||
|
wx.stopPullDownRefresh();
|
||||||
|
}).catch(e => {
|
||||||
|
console.log(e)
|
||||||
|
wx.stopPullDownRefresh();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
renderMessages(messages){
|
||||||
|
console.log(this.data.emoji.decoder)
|
||||||
|
messages.forEach((message,index)=>{
|
||||||
|
if(index === 0){
|
||||||
|
// 当页面只有一条消息时,显示发送时间
|
||||||
|
message.showTime = app.formatDate(message.timestamp);
|
||||||
|
}else {
|
||||||
|
// 当前消息与上条消息的发送时间进行比对,超过5分钟则显示当前消息的发送时间
|
||||||
|
if (message.timestamp - messages[index - 1].timestamp > 5 * 60 * 1000) {
|
||||||
|
message.showTime = app.formatDate(message.timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(message.type === 'text'){
|
||||||
|
// 渲染表情与文本消息
|
||||||
|
let text = this.data.emoji.decoder.decode(message.payload.text);
|
||||||
|
message.node= text;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.setData({
|
||||||
|
messages: messages
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
markPrivateMessageAsRead (friendId) {
|
||||||
|
wx.im.markPrivateMessageAsRead(friendId)
|
||||||
|
.then(() => {
|
||||||
|
console.log('标记为已读成功')
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
console.log('标记为已读失败', e)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setContent(e) {
|
||||||
|
// 监听输入的消息
|
||||||
|
let content = e.detail.value;
|
||||||
|
this.setData({
|
||||||
|
content: content
|
||||||
|
});
|
||||||
|
},
|
||||||
|
switchAudioKeyboard() {
|
||||||
|
// 语音录制按钮和键盘输入的切换
|
||||||
|
this.setData({
|
||||||
|
recordVisible: !this.data.recordVisible
|
||||||
|
});
|
||||||
|
if(this.data.more.show || this.data.emoji.show){
|
||||||
|
this.setData({
|
||||||
|
["more.show"]: false,
|
||||||
|
["emoji.show"]: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(this.data.recordVisible){
|
||||||
|
// 录音授权
|
||||||
|
wx.authorize({
|
||||||
|
scope: 'scope.record',
|
||||||
|
success() {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
playVideo (e) {
|
||||||
|
//播放视频
|
||||||
|
this.selectComponent("#videoPlayer").play({
|
||||||
|
url : e.currentTarget.dataset.url,
|
||||||
|
duration : e.currentTarget.dataset.duration
|
||||||
|
})
|
||||||
|
},
|
||||||
|
previewImage(event) {
|
||||||
|
// 预览图片
|
||||||
|
let imagesUrl = [event.currentTarget.dataset.src];
|
||||||
|
wx.previewImage({
|
||||||
|
urls: imagesUrl // 需要预览的图片http链接列表
|
||||||
|
})
|
||||||
|
},
|
||||||
|
selectEmoji(e){
|
||||||
|
// 选择表情
|
||||||
|
let emojiKey = e.currentTarget.dataset.emojikey;
|
||||||
|
emojiKey = this.data.content + emojiKey;
|
||||||
|
this.setData({
|
||||||
|
content: emojiKey
|
||||||
|
});
|
||||||
|
},
|
||||||
|
messageInputFocusin(){
|
||||||
|
this.setData({
|
||||||
|
["more.show"]: false,
|
||||||
|
["emoji.show"]: false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showEmoji(){
|
||||||
|
this.setData({
|
||||||
|
["emoji.show"]: true,
|
||||||
|
["more.show"]: false,
|
||||||
|
recordVisible: false
|
||||||
|
});
|
||||||
|
// 关闭手机键盘
|
||||||
|
wx.hideKeyboard();
|
||||||
|
},
|
||||||
|
showMore(){
|
||||||
|
this.setData({
|
||||||
|
["more.show"]: true,
|
||||||
|
["emoji.show"]: false
|
||||||
|
});
|
||||||
|
// 关闭手机键盘
|
||||||
|
wx.hideKeyboard();
|
||||||
|
},
|
||||||
|
scrollToBottom() { // 滑动到最底部
|
||||||
|
wx.pageScrollTo({
|
||||||
|
scrollTop : 200000,
|
||||||
|
duration :10
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
11
pages/chat/privateChat/privateChat.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "私聊",
|
||||||
|
"enablePullDownRefresh" : true,
|
||||||
|
"backgroundTextStyle" : "dark",
|
||||||
|
"usingComponents": {
|
||||||
|
"GoEasyRecorder": "/components/GoEasyRecorder/goEasyRecorder",
|
||||||
|
"GoEasyAudioPlayer": "/components/GoEasyAudioPlayer/goEasyAudioPlayer",
|
||||||
|
"GoEasyVideoPlayer": "/components/GoEasyVideoPlayer/goEasyVideoPlayer",
|
||||||
|
"GoEasyCustomMessage": "/components/GoEasyCustomMessage/customMessage"
|
||||||
|
}
|
||||||
|
}
|
||||||
92
pages/chat/privateChat/privateChat.wxml
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<!--privateChat.wxml-->
|
||||||
|
<page-meta>
|
||||||
|
<navigation-bar title="{{friend.name}}" front-color="#FFFFFF" background-color="#D02129" />
|
||||||
|
</page-meta>
|
||||||
|
<view class="chat">
|
||||||
|
<view class="chat-box">
|
||||||
|
<view class="scroll-view">
|
||||||
|
<view class="header">
|
||||||
|
<text>{{allHistoryLoaded ? '已经没有更多的历史消息' : '下拉获取历史消息'}}</text>
|
||||||
|
</view>
|
||||||
|
<!--已经收到的消息-->
|
||||||
|
<view wx:for="{{messages || []}}" wx:for-index="index" wx:key="index" wx:for-item="message">
|
||||||
|
<!--时间显示,类似于微信,隔5分钟不发言,才显示时间-->
|
||||||
|
<view class="time-lag">
|
||||||
|
{{message.showTime}}
|
||||||
|
</view>
|
||||||
|
<!--消息内容-->
|
||||||
|
<view class="{{message.senderId == currentUser.uuid ? 'message-item self' : 'message-item'}}">
|
||||||
|
<view class="avatar other-icon" wx:if="{{message.senderId !== currentUser.uuid}}">
|
||||||
|
<image class="avatar" src="{{friend.avatar}}" />
|
||||||
|
</view>
|
||||||
|
<view class="avatar self-icon" wx:else>
|
||||||
|
<image class="avatar" src="{{currentUser.avatar}}" />
|
||||||
|
</view>
|
||||||
|
<view class="content">
|
||||||
|
<image src="/static/images/pending.gif" class="pending" wx:if="{{message.status === 'new'}}"></image>
|
||||||
|
<image src="/static/images/failed.png" class="send-fail" wx:if="{{message.status == 'fail'}}"></image>
|
||||||
|
<rich-text class="text-content" nodes="{{message.node}}" wx:if="{{message.type ==='text'}}"></rich-text>
|
||||||
|
<image class="image-content" wx:if="{{message.type === 'image'}}" src="{{message.payload.url}}" bindtap="previewImage"
|
||||||
|
data-src="{{message.payload.url}}" mode="aspectFit"/>
|
||||||
|
<GoEasyAudioPlayer id="goEasyAudio" wx:if="{{message.type =='audio'}}" src="{{message.payload.url}}" duration="{{message.payload.duration}}" />
|
||||||
|
<view class="video-snapshot" bindtap="playVideo" data-url="{{message.payload.video.url}}" data-duration="{{message.payload.video.duration}}" wx:if="{{message.type == 'video'}}">
|
||||||
|
<image class="thumbnail-image" src="{{message.payload.thumbnail.url}}" mode="aspectFit"></image>
|
||||||
|
<image class="play-icon" src="/static/images/videoImage/play.png" mode="aspectFit"></image>
|
||||||
|
</view>
|
||||||
|
<view class="custom-message" wx:if="{{message.type === 'order'}}">
|
||||||
|
<view class="title">
|
||||||
|
<image src="../../../static/images/dingdan.png"></image>
|
||||||
|
<text>自定义消息</text>
|
||||||
|
</view>
|
||||||
|
<view class="custom-message-item">编号: {{message.payload.number}}</view>
|
||||||
|
<view class="custom-message-item">商品: {{message.payload.goods}}</view>
|
||||||
|
<view class="custom-message-item">金额: {{message.payload.price}}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!--发送消息,视频,语音,自定义消息操作-->
|
||||||
|
<view class="action-box">
|
||||||
|
<view class="action-top">
|
||||||
|
<view bindtap="switchAudioKeyboard" class="action-icon">
|
||||||
|
<image wx:if="{{!recordVisible}}" class="microphone-icon" src="/static/images/record-appearance-icon.png"></image>
|
||||||
|
<image wx:else class="keyboard-icon" src="/static/images/jianpan.png"></image>
|
||||||
|
</view>
|
||||||
|
<!-- 录音 -->
|
||||||
|
<GoEasyRecorder style="flex: 1;" wx:if="{{recordVisible}}" bind:onStop="onRecordStop"></GoEasyRecorder>
|
||||||
|
<!-- GoEasyIM最大支持3k的文本消息,如需发送长文本,需调整输入框maxlength值 -->
|
||||||
|
<input wx:else type="text" maxlength="700" placeholder="发送消息" confirm-hold hold-keyboard="{{true}}" adjust-position="{{true}}" class="msg-input-box"
|
||||||
|
data-content="content" bindinput="setContent" bindfocus="messageInputFocusin" value="{{content}}" />
|
||||||
|
<view class="action-icon">
|
||||||
|
<image src="/static/images/emoji.png" class="emoji-icon" bindtap="showEmoji"></image>
|
||||||
|
</view>
|
||||||
|
<view class="action-icon">
|
||||||
|
<image src="/static/images/more.png" class="more-icon" bindtap="showMore"></image>
|
||||||
|
</view>
|
||||||
|
<view class="send-btn-box" bindtap="sendTextMessage">发送</view>
|
||||||
|
</view>
|
||||||
|
<!--展示表情列表-->
|
||||||
|
<view class="action-bottom" wx:if="{{emoji.show}}" style="justify-content: space-around">
|
||||||
|
<image class="image" wx:for="{{emoji.map}}" wx:for-item="emojiItem" wx:for-index="key" wx:key="key" src="{{emoji.url + emojiItem}}" bindtap="selectEmoji" data-emojiKey="{{key}}"></image>
|
||||||
|
</view>
|
||||||
|
<!--更多-->
|
||||||
|
<view class="action-bottom" wx:if="{{more.show}}">
|
||||||
|
<view class="more-item" bindtap="sendImage">
|
||||||
|
<image class="image" src="../../../static/images/tupian.png"></image>
|
||||||
|
<text class="text">图片</text>
|
||||||
|
</view>
|
||||||
|
<view class="more-item" bindtap="sendVideo">
|
||||||
|
<image class="image" src="../../../static/images/shipin.png"></image>
|
||||||
|
<text class="text">视频</text>
|
||||||
|
</view>
|
||||||
|
<view class="more-item" bindtap="showCustomMessageForm">
|
||||||
|
<image class="image" src="../../../static/images/zidingyi.png"></image>
|
||||||
|
<text class="text">自定义消息</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<GoEasyVideoPlayer id="videoPlayer"></GoEasyVideoPlayer>
|
||||||
|
<GoEasyCustomMessage id="customMessage" bind:sendCustomMessage="sendCustomMessage"></GoEasyCustomMessage>
|
||||||
|
</view>
|
||||||
264
pages/chat/privateChat/privateChat.wxss
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
/* privateChat.wxss */
|
||||||
|
page {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.chat {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-view {
|
||||||
|
overflow-x: hidden;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
padding-bottom: 130rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
height: 90rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: gray !important;
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-lag{
|
||||||
|
font-size: 20rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-item {
|
||||||
|
max-height: 400rpx;
|
||||||
|
padding: 20rpx 0;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.self{
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar{
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.other-icon {
|
||||||
|
margin: 0 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.self-icon {
|
||||||
|
margin: 0 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content{
|
||||||
|
font-size: 34rpx;
|
||||||
|
line-height: 44rpx;
|
||||||
|
max-height: 400rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: right;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pending {
|
||||||
|
width: 30rpx;
|
||||||
|
height: 30rpx;
|
||||||
|
padding: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-fail{
|
||||||
|
width: 30rpx;
|
||||||
|
height: 30rpx;
|
||||||
|
margin-right: 10rpx;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-content{
|
||||||
|
padding: 16rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
background:#D02129;
|
||||||
|
word-break: break-all;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: center;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-content{
|
||||||
|
padding: 16rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
width: 300rpx;
|
||||||
|
height: 300rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-snapshot {
|
||||||
|
position: relative;
|
||||||
|
max-height: 240rpx;
|
||||||
|
max-width: 300rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-image{
|
||||||
|
max-height: 240rpx;
|
||||||
|
max-width: 300rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.play-icon {
|
||||||
|
position: absolute;
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -40rpx;
|
||||||
|
margin-top: -40rpx;
|
||||||
|
z-index: 1000;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-message{
|
||||||
|
width: 400rpx;
|
||||||
|
height: 260rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: flex-start;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 10rpx 30rpx;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
box-shadow: 0px 2px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title{
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title text {
|
||||||
|
padding: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-message-item{
|
||||||
|
text-align: left;
|
||||||
|
font-size: 28rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
text-overflow:ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-box {
|
||||||
|
width: 100%;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
background-color: #FAFAFA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-top {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 130rpx;
|
||||||
|
padding: 20rpx 10rpx 20rpx 10rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #FAFAFA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.microphone-icon {
|
||||||
|
width: 45rpx;
|
||||||
|
height: 50rpx;
|
||||||
|
padding: 0 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-icon {
|
||||||
|
width: 50rpx;
|
||||||
|
height: 50rpx;
|
||||||
|
padding: 0 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-input-box {
|
||||||
|
flex: 1;
|
||||||
|
height: 80rpx;
|
||||||
|
padding-left: 20rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
line-height: 80rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
background-color: #efefef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-icon {
|
||||||
|
width: 50rpx;
|
||||||
|
height: 50rpx;
|
||||||
|
padding-left: 15rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-icon {
|
||||||
|
width: 58rpx;
|
||||||
|
height: 58rpx;
|
||||||
|
padding-left: 15rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.send-btn-box {
|
||||||
|
width: 80rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 80rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #95949A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-bottom {
|
||||||
|
display: flex;
|
||||||
|
padding: 20rpx 10rpx 20rpx 10rpx;
|
||||||
|
height: 320rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
height: 100rpx;
|
||||||
|
width: 100rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-item{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 150rpx;
|
||||||
|
height: 150rpx;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font-size: 20rpx;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 50rpx;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title image {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
}
|
||||||
34
pages/contacts/contacts.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/* contacts.js */
|
||||||
|
|
||||||
|
import restapi from "../../static/lib/restapi";
|
||||||
|
|
||||||
|
const app = getApp()
|
||||||
|
|
||||||
|
Page({
|
||||||
|
data: {
|
||||||
|
groups:[],
|
||||||
|
friends:[],
|
||||||
|
},
|
||||||
|
onShow () {
|
||||||
|
let currentUser = app.globalData.imService.currentUser;
|
||||||
|
let groups = restapi.findGroups(currentUser);
|
||||||
|
let friends = restapi.findFriends(currentUser);
|
||||||
|
this.setData({
|
||||||
|
groups: groups,
|
||||||
|
friends: friends,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onUnload(){
|
||||||
|
app.globalData.imService.disconnect();
|
||||||
|
},
|
||||||
|
enterChat (e) {//进入私聊
|
||||||
|
let type = e.currentTarget.dataset.type;
|
||||||
|
let conversation = e.currentTarget.dataset.conversation;
|
||||||
|
let path = type === wx.GoEasyIM.SCENE.PRIVATE?
|
||||||
|
'../chat/privateChat/privateChat?to='+conversation.uuid
|
||||||
|
:'../chat/groupChat/groupChat?to='+ conversation.uuid;
|
||||||
|
wx.navigateTo({
|
||||||
|
url : path
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
3
pages/contacts/contacts.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "联系人"
|
||||||
|
}
|
||||||
26
pages/contacts/contacts.wxml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<!--contacts.wxml-->
|
||||||
|
<view class="contacts">
|
||||||
|
<view class="contacts-container">
|
||||||
|
<view class="user-list">
|
||||||
|
<view class="user-list-item" wx:for="{{groups || []}}" wx:key="key" wx:for-item="group" bindtap="enterChat" data-type="group" data-conversation="{{group}}">
|
||||||
|
<view class="user-item-avatar">
|
||||||
|
<image src="{{group.avatar}}"></image>
|
||||||
|
</view>
|
||||||
|
<view class="user-item-info">
|
||||||
|
<text class="user-item-info__name">{{group.name}}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="contacts-title" v-if="friends && friends.length !=0">联系人</view>
|
||||||
|
<view class="user-list">
|
||||||
|
<view class="user-list-item" wx:for="{{friends || []}}" wx:for-item="friend" bindtap="enterChat" data-type="private" data-conversation="{{friend}}" wx:key="key">
|
||||||
|
<div class="user-item-avatar">
|
||||||
|
<image src="{{friend.avatar}}"></image>
|
||||||
|
</div>
|
||||||
|
<div class="user-item-info">
|
||||||
|
<span class="user-item-info__name">{{friend.name}}</span>
|
||||||
|
</div>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
87
pages/contacts/contacts.wxss
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/* contacts.wxss */
|
||||||
|
|
||||||
|
page {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
font-family: Source Han Sans CN;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contacts{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.contacts .contacts-container{
|
||||||
|
width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contacts .user-list-item{
|
||||||
|
height: 132rpx;
|
||||||
|
padding-left: 32rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.contacts .contacts-title{
|
||||||
|
height: 80rpx;
|
||||||
|
line-height: 100rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #666666;
|
||||||
|
background: #F3F4F7;
|
||||||
|
text-indent: 44rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contacts .user-list{
|
||||||
|
flex-grow: 1;
|
||||||
|
background: #ffffff;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.contacts .user-item-avatar{
|
||||||
|
width: 96rpx;
|
||||||
|
height: 96rpx;
|
||||||
|
margin-right: 32rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.contacts .user-item-avatar image{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.contacts .user-item-info{
|
||||||
|
height: 130rpx;
|
||||||
|
padding-right: 32rpx;
|
||||||
|
line-height: 88rpx;
|
||||||
|
flex-grow: 1;
|
||||||
|
border-bottom: 1px solid #EFEFEF;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.contacts .user-item-info__name{
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-family: Source Han Sans CN;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #262628;
|
||||||
|
}
|
||||||
|
.contacts .user-item-info__tips{
|
||||||
|
height: 44rpx;
|
||||||
|
width: 64rpx;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
line-height: 44rpx;
|
||||||
|
background: #D02129;
|
||||||
|
color: #ffffff;
|
||||||
|
font-family: Cabin;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.contacts .online-dot{
|
||||||
|
position: absolute;
|
||||||
|
width: 32rpx!important;
|
||||||
|
height: 32rpx!important;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
142
pages/conversations/conversations.js
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
const app = getApp()
|
||||||
|
import IMService from '../../static/lib/imservice.js';
|
||||||
|
Page({
|
||||||
|
data : {
|
||||||
|
conversations : [],
|
||||||
|
action : {
|
||||||
|
conversation : null,
|
||||||
|
show : false,
|
||||||
|
toastMessage : '',
|
||||||
|
showToast : false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onShow () {
|
||||||
|
let currentUser = wx.getStorageSync("currentUser");
|
||||||
|
if(!currentUser){
|
||||||
|
wx.redirectTo({
|
||||||
|
url : '../login/login'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(wx.im.getStatus() === 'disconnected') {
|
||||||
|
app.globalData.imService= new IMService(wx.im);
|
||||||
|
app.globalData.imService.connectIM(currentUser);
|
||||||
|
}
|
||||||
|
wx.showLoading({
|
||||||
|
title: '加载中'
|
||||||
|
});
|
||||||
|
//监听会话列表变化
|
||||||
|
let self = this;
|
||||||
|
wx.im.on(wx.GoEasyIM.EVENT.CONVERSATIONS_UPDATED, (conversations) => {
|
||||||
|
// 设置tabBar未读消息总数以及conversation
|
||||||
|
self.setConversations(conversations);
|
||||||
|
});
|
||||||
|
|
||||||
|
//加载会话列表
|
||||||
|
wx.im.latestConversations()
|
||||||
|
.then(res => {
|
||||||
|
let content = res.content;
|
||||||
|
self.setConversations(content);
|
||||||
|
wx.hideLoading();
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
console.log(e);
|
||||||
|
wx.hideLoading();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onHide(){
|
||||||
|
// 销毁conversation监听器
|
||||||
|
wx.im.on(wx.GoEasyIM.EVENT.CONVERSATIONS_UPDATED, (conversations) => {});
|
||||||
|
},
|
||||||
|
setConversations (conversations) {
|
||||||
|
conversations.conversations && conversations.conversations.map((item) => {
|
||||||
|
// 格式化时间格式
|
||||||
|
item.lastMessage.date = app.formatDate(item.lastMessage.timestamp)
|
||||||
|
});
|
||||||
|
this.setData({
|
||||||
|
conversations : conversations.conversations
|
||||||
|
});
|
||||||
|
this.setUnreadAmount(conversations.unreadTotal);
|
||||||
|
},
|
||||||
|
navigateToChat (e) {
|
||||||
|
let conversation = e.currentTarget.dataset.conversation;
|
||||||
|
let path = conversation.type === wx.GoEasyIM.SCENE.PRIVATE?
|
||||||
|
'../chat/privateChat/privateChat?to='+conversation.userId
|
||||||
|
:'../chat/groupChat/groupChat?to='+ conversation.groupId;
|
||||||
|
wx.navigateTo({
|
||||||
|
url : path
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setUnreadAmount(unreadTotal) {
|
||||||
|
if(unreadTotal >0){
|
||||||
|
wx.setTabBarBadge({
|
||||||
|
index: 0,
|
||||||
|
text: unreadTotal.toString()
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
wx.hideTabBarRedDot({
|
||||||
|
index :0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showAction(e){
|
||||||
|
let conversation = e.currentTarget.dataset.conversation;
|
||||||
|
this.setData({
|
||||||
|
["action.conversation"]: conversation,
|
||||||
|
["action.show"]: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
topConversation(){
|
||||||
|
let conversation = this.data.action.conversation;
|
||||||
|
let title = conversation.top ? '取消置顶失败' : '置顶失败';
|
||||||
|
let promise;
|
||||||
|
wx.showLoading({
|
||||||
|
title: ""
|
||||||
|
});
|
||||||
|
if(conversation.type === wx.GoEasyIM.SCENE.PRIVATE){
|
||||||
|
promise = wx.im.topPrivateConversation(conversation.userId, !conversation.top)
|
||||||
|
}else{
|
||||||
|
promise = wx.im.topGroupConversation(conversation.groupId, !conversation.top)
|
||||||
|
}
|
||||||
|
this.afterDoAction(promise, title)
|
||||||
|
},
|
||||||
|
removeConversation(){
|
||||||
|
wx.showLoading({title: "删除中"});
|
||||||
|
let conversation = this.data.action.conversation;
|
||||||
|
let promise;
|
||||||
|
if(conversation.type === wx.GoEasyIM.SCENE.PRIVATE){
|
||||||
|
promise = wx.im.removePrivateConversation(conversation.userId);
|
||||||
|
}else{
|
||||||
|
promise = wx.im.removeGroupConversation(conversation.groupId);
|
||||||
|
}
|
||||||
|
this.afterDoAction(promise, '删除失败')
|
||||||
|
},
|
||||||
|
afterDoAction (promise, failedDescription) {
|
||||||
|
promise.then(() => {
|
||||||
|
wx.hideLoading()
|
||||||
|
}).catch(() => {
|
||||||
|
let self = this;
|
||||||
|
wx.hideLoading();
|
||||||
|
this.setData({
|
||||||
|
["action.showToast"]: true,
|
||||||
|
["action.toastMessage"]: failedDescription,
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
self.setData({
|
||||||
|
["action.showToast"]: false
|
||||||
|
});
|
||||||
|
},2000);
|
||||||
|
});
|
||||||
|
this.setData({
|
||||||
|
["action.show"]: false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
closeMask(){
|
||||||
|
this.setData({
|
||||||
|
["action.show"]: false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
3
pages/conversations/conversations.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "会话列表"
|
||||||
|
}
|
||||||
43
pages/conversations/conversations.wxml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<view class="conversations-container">
|
||||||
|
<scroll-view class="conversations" scroll-y="true" enable-flex="true">
|
||||||
|
<view wx:if="{{conversations.length !=0}}">
|
||||||
|
<view class="scroll-item" wx:for="{{conversations}}" wx:for-item="conversation" wx:key="key">
|
||||||
|
<view class="item-head">
|
||||||
|
<image src="{{conversation.data.avatar}}" class="head-icon"></image>
|
||||||
|
<view wx:if="{{conversation.unread}}" class="item-head_unread">{{conversation.unread}}</view>
|
||||||
|
</view>
|
||||||
|
<view class="scroll-item_info">
|
||||||
|
<view class="item-info-top">
|
||||||
|
<text class="item-info-top_name">{{conversation.data.name}}</text>
|
||||||
|
<view class="item-info-top_time">{{conversation.lastMessage.date}}</view>
|
||||||
|
</view>
|
||||||
|
<view class="item-info-bottom">
|
||||||
|
<view class="item-info-bottom-item" bindtap="navigateToChat" data-conversation="{{conversation}}" >
|
||||||
|
<view class="item-info-top_content" wx:if="{{conversation.lastMessage.type == 'text'}}">{{conversation.lastMessage.payload.text}}</view>
|
||||||
|
<view class="item-info-top_content" wx:elif="{{conversation.lastMessage.type == 'video'}}">[视频消息]</view>
|
||||||
|
<view class="item-info-top_content" wx:elif="{{conversation.lastMessage.type == 'audio'}}">[语音消息]</view>
|
||||||
|
<view class="item-info-top_content" wx:elif="{{conversation.lastMessage.type == 'image'}}">[图片消息]</view>
|
||||||
|
<view class="item-info-top_content" wx:elif="{{conversation.lastMessage.type == 'file'}}">[文件消息]</view>
|
||||||
|
<view class="item-info-top_content" wx:elif="{{conversation.lastMessage.type == 'order'}}">[自定义消息:订单]</view>
|
||||||
|
<view class="item-info-top_content" wx:else>[[未识别内容]]</view>
|
||||||
|
<image class="item-info-bottom_action" catchtap="showAction" data-conversation="{{conversation}}" src="../../static/images/action.png"></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="no-conversation" wx:else>
|
||||||
|
当前没有会话为空
|
||||||
|
</view>
|
||||||
|
<view class="action-container" wx:if="{{action.show}}">
|
||||||
|
<view class="layer" bindtap="closeMask"></view>
|
||||||
|
<view class="action-box">
|
||||||
|
<view class="action-item" bindtap="topConversation">{{action.conversation.top ? '取消置顶' : '置顶聊天'}}</view>
|
||||||
|
<view class="action-item" bindtap="removeConversation">删除聊天</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="action-toast" wx:if="{{action.showToast}}">
|
||||||
|
{{action.toastMessage}}
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
164
pages/conversations/conversations.wxss
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
page{ height: 100%; }
|
||||||
|
|
||||||
|
.conversations-container{
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.conversations{
|
||||||
|
width: 750rpx;
|
||||||
|
height: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
.conversations .scroll-item{
|
||||||
|
height: 152rpx;
|
||||||
|
padding-left: 32rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.conversations .scroll-item .head-icon{
|
||||||
|
width:100rpx;
|
||||||
|
height: 100rpx;
|
||||||
|
margin-right: 28rpx;
|
||||||
|
}
|
||||||
|
.conversations .scroll-item_info{
|
||||||
|
height: 151rpx;
|
||||||
|
width: 590rpx;
|
||||||
|
padding-right: 32rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-bottom: 1px solid #EFEFEF;
|
||||||
|
}
|
||||||
|
.conversations .scroll-item_info .item-info-top{
|
||||||
|
padding-top: 20rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
line-height: 60rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
}
|
||||||
|
.conversations .item-info-top_name{
|
||||||
|
font-size: 34rpx;
|
||||||
|
color: #262628;
|
||||||
|
}
|
||||||
|
.conversations .item-info-top_time{
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: rgba(179, 179, 179, 0.8);
|
||||||
|
font-family: Source Han Sans CN;
|
||||||
|
}
|
||||||
|
.conversations .item-info-bottom{
|
||||||
|
height: 40rpx;
|
||||||
|
line-height: 40rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.conversations .item-info-bottom-item{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.item-info-bottom .item-info-top_content{
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #b3b3b3;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow:ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
}
|
||||||
|
.item-info-bottom .item-info-bottom_unread{
|
||||||
|
padding: 6rpx;
|
||||||
|
background-color: #EE593C;
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-size: 24rpx;
|
||||||
|
line-height: 28rpx;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
min-width: 24rpx;
|
||||||
|
min-height: 24rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.no-conversation{
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
height: 80rpx;
|
||||||
|
line-height: 80rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #9D9D9D;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-info-bottom .item-info-bottom_action{
|
||||||
|
width:30rpx;
|
||||||
|
height: 30rpx;
|
||||||
|
font-size: 20rpx;
|
||||||
|
background-size: 28rpx 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-head{
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-head .item-head_unread{
|
||||||
|
padding: 6rpx;
|
||||||
|
background-color: #EE593C;
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-size: 24rpx;
|
||||||
|
line-height: 28rpx;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
min-width: 24rpx;
|
||||||
|
min-height: 24rpx;
|
||||||
|
text-align: center;
|
||||||
|
position: absolute;
|
||||||
|
top:0;
|
||||||
|
right: 15rpx;
|
||||||
|
}
|
||||||
|
.action-container{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.action-container .layer{
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
background: rgba(51, 51, 51, 0.5);
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
.action-box{
|
||||||
|
width: 400rpx;
|
||||||
|
height: 240rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
position: relative;
|
||||||
|
z-index: 100;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.action-item{
|
||||||
|
text-align: center;
|
||||||
|
line-height: 120rpx;
|
||||||
|
font-size: 34rpx;
|
||||||
|
color: #262628;
|
||||||
|
border-bottom: 1px solid #EFEFEF;
|
||||||
|
|
||||||
|
}
|
||||||
|
.action-toast{
|
||||||
|
position: absolute;
|
||||||
|
width: 400rpx;
|
||||||
|
height: 100rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
line-height: 100rpx;
|
||||||
|
background: #9D9D9D;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
top:50%;
|
||||||
|
left: 50%;
|
||||||
|
margin: -50rpx -200rpx;
|
||||||
|
text-align: center;
|
||||||
|
color: #262628;
|
||||||
|
}
|
||||||
27
pages/login/login.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/* login.js */
|
||||||
|
import restapi from "../../static/lib/restapi";
|
||||||
|
Page({
|
||||||
|
data: {
|
||||||
|
username:"",
|
||||||
|
password:"",
|
||||||
|
showError:false,
|
||||||
|
},
|
||||||
|
login: function(e) {
|
||||||
|
let username = e.detail.value.username;
|
||||||
|
let password = e.detail.value.password;
|
||||||
|
if (username.trim() !== "" && password.trim() !== "") {
|
||||||
|
let user = restapi.findUser(username,password);
|
||||||
|
if (user) {
|
||||||
|
wx.setStorageSync('currentUser',user);
|
||||||
|
// 页面跳转
|
||||||
|
wx.switchTab({
|
||||||
|
url:'../conversations/conversations'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setData({
|
||||||
|
showError:true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
3
pages/login/login.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": ""
|
||||||
|
}
|
||||||
13
pages/login/login.wxml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!--login.wxml-->
|
||||||
|
<view class="container">
|
||||||
|
<view class="login">
|
||||||
|
<form bindsubmit="login">
|
||||||
|
<view class="title">GoEasy IM</view>
|
||||||
|
<input class="input-box" type="text" placeholder="请输入账号" name="username" confirm-hold hold-keyboard="{{true}}" adjust-position="{{true}}"/>
|
||||||
|
<input class="input-box" type="password" placeholder="请输入密码" name= "password" />
|
||||||
|
<view wx:if="{{showError}}" class="error">请输入正确的用户名和密码</view>
|
||||||
|
<button class="login-btn" form-type="submit">登录</button>
|
||||||
|
<view class="login-tips">登录所需用户名和密码见 restapi.js</view>
|
||||||
|
</form>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
67
pages/login/login.wxss
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/* login.wxss */
|
||||||
|
|
||||||
|
page {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
font-family: Source Han Sans CN;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 72rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
form{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 84rpx;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #D02129;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-tips {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 40rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 100rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
border: 2rpx solid #D02129;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 120rpx;
|
||||||
|
padding-left: 20rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
color: #D02129;
|
||||||
|
background: rgba(208, 33, 41, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100rpx;
|
||||||
|
color: #FFFFFF;
|
||||||
|
background: #D02129;
|
||||||
|
}
|
||||||
29
pages/mine/mine.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/* login.js */
|
||||||
|
const app = getApp()
|
||||||
|
Page({
|
||||||
|
data : {
|
||||||
|
currentUser : null
|
||||||
|
},
|
||||||
|
onShow () {
|
||||||
|
let service = app.globalData.imService;
|
||||||
|
this.setData({
|
||||||
|
currentUser : service.currentUser
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!this.data.currentUser) {
|
||||||
|
wx.redirectTo({
|
||||||
|
url : '../login/login'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
logout () {
|
||||||
|
wx.im.disconnect().then(() => {
|
||||||
|
console.log("断连成功");
|
||||||
|
wx.removeStorageSync("currentUser");
|
||||||
|
app.globalData.imService= null;
|
||||||
|
wx.redirectTo({
|
||||||
|
url: '../login/login'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
3
pages/mine/mine.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "我的"
|
||||||
|
}
|
||||||
11
pages/mine/mine.wxml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<!--login.wxml-->
|
||||||
|
<div class="mine">
|
||||||
|
<div class="top">
|
||||||
|
<image src="{{currentUser.avatar}}"></image>
|
||||||
|
<view class="name">{{currentUser.name}}</view>
|
||||||
|
</div>
|
||||||
|
<div class="bottom">
|
||||||
|
<text>欢迎体验GoEasyIM</text>
|
||||||
|
<view class="logout" bindtap="logout">注销</view>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
36
pages/mine/mine.wxss
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
.mine{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.top{
|
||||||
|
height: 400rpx;
|
||||||
|
background: #F3F4F7;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.top image{
|
||||||
|
width:156rpx;
|
||||||
|
height: 156rpx;
|
||||||
|
border-radius: 156rpx;
|
||||||
|
}
|
||||||
|
.top .name{
|
||||||
|
line-height: 80rpx;
|
||||||
|
}
|
||||||
|
.bottom{
|
||||||
|
text-align: center;
|
||||||
|
line-height: 200rpx;
|
||||||
|
}
|
||||||
|
.bottom .logout{
|
||||||
|
width:266rpx;
|
||||||
|
height: 76rpx;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
background: #D02129;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 32rpx;
|
||||||
|
margin:0 auto;
|
||||||
|
line-height: 76rpx;
|
||||||
|
}
|
||||||
BIN
static/images/Arrow-Left.png
Normal file
|
After Width: | Height: | Size: 222 B |
BIN
static/images/Avatar-1.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
static/images/Avatar-2.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
static/images/Avatar-3.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
static/images/Avatar-4.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
static/images/Vector.png
Normal file
|
After Width: | Height: | Size: 714 B |
BIN
static/images/action.png
Normal file
|
After Width: | Height: | Size: 203 B |
BIN
static/images/audioImage/play.gif
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
static/images/audioImage/voice.png
Normal file
|
After Width: | Height: | Size: 300 B |
BIN
static/images/chat-active.png
Normal file
|
After Width: | Height: | Size: 573 B |
BIN
static/images/chat.png
Normal file
|
After Width: | Height: | Size: 802 B |
BIN
static/images/contacts-active.png
Normal file
|
After Width: | Height: | Size: 588 B |
BIN
static/images/contacts.png
Normal file
|
After Width: | Height: | Size: 830 B |
BIN
static/images/dingdan.png
Normal file
|
After Width: | Height: | Size: 840 B |
BIN
static/images/emoji.png
Normal file
|
After Width: | Height: | Size: 717 B |
BIN
static/images/failed.png
Normal file
|
After Width: | Height: | Size: 292 B |
BIN
static/images/file-content.png
Normal file
|
After Width: | Height: | Size: 808 B |
BIN
static/images/file-icon.png
Normal file
|
After Width: | Height: | Size: 372 B |
BIN
static/images/file.png
Normal file
|
After Width: | Height: | Size: 463 B |
BIN
static/images/goeasy.jpeg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
static/images/green-dot.png
Normal file
|
After Width: | Height: | Size: 373 B |
BIN
static/images/group-icon.png
Normal file
|
After Width: | Height: | Size: 502 B |
BIN
static/images/group.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
static/images/im.gif
Normal file
|
After Width: | Height: | Size: 306 KiB |
BIN
static/images/jianpan.png
Normal file
|
After Width: | Height: | Size: 932 B |
BIN
static/images/loading.gif
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
static/images/mine-active.png
Normal file
|
After Width: | Height: | Size: 503 B |
BIN
static/images/mine.png
Normal file
|
After Width: | Height: | Size: 770 B |
BIN
static/images/more.png
Normal file
|
After Width: | Height: | Size: 664 B |
BIN
static/images/pending.gif
Normal file
|
After Width: | Height: | Size: 771 B |
BIN
static/images/record-appearance-icon.png
Normal file
|
After Width: | Height: | Size: 620 B |
BIN
static/images/recordImage/loading.gif
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
static/images/shipin.png
Normal file
|
After Width: | Height: | Size: 1009 B |
BIN
static/images/tupian.png
Normal file
|
After Width: | Height: | Size: 972 B |
BIN
static/images/uniapp.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
static/images/videoImage/play.png
Normal file
|
After Width: | Height: | Size: 560 B |
BIN
static/images/wx.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
static/images/zidingyi.png
Normal file
|
After Width: | Height: | Size: 900 B |
33
static/lib/EmojiDecoder.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* @Author: jack.lu
|
||||||
|
* @Date: 2020/9/11
|
||||||
|
* @Last Modified by: jack.lu
|
||||||
|
* @Last Modified time: 2020/9/11 4:35 下午
|
||||||
|
*/
|
||||||
|
|
||||||
|
class EmojiDecoder {
|
||||||
|
emojiMap = null;
|
||||||
|
url = "";
|
||||||
|
patterns = [];
|
||||||
|
metaChars = /[[\]{}()*+?.\\|^$\-,&#\s]/g;
|
||||||
|
decode = this.decode;
|
||||||
|
|
||||||
|
constructor(url,emojiMap) {
|
||||||
|
this.url = url || '';
|
||||||
|
this.emojiMap = emojiMap || {};
|
||||||
|
for (let i in this.emojiMap) {
|
||||||
|
if (this.emojiMap.hasOwnProperty(i)){
|
||||||
|
this.patterns.push('('+i.replace(this.metaChars, "\\$&")+')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
decode (text) {
|
||||||
|
return text.replace(new RegExp(this.patterns.join('|'),'g'), (match) => {
|
||||||
|
return typeof this.emojiMap[match] != 'undefined' ? '<img height="20rpx" width="20rpx" src="'+this.url+this.emojiMap[match]+'" />' : match;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EmojiDecoder
|
||||||
2
static/lib/goeasy-im-1.5.1.js
Normal file
205
static/lib/imservice.js
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* @Author: jack.lu
|
||||||
|
* @Date: 2020-4-21 10:10:20
|
||||||
|
* @Last Modified by: jack.lu
|
||||||
|
* @Last Modified time: 2020-4-21 15:01:41
|
||||||
|
*/
|
||||||
|
|
||||||
|
import GoEasyIM from './goeasy-im-1.5.1';
|
||||||
|
import restApi from './restapi';
|
||||||
|
|
||||||
|
function Friend(uuid, name, avatar) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.name = name;
|
||||||
|
this.avatar = avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Group(uuid, name, avatar) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.name = name;
|
||||||
|
this.avatar = avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CurrentUser(uuid, name, avatar) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.name = name;
|
||||||
|
this.avatar = avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
function IMService(im) {
|
||||||
|
this.im = im;
|
||||||
|
//当前“我”
|
||||||
|
this.currentUser = null;
|
||||||
|
|
||||||
|
//私聊消息记录,map格式,每个好友对应一个数组
|
||||||
|
this.privateMessages = {};
|
||||||
|
|
||||||
|
//群聊消息记录,map格式,每个群对应一个数组
|
||||||
|
this.groupMessages = {};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 监听器们
|
||||||
|
*
|
||||||
|
* 可以在页面里,根据需求,重写以下监听器,
|
||||||
|
* 便于当各种事件触发时,页面能够执行对应的响应
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
//收到一条私聊消息
|
||||||
|
this.onNewPrivateMessageReceive = function (friendId, message) {};
|
||||||
|
//收到一条群聊消息
|
||||||
|
this.onNewGroupMessageReceive = function (groupId, message) {};
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取群成员
|
||||||
|
IMService.prototype.getGroupMembers = function (groupId) {
|
||||||
|
let members = restApi.findGroupMembers(groupId);
|
||||||
|
let membersMap = {};
|
||||||
|
members.map(item => {
|
||||||
|
membersMap[item.uuid] = item
|
||||||
|
});
|
||||||
|
return membersMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
IMService.prototype.findGroupById = function (groupId) {
|
||||||
|
let group = restApi.findGroupById(groupId);
|
||||||
|
return new Group(group.uuid, group.name, group.avatar);
|
||||||
|
};
|
||||||
|
|
||||||
|
IMService.prototype.getGroupMessages = function (groupId) {
|
||||||
|
if (!this.groupMessages[groupId]) {
|
||||||
|
this.groupMessages[groupId] = [];
|
||||||
|
}
|
||||||
|
return this.groupMessages[groupId]
|
||||||
|
};
|
||||||
|
|
||||||
|
IMService.prototype.findFriendById = function (userId) {
|
||||||
|
let user = restApi.findUserById(userId);
|
||||||
|
return new Friend(user.uuid, user.name, user.avatar);
|
||||||
|
};
|
||||||
|
|
||||||
|
IMService.prototype.getPrivateMessages = function (friendId) {
|
||||||
|
if (!this.privateMessages[friendId]) {
|
||||||
|
this.privateMessages[friendId] = [];
|
||||||
|
}
|
||||||
|
return this.privateMessages[friendId]
|
||||||
|
};
|
||||||
|
|
||||||
|
//连接GoEasy
|
||||||
|
IMService.prototype.connectIM = function (currentUser) {
|
||||||
|
this.currentUser = currentUser;
|
||||||
|
//初始化IM相关的监听器
|
||||||
|
this.initialIMListeners();
|
||||||
|
this.im.connect({
|
||||||
|
id: this.currentUser.uuid,
|
||||||
|
data: {
|
||||||
|
avatar: this.currentUser.avatar,
|
||||||
|
name: this.currentUser.name
|
||||||
|
}
|
||||||
|
}).then(() => {
|
||||||
|
console.log('connect成功')
|
||||||
|
}).catch(error => {
|
||||||
|
console.log('connect失败,请确保网络正常,appkey和host正确,code:' + error.code + " content:" + error.content);
|
||||||
|
});
|
||||||
|
this.subscribeGroupMessage();
|
||||||
|
};
|
||||||
|
|
||||||
|
IMService.prototype.subscribeGroupMessage = function () {
|
||||||
|
let groups = restApi.findGroups(this.currentUser);
|
||||||
|
let groupIds = groups.map(item => item.uuid);
|
||||||
|
this.im.subscribeGroup(groupIds)
|
||||||
|
.then(() => {
|
||||||
|
console.log('订阅群消息成功')
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log('订阅群消息失败')
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//IM监听
|
||||||
|
IMService.prototype.initialIMListeners = function () {
|
||||||
|
this.im.on(GoEasyIM.EVENT.CONNECTED, () => {
|
||||||
|
console.log('连接成功.')
|
||||||
|
});
|
||||||
|
|
||||||
|
this.im.on(GoEasyIM.EVENT.DISCONNECTED, () => {
|
||||||
|
console.log('连接断开.')
|
||||||
|
});
|
||||||
|
|
||||||
|
this.im.on(GoEasyIM.EVENT.CONNECTING, (times) => {
|
||||||
|
console.log('连接中', times);
|
||||||
|
});
|
||||||
|
|
||||||
|
//监听私聊消息
|
||||||
|
this.im.on(GoEasyIM.EVENT.PRIVATE_MESSAGE_RECEIVED, (message) => {
|
||||||
|
//更新私聊消息记录
|
||||||
|
let friendId;
|
||||||
|
if (this.currentUser.uuid == message.senderId) {
|
||||||
|
friendId = message.receiverId;
|
||||||
|
} else {
|
||||||
|
friendId = message.senderId;
|
||||||
|
}
|
||||||
|
let friendMessages = this.getPrivateMessages(friendId);
|
||||||
|
friendMessages.push(message);
|
||||||
|
//如果页面传入了相应的listener,执行listener
|
||||||
|
this.onNewPrivateMessageReceive(friendId, message);
|
||||||
|
});
|
||||||
|
|
||||||
|
//监听群聊消息
|
||||||
|
this.im.on(GoEasyIM.EVENT.GROUP_MESSAGE_RECEIVED, (message) => {
|
||||||
|
let groupId = message.groupId;
|
||||||
|
|
||||||
|
//更新群聊消息记录
|
||||||
|
let groupMessages = this.getGroupMessages(groupId);
|
||||||
|
groupMessages.push(message);
|
||||||
|
|
||||||
|
//如果页面传入了相应的listener,执行listener
|
||||||
|
this.onNewGroupMessageReceive(groupId, message);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
//加载单聊历史消息
|
||||||
|
IMService.prototype.loadPrivateHistoryMessage = function (friendId, timeStamp) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.im.history({
|
||||||
|
friendId: friendId,
|
||||||
|
lastTimestamp: timeStamp
|
||||||
|
}).then(result => {
|
||||||
|
let history = result.content;
|
||||||
|
let friendMessages = this.getPrivateMessages(friendId);
|
||||||
|
for (let i = history.length - 1; i >=0; i--) {
|
||||||
|
friendMessages.unshift(history[i])
|
||||||
|
}
|
||||||
|
resolve(friendMessages)
|
||||||
|
}).catch(error => {
|
||||||
|
if (error.code == 401) {
|
||||||
|
console.log("您尚未开通历史消息,请登录GoEasy,查看应用详情里自助启用.");
|
||||||
|
}
|
||||||
|
reject(error)
|
||||||
|
});
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
//群聊历史消息
|
||||||
|
IMService.prototype.loadGroupHistoryMessage = function (groupId, timeStamp) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.im.history({
|
||||||
|
groupId: groupId,
|
||||||
|
lastTimestamp: timeStamp
|
||||||
|
}).then(result => {
|
||||||
|
let history = result.content;
|
||||||
|
let chatMessage = this.getGroupMessages(groupId);
|
||||||
|
for (let i = history.length - 1; i >= 0; i--) {
|
||||||
|
chatMessage.unshift(history[i]);
|
||||||
|
}
|
||||||
|
resolve(chatMessage)
|
||||||
|
}).catch(error => {
|
||||||
|
if (error.code == 401) {
|
||||||
|
console.log("您尚未开通历史消息,请登录GoEasy,查看应用详情里自助启用.");
|
||||||
|
}
|
||||||
|
reject(error)
|
||||||
|
});
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
export default IMService;
|
||||||
94
static/lib/restapi.js
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
//用户数据示例
|
||||||
|
let users = [
|
||||||
|
{
|
||||||
|
"uuid": "08c0a6ec-a42b-47b2-bb1e-15e0f5f9a19a",
|
||||||
|
"name": "Mattie",
|
||||||
|
"password": "123",
|
||||||
|
"avatar": '/static/images/Avatar-1.png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "3bb179af-bcc5-4fe0-9dac-c05688484649",
|
||||||
|
"name": "Wallace",
|
||||||
|
"password": "123",
|
||||||
|
"avatar": '/static/images/Avatar-2.png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "fdee46b0-4b01-4590-bdba-6586d7617f95",
|
||||||
|
"name": "Tracy",
|
||||||
|
"password": "123",
|
||||||
|
"avatar": '/static/images/Avatar-3.png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "33c3693b-dbb0-4bc9-99c6-fa77b9eb763f",
|
||||||
|
"name": "Juanita",
|
||||||
|
"password": "123",
|
||||||
|
"avatar": '/static/images/Avatar-4.png'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
//群数据示例
|
||||||
|
let groups = [
|
||||||
|
{
|
||||||
|
"uuid": "group-a42b-47b2-bb1e-15e0f5f9a19a",
|
||||||
|
"name": "小程序交流群",
|
||||||
|
"avatar" : '/static/images/wx.png',
|
||||||
|
"userList": ['08c0a6ec-a42b-47b2-bb1e-15e0f5f9a19a', '3bb179af-bcc5-4fe0-9dac-c05688484649', 'fdee46b0-4b01-4590-bdba-6586d7617f95', '33c3693b-dbb0-4bc9-99c6-fa77b9eb763f']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "group-4b01-4590-bdba-6586d7617f95",
|
||||||
|
"name": "UniApp交流群",
|
||||||
|
"avatar" : '/static/images/uniapp.png',
|
||||||
|
"userList": ['08c0a6ec-a42b-47b2-bb1e-15e0f5f9a19a', 'fdee46b0-4b01-4590-bdba-6586d7617f95', '33c3693b-dbb0-4bc9-99c6-fa77b9eb763f']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "group-dbb0-4bc9-99c6-fa77b9eb763f",
|
||||||
|
"name": "GoEasy交流群",
|
||||||
|
"avatar" : '/static/images/goeasy.jpeg',
|
||||||
|
"userList": ['08c0a6ec-a42b-47b2-bb1e-15e0f5f9a19a', '3bb179af-bcc5-4fe0-9dac-c05688484649']
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
function RestApi() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RestApi.prototype.findFriends = function (user) {
|
||||||
|
var friendList = users.filter(v => v.uuid != user.uuid);
|
||||||
|
return friendList;
|
||||||
|
};
|
||||||
|
|
||||||
|
RestApi.prototype.findGroups = function (user) {
|
||||||
|
var groupList = groups.filter(v => v.userList.find(id => id == user.uuid));
|
||||||
|
return groupList;
|
||||||
|
};
|
||||||
|
|
||||||
|
RestApi.prototype.findUser = function (username, password) {
|
||||||
|
var user = users.find(user => (user.name == username && user.password == password))
|
||||||
|
return user;
|
||||||
|
};
|
||||||
|
|
||||||
|
RestApi.prototype.findGroupById = function (groupId) {
|
||||||
|
var group = groups.find(group => (group.uuid == groupId));
|
||||||
|
return group;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
RestApi.prototype.findUserById = function (userId) {
|
||||||
|
var user = users.find(user => (user.uuid == userId))
|
||||||
|
return user;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
RestApi.prototype.findGroupMembers = function (groupId) {
|
||||||
|
let members = [];
|
||||||
|
let group = groups.find(v => v.uuid == groupId);
|
||||||
|
users.map(user => {
|
||||||
|
if (group.userList.find(v => v == user.uuid)) {
|
||||||
|
members.push(user)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return members;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default new RestApi();
|
||||||