From ab7a7cf62f3dcc499ca673c848434280e6c8108a Mon Sep 17 00:00:00 2001
From: luyuan <1162963624@qq.com>
Date: Wed, 7 Apr 2021 15:18:17 +0800
Subject: [PATCH] im ok
---
app.js | 3 +-
app.json | 17 +-
.../GoEasyAudioPlayer/goEasyAudioPlayer.js | 63 ++++
.../GoEasyAudioPlayer/goEasyAudioPlayer.json | 4 +
.../GoEasyAudioPlayer/goEasyAudioPlayer.wxml | 7 +
.../GoEasyAudioPlayer/goEasyAudioPlayer.wxss | 34 ++
.../GoEasyCustomMessage/customMessage.js | 49 +++
.../GoEasyCustomMessage/customMessage.json | 5 +
.../GoEasyCustomMessage/customMessage.wxml | 29 ++
.../GoEasyCustomMessage/customMessage.wxss | 78 ++++
components/GoEasyRecorder/goEasyRecorder.js | 67 ++++
components/GoEasyRecorder/goEasyRecorder.json | 4 +
components/GoEasyRecorder/goEasyRecorder.wxml | 6 +
components/GoEasyRecorder/goEasyRecorder.wxss | 32 ++
.../GoEasyVideoPlayer/goEasyVideoPlayer.js | 48 +++
.../GoEasyVideoPlayer/goEasyVideoPlayer.json | 4 +
.../GoEasyVideoPlayer/goEasyVideoPlayer.wxml | 3 +
.../GoEasyVideoPlayer/goEasyVideoPlayer.wxss | 11 +
pages/chat/groupChat/groupChat.js | 332 ++++++++++++++++++
pages/chat/groupChat/groupChat.json | 11 +
pages/chat/groupChat/groupChat.wxml | 91 +++++
pages/chat/groupChat/groupChat.wxss | 276 +++++++++++++++
pages/chat/groupMember/groupMember.js | 20 ++
pages/chat/groupMember/groupMember.json | 3 +
pages/chat/groupMember/groupMember.wxml | 15 +
pages/chat/groupMember/groupMember.wxss | 51 +++
pages/chat/privateChat/privateChat.js | 323 +++++++++++++++++
pages/chat/privateChat/privateChat.json | 11 +
pages/chat/privateChat/privateChat.wxml | 92 +++++
pages/chat/privateChat/privateChat.wxss | 264 ++++++++++++++
pages/contacts/contacts.js | 34 ++
pages/contacts/contacts.json | 3 +
pages/contacts/contacts.wxml | 26 ++
pages/contacts/contacts.wxss | 87 +++++
pages/conversations/conversations.js | 142 ++++++++
pages/conversations/conversations.json | 3 +
pages/conversations/conversations.wxml | 43 +++
pages/conversations/conversations.wxss | 164 +++++++++
pages/login/login.js | 27 ++
pages/login/login.json | 3 +
pages/login/login.wxml | 13 +
pages/login/login.wxss | 67 ++++
pages/mine/mine.js | 29 ++
pages/mine/mine.json | 3 +
pages/mine/mine.wxml | 11 +
pages/mine/mine.wxss | 36 ++
static/images/Arrow-Left.png | Bin 0 -> 222 bytes
static/images/Avatar-1.png | Bin 0 -> 3609 bytes
static/images/Avatar-2.png | Bin 0 -> 3611 bytes
static/images/Avatar-3.png | Bin 0 -> 3485 bytes
static/images/Avatar-4.png | Bin 0 -> 3546 bytes
static/images/Vector.png | Bin 0 -> 714 bytes
static/images/action.png | Bin 0 -> 203 bytes
static/images/audioImage/play.gif | Bin 0 -> 2321 bytes
static/images/audioImage/voice.png | Bin 0 -> 300 bytes
static/images/chat-active.png | Bin 0 -> 573 bytes
static/images/chat.png | Bin 0 -> 802 bytes
static/images/contacts-active.png | Bin 0 -> 588 bytes
static/images/contacts.png | Bin 0 -> 830 bytes
static/images/dingdan.png | Bin 0 -> 840 bytes
static/images/emoji.png | Bin 0 -> 717 bytes
static/images/failed.png | Bin 0 -> 292 bytes
static/images/file-content.png | Bin 0 -> 808 bytes
static/images/file-icon.png | Bin 0 -> 372 bytes
static/images/file.png | Bin 0 -> 463 bytes
static/images/goeasy.jpeg | Bin 0 -> 24003 bytes
static/images/green-dot.png | Bin 0 -> 373 bytes
static/images/group-icon.png | Bin 0 -> 502 bytes
static/images/group.png | Bin 0 -> 1778 bytes
static/images/im.gif | Bin 0 -> 313497 bytes
static/images/jianpan.png | Bin 0 -> 932 bytes
static/images/loading.gif | Bin 0 -> 2711 bytes
static/images/mine-active.png | Bin 0 -> 503 bytes
static/images/mine.png | Bin 0 -> 770 bytes
static/images/more.png | Bin 0 -> 664 bytes
static/images/pending.gif | Bin 0 -> 771 bytes
static/images/record-appearance-icon.png | Bin 0 -> 620 bytes
static/images/recordImage/loading.gif | Bin 0 -> 12122 bytes
static/images/shipin.png | Bin 0 -> 1009 bytes
static/images/tupian.png | Bin 0 -> 972 bytes
static/images/uniapp.png | Bin 0 -> 1039 bytes
static/images/videoImage/play.png | Bin 0 -> 560 bytes
static/images/wx.png | Bin 0 -> 1372 bytes
static/images/zidingyi.png | Bin 0 -> 900 bytes
static/lib/EmojiDecoder.js | 33 ++
static/lib/goeasy-im-1.5.1.js | 2 +
static/lib/imservice.js | 205 +++++++++++
static/lib/restapi.js | 94 +++++
88 files changed, 2974 insertions(+), 4 deletions(-)
create mode 100644 components/GoEasyAudioPlayer/goEasyAudioPlayer.js
create mode 100644 components/GoEasyAudioPlayer/goEasyAudioPlayer.json
create mode 100644 components/GoEasyAudioPlayer/goEasyAudioPlayer.wxml
create mode 100644 components/GoEasyAudioPlayer/goEasyAudioPlayer.wxss
create mode 100644 components/GoEasyCustomMessage/customMessage.js
create mode 100644 components/GoEasyCustomMessage/customMessage.json
create mode 100644 components/GoEasyCustomMessage/customMessage.wxml
create mode 100644 components/GoEasyCustomMessage/customMessage.wxss
create mode 100644 components/GoEasyRecorder/goEasyRecorder.js
create mode 100644 components/GoEasyRecorder/goEasyRecorder.json
create mode 100644 components/GoEasyRecorder/goEasyRecorder.wxml
create mode 100644 components/GoEasyRecorder/goEasyRecorder.wxss
create mode 100644 components/GoEasyVideoPlayer/goEasyVideoPlayer.js
create mode 100644 components/GoEasyVideoPlayer/goEasyVideoPlayer.json
create mode 100644 components/GoEasyVideoPlayer/goEasyVideoPlayer.wxml
create mode 100644 components/GoEasyVideoPlayer/goEasyVideoPlayer.wxss
create mode 100644 pages/chat/groupChat/groupChat.js
create mode 100644 pages/chat/groupChat/groupChat.json
create mode 100644 pages/chat/groupChat/groupChat.wxml
create mode 100644 pages/chat/groupChat/groupChat.wxss
create mode 100644 pages/chat/groupMember/groupMember.js
create mode 100644 pages/chat/groupMember/groupMember.json
create mode 100644 pages/chat/groupMember/groupMember.wxml
create mode 100644 pages/chat/groupMember/groupMember.wxss
create mode 100644 pages/chat/privateChat/privateChat.js
create mode 100644 pages/chat/privateChat/privateChat.json
create mode 100644 pages/chat/privateChat/privateChat.wxml
create mode 100644 pages/chat/privateChat/privateChat.wxss
create mode 100644 pages/contacts/contacts.js
create mode 100644 pages/contacts/contacts.json
create mode 100644 pages/contacts/contacts.wxml
create mode 100644 pages/contacts/contacts.wxss
create mode 100644 pages/conversations/conversations.js
create mode 100644 pages/conversations/conversations.json
create mode 100644 pages/conversations/conversations.wxml
create mode 100644 pages/conversations/conversations.wxss
create mode 100644 pages/login/login.js
create mode 100644 pages/login/login.json
create mode 100644 pages/login/login.wxml
create mode 100644 pages/login/login.wxss
create mode 100644 pages/mine/mine.js
create mode 100644 pages/mine/mine.json
create mode 100644 pages/mine/mine.wxml
create mode 100644 pages/mine/mine.wxss
create mode 100644 static/images/Arrow-Left.png
create mode 100644 static/images/Avatar-1.png
create mode 100644 static/images/Avatar-2.png
create mode 100644 static/images/Avatar-3.png
create mode 100644 static/images/Avatar-4.png
create mode 100644 static/images/Vector.png
create mode 100644 static/images/action.png
create mode 100644 static/images/audioImage/play.gif
create mode 100644 static/images/audioImage/voice.png
create mode 100644 static/images/chat-active.png
create mode 100644 static/images/chat.png
create mode 100644 static/images/contacts-active.png
create mode 100644 static/images/contacts.png
create mode 100644 static/images/dingdan.png
create mode 100644 static/images/emoji.png
create mode 100644 static/images/failed.png
create mode 100644 static/images/file-content.png
create mode 100644 static/images/file-icon.png
create mode 100644 static/images/file.png
create mode 100644 static/images/goeasy.jpeg
create mode 100644 static/images/green-dot.png
create mode 100644 static/images/group-icon.png
create mode 100644 static/images/group.png
create mode 100644 static/images/im.gif
create mode 100644 static/images/jianpan.png
create mode 100644 static/images/loading.gif
create mode 100644 static/images/mine-active.png
create mode 100644 static/images/mine.png
create mode 100644 static/images/more.png
create mode 100644 static/images/pending.gif
create mode 100644 static/images/record-appearance-icon.png
create mode 100644 static/images/recordImage/loading.gif
create mode 100644 static/images/shipin.png
create mode 100644 static/images/tupian.png
create mode 100644 static/images/uniapp.png
create mode 100644 static/images/videoImage/play.png
create mode 100644 static/images/wx.png
create mode 100644 static/images/zidingyi.png
create mode 100644 static/lib/EmojiDecoder.js
create mode 100644 static/lib/goeasy-im-1.5.1.js
create mode 100644 static/lib/imservice.js
create mode 100644 static/lib/restapi.js
diff --git a/app.js b/app.js
index ae201e6..c0e22cb 100644
--- a/app.js
+++ b/app.js
@@ -1,5 +1,6 @@
// app.js
-import GoEasyIM from './utils/goeasy-im-1.5.1.js';
+import GoEasyIM from './static/lib/goeasy-im-1.5.1.js';
+
App({
onLaunch: function () {
wx.im = GoEasyIM.getInstance({
diff --git a/app.json b/app.json
index 1b0f381..7fa7384 100644
--- a/app.json
+++ b/app.json
@@ -2,7 +2,14 @@
"pages":[
"pages/index/index",
"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":{
"color": "#f00",
@@ -12,8 +19,12 @@
"pagePath": "pages/index/index",
"text": "首页"
},{
- "pagePath": "pages/liaotian/liaotian",
- "text": "聊天"
+ "pagePath" : "pages/conversations/conversations",
+ "text":"信息"
+ },
+ {
+ "pagePath" : "pages/contacts/contacts",
+ "text" : "通讯录"
}]
},
"window":{
diff --git a/components/GoEasyAudioPlayer/goEasyAudioPlayer.js b/components/GoEasyAudioPlayer/goEasyAudioPlayer.js
new file mode 100644
index 0000000..6ba0d5e
--- /dev/null
+++ b/components/GoEasyAudioPlayer/goEasyAudioPlayer.js
@@ -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();
+ }
+ },
+})
diff --git a/components/GoEasyAudioPlayer/goEasyAudioPlayer.json b/components/GoEasyAudioPlayer/goEasyAudioPlayer.json
new file mode 100644
index 0000000..a89ef4d
--- /dev/null
+++ b/components/GoEasyAudioPlayer/goEasyAudioPlayer.json
@@ -0,0 +1,4 @@
+{
+ "component": true,
+ "usingComponents": {}
+}
diff --git a/components/GoEasyAudioPlayer/goEasyAudioPlayer.wxml b/components/GoEasyAudioPlayer/goEasyAudioPlayer.wxml
new file mode 100644
index 0000000..faa691e
--- /dev/null
+++ b/components/GoEasyAudioPlayer/goEasyAudioPlayer.wxml
@@ -0,0 +1,7 @@
+
+
+
+
+ {{finalDuration}}
+
+
diff --git a/components/GoEasyAudioPlayer/goEasyAudioPlayer.wxss b/components/GoEasyAudioPlayer/goEasyAudioPlayer.wxss
new file mode 100644
index 0000000..64d470d
--- /dev/null
+++ b/components/GoEasyAudioPlayer/goEasyAudioPlayer.wxss
@@ -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;
+}
\ No newline at end of file
diff --git a/components/GoEasyCustomMessage/customMessage.js b/components/GoEasyCustomMessage/customMessage.js
new file mode 100644
index 0000000..6f7e50c
--- /dev/null
+++ b/components/GoEasyCustomMessage/customMessage.js
@@ -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
+ });
+ }
+ }
+})
diff --git a/components/GoEasyCustomMessage/customMessage.json b/components/GoEasyCustomMessage/customMessage.json
new file mode 100644
index 0000000..d507499
--- /dev/null
+++ b/components/GoEasyCustomMessage/customMessage.json
@@ -0,0 +1,5 @@
+{
+ "component": true,
+ "usingComponents":{},
+ "navigationBarTitleText": "自定义消息"
+}
\ No newline at end of file
diff --git a/components/GoEasyCustomMessage/customMessage.wxml b/components/GoEasyCustomMessage/customMessage.wxml
new file mode 100644
index 0000000..3ba2596
--- /dev/null
+++ b/components/GoEasyCustomMessage/customMessage.wxml
@@ -0,0 +1,29 @@
+
+
+
+ 发送订单
+
+
+ 编号:
+ 商品:
+ 金额:
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 发送
+
+
+
+
+
+
diff --git a/components/GoEasyCustomMessage/customMessage.wxss b/components/GoEasyCustomMessage/customMessage.wxss
new file mode 100644
index 0000000..1c77a71
--- /dev/null
+++ b/components/GoEasyCustomMessage/customMessage.wxss
@@ -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)
+}
diff --git a/components/GoEasyRecorder/goEasyRecorder.js b/components/GoEasyRecorder/goEasyRecorder.js
new file mode 100644
index 0000000..4c2ebf6
--- /dev/null
+++ b/components/GoEasyRecorder/goEasyRecorder.js
@@ -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
+ });
+ });
+ }
+})
diff --git a/components/GoEasyRecorder/goEasyRecorder.json b/components/GoEasyRecorder/goEasyRecorder.json
new file mode 100644
index 0000000..a89ef4d
--- /dev/null
+++ b/components/GoEasyRecorder/goEasyRecorder.json
@@ -0,0 +1,4 @@
+{
+ "component": true,
+ "usingComponents": {}
+}
diff --git a/components/GoEasyRecorder/goEasyRecorder.wxml b/components/GoEasyRecorder/goEasyRecorder.wxml
new file mode 100644
index 0000000..3cf3a08
--- /dev/null
+++ b/components/GoEasyRecorder/goEasyRecorder.wxml
@@ -0,0 +1,6 @@
+
+
+ {{recording ? '松开发送' : '按下录音'}}
+
+
+
\ No newline at end of file
diff --git a/components/GoEasyRecorder/goEasyRecorder.wxss b/components/GoEasyRecorder/goEasyRecorder.wxss
new file mode 100644
index 0000000..5c5d196
--- /dev/null
+++ b/components/GoEasyRecorder/goEasyRecorder.wxss
@@ -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;
+}
diff --git a/components/GoEasyVideoPlayer/goEasyVideoPlayer.js b/components/GoEasyVideoPlayer/goEasyVideoPlayer.js
new file mode 100644
index 0000000..d3ec08a
--- /dev/null
+++ b/components/GoEasyVideoPlayer/goEasyVideoPlayer.js
@@ -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();
+ }
+ }
+})
diff --git a/components/GoEasyVideoPlayer/goEasyVideoPlayer.json b/components/GoEasyVideoPlayer/goEasyVideoPlayer.json
new file mode 100644
index 0000000..a89ef4d
--- /dev/null
+++ b/components/GoEasyVideoPlayer/goEasyVideoPlayer.json
@@ -0,0 +1,4 @@
+{
+ "component": true,
+ "usingComponents": {}
+}
diff --git a/components/GoEasyVideoPlayer/goEasyVideoPlayer.wxml b/components/GoEasyVideoPlayer/goEasyVideoPlayer.wxml
new file mode 100644
index 0000000..0f4e42c
--- /dev/null
+++ b/components/GoEasyVideoPlayer/goEasyVideoPlayer.wxml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/components/GoEasyVideoPlayer/goEasyVideoPlayer.wxss b/components/GoEasyVideoPlayer/goEasyVideoPlayer.wxss
new file mode 100644
index 0000000..c0e3ced
--- /dev/null
+++ b/components/GoEasyVideoPlayer/goEasyVideoPlayer.wxss
@@ -0,0 +1,11 @@
+.goeasy-video-player {
+ width: 100%;
+ height: 100%;
+}
+.mask {
+ position: absolute;
+ top: 0;
+ z-index: 1;
+ opacity: 0.6;
+ background: #333;
+}
diff --git a/pages/chat/groupChat/groupChat.js b/pages/chat/groupChat/groupChat.js
new file mode 100644
index 0000000..7f4b9e5
--- /dev/null
+++ b/pages/chat/groupChat/groupChat.js
@@ -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
+ })
+ }
+})
diff --git a/pages/chat/groupChat/groupChat.json b/pages/chat/groupChat/groupChat.json
new file mode 100644
index 0000000..a13c6f6
--- /dev/null
+++ b/pages/chat/groupChat/groupChat.json
@@ -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"
+ }
+}
\ No newline at end of file
diff --git a/pages/chat/groupChat/groupChat.wxml b/pages/chat/groupChat/groupChat.wxml
new file mode 100644
index 0000000..b598ba7
--- /dev/null
+++ b/pages/chat/groupChat/groupChat.wxml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{message.showTime}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 自定义消息
+
+ 编号: {{message.payload.number}}
+ 商品: {{message.payload.goods}}
+ 金额: {{message.payload.price}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 发送
+
+
+
+
+
+
+
+
+
+ 图片
+
+
+
+ 视频
+
+
+
+ 自定义消息
+
+
+
+
+
+
+
diff --git a/pages/chat/groupChat/groupChat.wxss b/pages/chat/groupChat/groupChat.wxss
new file mode 100644
index 0000000..63a9751
--- /dev/null
+++ b/pages/chat/groupChat/groupChat.wxss
@@ -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;
+}
\ No newline at end of file
diff --git a/pages/chat/groupMember/groupMember.js b/pages/chat/groupMember/groupMember.js
new file mode 100644
index 0000000..c6165a6
--- /dev/null
+++ b/pages/chat/groupMember/groupMember.js
@@ -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,
+ });
+ },
+})
diff --git a/pages/chat/groupMember/groupMember.json b/pages/chat/groupMember/groupMember.json
new file mode 100644
index 0000000..e6364ab
--- /dev/null
+++ b/pages/chat/groupMember/groupMember.json
@@ -0,0 +1,3 @@
+{
+ "navigationBarTitleText": ""
+}
\ No newline at end of file
diff --git a/pages/chat/groupMember/groupMember.wxml b/pages/chat/groupMember/groupMember.wxml
new file mode 100644
index 0000000..a968cc7
--- /dev/null
+++ b/pages/chat/groupMember/groupMember.wxml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/chat/groupMember/groupMember.wxss b/pages/chat/groupMember/groupMember.wxss
new file mode 100644
index 0000000..4997714
--- /dev/null
+++ b/pages/chat/groupMember/groupMember.wxss
@@ -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;
+}
\ No newline at end of file
diff --git a/pages/chat/privateChat/privateChat.js b/pages/chat/privateChat/privateChat.js
new file mode 100644
index 0000000..d2aa719
--- /dev/null
+++ b/pages/chat/privateChat/privateChat.js
@@ -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
+ })
+ }
+})
diff --git a/pages/chat/privateChat/privateChat.json b/pages/chat/privateChat/privateChat.json
new file mode 100644
index 0000000..345369c
--- /dev/null
+++ b/pages/chat/privateChat/privateChat.json
@@ -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"
+ }
+}
diff --git a/pages/chat/privateChat/privateChat.wxml b/pages/chat/privateChat/privateChat.wxml
new file mode 100644
index 0000000..a9c4331
--- /dev/null
+++ b/pages/chat/privateChat/privateChat.wxml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{message.showTime}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 自定义消息
+
+ 编号: {{message.payload.number}}
+ 商品: {{message.payload.goods}}
+ 金额: {{message.payload.price}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 发送
+
+
+
+
+
+
+
+
+
+ 图片
+
+
+
+ 视频
+
+
+
+ 自定义消息
+
+
+
+
+
+
+
diff --git a/pages/chat/privateChat/privateChat.wxss b/pages/chat/privateChat/privateChat.wxss
new file mode 100644
index 0000000..9e0f4f4
--- /dev/null
+++ b/pages/chat/privateChat/privateChat.wxss
@@ -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;
+}
\ No newline at end of file
diff --git a/pages/contacts/contacts.js b/pages/contacts/contacts.js
new file mode 100644
index 0000000..f2f244f
--- /dev/null
+++ b/pages/contacts/contacts.js
@@ -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
+ });
+ }
+})
\ No newline at end of file
diff --git a/pages/contacts/contacts.json b/pages/contacts/contacts.json
new file mode 100644
index 0000000..e53ee87
--- /dev/null
+++ b/pages/contacts/contacts.json
@@ -0,0 +1,3 @@
+{
+ "navigationBarTitleText": "联系人"
+}
\ No newline at end of file
diff --git a/pages/contacts/contacts.wxml b/pages/contacts/contacts.wxml
new file mode 100644
index 0000000..3fbb15a
--- /dev/null
+++ b/pages/contacts/contacts.wxml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+ {{group.name}}
+
+
+
+ 联系人
+
+
+
+
+
+
+ {{friend.name}}
+
+
+
+
+
diff --git a/pages/contacts/contacts.wxss b/pages/contacts/contacts.wxss
new file mode 100644
index 0000000..7501743
--- /dev/null
+++ b/pages/contacts/contacts.wxss
@@ -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;
+}
\ No newline at end of file
diff --git a/pages/conversations/conversations.js b/pages/conversations/conversations.js
new file mode 100644
index 0000000..c06bd81
--- /dev/null
+++ b/pages/conversations/conversations.js
@@ -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
+ })
+ },
+})
\ No newline at end of file
diff --git a/pages/conversations/conversations.json b/pages/conversations/conversations.json
new file mode 100644
index 0000000..923f2a0
--- /dev/null
+++ b/pages/conversations/conversations.json
@@ -0,0 +1,3 @@
+{
+ "navigationBarTitleText": "会话列表"
+}
diff --git a/pages/conversations/conversations.wxml b/pages/conversations/conversations.wxml
new file mode 100644
index 0000000..8327abf
--- /dev/null
+++ b/pages/conversations/conversations.wxml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+ {{conversation.unread}}
+
+
+
+ {{conversation.data.name}}
+ {{conversation.lastMessage.date}}
+
+
+
+ {{conversation.lastMessage.payload.text}}
+ [视频消息]
+ [语音消息]
+ [图片消息]
+ [文件消息]
+ [自定义消息:订单]
+ [[未识别内容]]
+
+
+
+
+
+
+
+ 当前没有会话为空
+
+
+
+
+ {{action.conversation.top ? '取消置顶' : '置顶聊天'}}
+ 删除聊天
+
+
+
+ {{action.toastMessage}}
+
+
+
\ No newline at end of file
diff --git a/pages/conversations/conversations.wxss b/pages/conversations/conversations.wxss
new file mode 100644
index 0000000..754d2c5
--- /dev/null
+++ b/pages/conversations/conversations.wxss
@@ -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;
+}
\ No newline at end of file
diff --git a/pages/login/login.js b/pages/login/login.js
new file mode 100644
index 0000000..290c471
--- /dev/null
+++ b/pages/login/login.js
@@ -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
+ });
+ }
+})
\ No newline at end of file
diff --git a/pages/login/login.json b/pages/login/login.json
new file mode 100644
index 0000000..6101173
--- /dev/null
+++ b/pages/login/login.json
@@ -0,0 +1,3 @@
+{
+ "navigationBarTitleText": ""
+}
diff --git a/pages/login/login.wxml b/pages/login/login.wxml
new file mode 100644
index 0000000..34be976
--- /dev/null
+++ b/pages/login/login.wxml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/pages/login/login.wxss b/pages/login/login.wxss
new file mode 100644
index 0000000..84c9b51
--- /dev/null
+++ b/pages/login/login.wxss
@@ -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;
+}
diff --git a/pages/mine/mine.js b/pages/mine/mine.js
new file mode 100644
index 0000000..2084455
--- /dev/null
+++ b/pages/mine/mine.js
@@ -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'
+ })
+ })
+ }
+})
\ No newline at end of file
diff --git a/pages/mine/mine.json b/pages/mine/mine.json
new file mode 100644
index 0000000..1da2e19
--- /dev/null
+++ b/pages/mine/mine.json
@@ -0,0 +1,3 @@
+{
+ "navigationBarTitleText": "我的"
+}
diff --git a/pages/mine/mine.wxml b/pages/mine/mine.wxml
new file mode 100644
index 0000000..a8938ed
--- /dev/null
+++ b/pages/mine/mine.wxml
@@ -0,0 +1,11 @@
+
+
+
+
+ {{currentUser.name}}
+
+
+ 欢迎体验GoEasyIM
+ 注销
+
+
diff --git a/pages/mine/mine.wxss b/pages/mine/mine.wxss
new file mode 100644
index 0000000..e76b48c
--- /dev/null
+++ b/pages/mine/mine.wxss
@@ -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;
+}
\ No newline at end of file
diff --git a/static/images/Arrow-Left.png b/static/images/Arrow-Left.png
new file mode 100644
index 0000000000000000000000000000000000000000..66a11c525faff45f820fadbb1eca92411607546d
GIT binary patch
literal 222
zcmeAS@N?(olHy`uVBq!ia0vp^VnD3M!3HD`oXegDq&N#aB8!3Isvykh8Km+7D9BhG
z(8O;wT5*x*YCPr>QV9aX1v-^XF^ZA6eEm@q?;x5FRFEm@^
z(rM>izfd_Vn~V3}iw_^g*4?~xf9<2%xFemy>i1;%`n@dnocd6~prTv#wczTfY@qcF
Mp00i_>zopr0E_!g2LJ#7
literal 0
HcmV?d00001
diff --git a/static/images/Avatar-1.png b/static/images/Avatar-1.png
new file mode 100644
index 0000000000000000000000000000000000000000..fa57b53163afaf6a55196dfa7441bfaa8b0e6f75
GIT binary patch
literal 3609
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
literal 0
HcmV?d00001
diff --git a/static/images/Avatar-3.png b/static/images/Avatar-3.png
new file mode 100644
index 0000000000000000000000000000000000000000..eeaed5270f85445e6f617cf5bcd5b6ea3d8ec2b0
GIT binary patch
literal 3485
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
LNkvXXu0mjf$~6?8
literal 0
HcmV?d00001
diff --git a/static/images/Avatar-4.png b/static/images/Avatar-4.png
new file mode 100644
index 0000000000000000000000000000000000000000..df26452a1f88223b485ef986e482735562ea58c6
GIT binary patch
literal 3546
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
literal 0
HcmV?d00001
diff --git a/static/images/action.png b/static/images/action.png
new file mode 100644
index 0000000000000000000000000000000000000000..9398ca11a6fe7aac27d2de75013dda66aad7c8b9
GIT binary patch
literal 203
zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|&H|6fVg?3oVGw3ym^DWND9BhG
z+_zx<33i%`R&)ApW91kJ!Y|V_J00;MxQd!E(T9mKbLh*2~7aZVnU?=
literal 0
HcmV?d00001
diff --git a/static/images/audioImage/play.gif b/static/images/audioImage/play.gif
new file mode 100644
index 0000000000000000000000000000000000000000..e367c875dbc4b50314e68f28fe1ea9fd922ebfe0
GIT binary patch
literal 2321
zcmZ?wbhEHb6k-r!`2LV#6pV(zU=M-+3>OqNFYB3I);GIm>wL}D<)&BQP4B?l5%G5-
z;vSY%Ju0bu($w~}sqOiMDK92Wc{Ok0t9grF&s+F*^}4sK*MHc#=flpuA9n5euxsz<
z<0n5KKlSbE^`DQQ{Q3Cl|NsAr|GE8KLxPgcm4OQwQF;uATb0ZG5_Z=8JJY)Q
zJO8}{^?zBG{TBCEa&3I_@1?MW-+lWsrn5g^o9it()Bs5=WLk}Gq`H2EaXyuB)DNQ>ovufdP#5JtT{FRrr)Z$EX5ye9sY`Fyy|xe
zbv*L#SxLYe^>f=hY74A3d2Cy{N+!BE+pOH}lJk*c+81OO|eZ^kjCXnXrO>`@Wg`p5%!#tv@i|3g{UIPgg&ebxsLQ083$Rg8%>k
literal 0
HcmV?d00001
diff --git a/static/images/chat-active.png b/static/images/chat-active.png
new file mode 100644
index 0000000000000000000000000000000000000000..06c019b4c32eebf6fc4ea6f5d09025c65e8ee729
GIT binary patch
literal 573
zcmV-D0>b@?P)^V;_YsHs^Gp&+&pCa^1j9rN$$%(0}Lpa8i;Z^X?$4NC^_DG8C8ajzdX6pd6G8ThU`O5HmAwN|ex;WY8UA`vzD>
z;{z42Ox}?{H^Nq4G%A8R!CCldh})=2gK!?2`vY6FOrrTGDuj7@=$C{*L}uH5Ra4oc
z{R4k&;GYl>X%woOn=t3;7osATbS7X`IjchkTZYilFfc?_5RZb>NRDvk{$W$tMGr|L
z-j|M0z6^0cwbro9*J@>%i7TVgSSg}SncXf*fmG$C?y3eU;X7iK5D_f_Rp_2qHbyZK
zuR->jqOIbnC|gMqnn)XvRB!y8$=k8%DCp+-%97wMK4%2HEH|+v<1T?K5$LZ00000
LNkvXXu0mjf1iJ9;
literal 0
HcmV?d00001
diff --git a/static/images/chat.png b/static/images/chat.png
new file mode 100644
index 0000000000000000000000000000000000000000..75884e06eb06fe0827d0f2a88cfeee95cbe321ae
GIT binary patch
literal 802
zcmV+-1Ks?IP)|UM<4|kKSwBfczm>N+7eq4X?JBGhYu5nD_ST%R8V9A9NwfPL6-75s5w-@JOYT<@6I3!P
z*i!}BCR;seZ6ul*#_YuT$BN4#zpB{as0|5yTirk09c*KAuyH3yO
zE@o5vln#hiM?P~#u71;Ln2nJ|PEI04+$&9YfzWDfyfxm1V+Q>vh=?qgktIwzFv1(#
z`{n)25YfJJnMgXd%7g{g4zG>4S|?oN;X>0Liw=aE;j3$Sfl@A!FtyP3wYZgvxL`uu
zMjMIn?Fyz$#)P=tQpt$znGwuP)x4;C27&Cr;La+h|1u5oWYE{qCD_-E!V28nxJpN1*j
z)UW*-6zzP|h9KRj)=^vscZsEk=Wxfz$+o3p?7m4tX|anH#vDf=8z*C8tdS|Oxwx9z
zH}nEB7U4S6Q*@?Z{=j3tUIyPQ7ab|#G5(Y|ixtKh(x`83%o*=efK;GXAXcDO039S8RK^2r2j=47#eKdYG702k
z6T(3!zZqs(c)Rn@%liT-DEK2Fq{Nohh5$SzL1onFYM>`ejfb!y#zJf<%P98lfT+24
z35px7KYEr@E@VWQ8Y1ov!bErw={EoW02^Q=gkQ%6FZBeohAnGt*rpIf?3&qukzFPs
zUU2dv!Hn|4K7S!h15$xWuMn~dNePu913QpAI8R|)9&sj#R*a!rpT1gYgbewUA_&Rx
zeOHlYh=H18RM4Et@w=hR6+dMKGGG)VL|;8KJ2J8h!??sdm6{E4MGdK6pNud4KXEEc
zu0hj?69MlT2P;2qhc6vOT}m24yiszAX16GCf9*bvBmX{C_l@*Qh&R$=>=Cc~@)8lS
z!r*LarD1BYPQ{Z~_0LKU>3YiP|X?6`r
z3u2$NzV{DI>pe)YM_!yrA)~`uiwL}SE@5-#USdK0Btr~}sCJ3)3UbDq;7%sbomdfTOK=(=#Ep{6aJOYWQ`gLkr4UOwtd@qb9aPd_9jGHjr89s}FW)8PhSz0heJCjS
aFMI=j>aFj)ny)|r0000P);RyuGh2pvS9b`08_D=m&t
zcq|xyB*U4}tfYH)b#(SV7T9Bte*_LTM5UrUH-{4q6gfGJrnni6`~WY6H4)EK#ry%T
zI6*B~r2Fd;ognZ-Y>B+BW8naI9LmrLwTnxBe2fSll{q1k7VTx)kFX`I6scBK6K<@=
z*mZE!@B8DKtzK89NaK!#N-oeI4E!#(fNSY>9*-LBGzoz`M-EQqK7#Rk4I&bp97V_;mJVhzpZkk8aGHB%Xk$c2AV;-%I?hU8bxdY>#T8{
z)gPe!eR7i!svN5p|MF~^KNSv#1OMaf*=B13b9b`F(=$ZwXD_&2<7JmlV(QP2zyg(;
z`YDQNrmhLhIaH68{CQW>`*z@ez!tEJ5eJ=k(72Kcn>Kl@BaEL5x!1;T8(782h()Se
zQwSbG
z758s79vHX>xMWPc&6+spd-~a$h{Og}xsO#jjB7=JTv9U_ctN(}3QQAh*{p9(B#sS}
z!p;;Mby=BPD9m?lDjTLN^;#k#zmr3Q0VxX=&0dT(rr|@tCijryPaI
zsKet!lf#$vJ8k$p6I(+-nwXcG9Ps0(>1~kS;xe10`=+M%^c2_W15>sKiK;{MLko$R
znjrgHGGC9+ACm`kR~>w+b=9P54JSn$$(g0Y5B9JIb3|%2b
zW)f9u%v6#S91C3=wnV5#ymiYe=%)BoQ;KQ*Rqmf2@ov=*JqK&5Vuo@AWLfvH14!3i
z*DwWHMdV!SVaKrPL38O#`92p8p>#Bt0WW0{a@`*G*yDe}Zw3n-NieSWLjV8(07*qo
IM6N<$f^oNRU;qFB
literal 0
HcmV?d00001
diff --git a/static/images/dingdan.png b/static/images/dingdan.png
new file mode 100644
index 0000000000000000000000000000000000000000..c56da6e8498442bbaf8ded402fcf10065f73dae1
GIT binary patch
literal 840
zcmV-O1GoH%P)PIlF(4)h
zVMzF`MM9Y*@&d@qnpg@gEN4x8QX@gXZ)047Ch9_sDGNu=HH}YDqM<3XCyFzCLkG-!NPs-9S+o!dF&WG(C+os%Ll?sB#9pe$Fj`BQez%1OI{{Dl9K@G3oJu{nwfR1N9jxoLviWS;Ip7z*4Si{+tcbtG`&ONzWnJA$
z?3p$Gad#ALz5TWxH{ZR&$%pB!u*1Xjl>TDLF~1B^-P!4X45m$=F-5p~av`14``!r|=5
z*9q^{8!$p5%-!T-eRdv)HR|Yu3;f)TwRUcITUn@k*CjN8>w0P$b@juF?N%S_)zP{V
zF6*r>s6oWUy4~~)8_p&xK@J56N+pFLaFELo-mq
zi;R1D-a6-_qY%n0XIUV4s|1^#uO_0}i&G?!F0IfWLXeediz_m!n$JepQ-GlHgy
zVlJ4;C3eoSy+$Er(9FoSuf3wS!kS>_^@KVP)sD&n{ulKVRgq3=U-rufP*P2x08bE!
z5#vTs3kpiH3Qy{$&e_Zm-6D~w#Z(dR!B!2g|7p|tUgd%%K`hHFJb#ejHh%#k4H}d-
S_dzKD0000X;;DS^!9x7P50N0u*ZxsCscS^M}U_MSQb}N{m2l=--;P-y6x80zP-2uHuzMf
z94+Ma1diJZnETtnlwEjZ-xsiJ1(TER0Jv$__oeRTIqJ{y!wbvY5E?7T8gVh0_`SUh
z^#d~-c?|`q-@R~7YW*JXSyBWYAm>GR66XtIf
z)NX9ebS{<-(3{M@18d^B`8#F@nEH;)8cUdAw~{=S+{tla5OLml-|RmEkUY!5X9*+q
z^~bYYVOCK>ltXysBBf%4X-Uk!==3>y;~A?{WdZ+S2@b)3Hi|_G!{L`_iBk(@%%pf|lgh=D
znCx1gLNOp@cDb{3UrSFx^r`UVTXs`ogAcRUPJ8I25?jsz3b`M7LH#PPRT%uh4mOr`42J?2CUT
zu@!bYgHU}cV2w!_M@=C*mO&A%IN!))O5!6p$iD7fYFOpY?+VButN4
znoHTknKAZAd;QVWNV_HD$jv(on;>DVm8U|Xijwa*1)xNF?emnt?tslk0n8q~2?R~!he~>KJVqI(S00000NkvXXu0mjf0enEw
literal 0
HcmV?d00001
diff --git a/static/images/failed.png b/static/images/failed.png
new file mode 100644
index 0000000000000000000000000000000000000000..09fc972e802f70c5ffa04b2db6e93181d5a6e438
GIT binary patch
literal 292
zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQa_5nU2u0Z-tgw^{<5PBD7{Vvk_
zO}Opr5CM_B>6|NsC0)RglfFZg}3`}8P=Qo@`U%z5r#Yvz{o2QFoh{WaG3Acrs6?j}GH!Rqje)VQK8lW`^|#u1rx{b>w4%{odYQGu@;25|eZGth4+ewJwr$_R4cTlqFrUvK_}h1s(w0-eOUPcq`0=CH>oFigD2!$N
zg0dGF5K0S$!{P7;&;tfU(oQA)8uWnyfwYsb@^0)B2G5jsY+4$^;2F|>6h;_$l-5?5
z4>?XD3_M6{D;$kRl}Kxy;dDAJ6UxAawAO1llgU6V7Ly5OAStc2FlM<>C_pNek_l%Z
zAsqqDLFs%x54l_pcDo&%PA5?~zk(nv=_b*iA2ta1j1p-ululPjBwB~!m7au
zDjtu^ZZ+iGtV<^n38>fWXVL3!%b#1pbUHmh!@31G5k|>ov-#g`1qTb3b2vkC`tvHF!fqf1(~p_u@Ju)$Abz|;nivd)GU`v
zpoFwbkH=#;91cJQW)|K89qS)H2v_oTAuEJ+l}`@6KQykHLa`?a{1
z8>iWu6xnHBK#
z8dr%F%NX3=$BhC-a)pveID1p%z}YiUlVz;GP6j3Nl_CILwSVHdXBmDv^1>F40#)P{
zndo4|=;}QK<`nnW_CcKpEIZW%HJZHeA3igKG~Xnm4p>4&`fRFvIs=*RgX~zJ@9xcu
zQLGJAT=1k^`ZcB3uiHC4U<{7H{zXk?pm(x_9&E8O_yXqo*qe{rIlTY?002ovPDHLk
FV1oGy#f1O>
literal 0
HcmV?d00001
diff --git a/static/images/goeasy.jpeg b/static/images/goeasy.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..7a9f055931b5a99589fba17d4b4a74ac7d7d0e13
GIT binary patch
literal 24003
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
HUi|+6rtN@y
literal 0
HcmV?d00001
diff --git a/static/images/green-dot.png b/static/images/green-dot.png
new file mode 100644
index 0000000000000000000000000000000000000000..3b847072d89912034c958904340fb8785e4b647e
GIT binary patch
literal 373
zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xa&H|6fVg?3oVGw3ym^DWND9BhG
zB(;C4Wv89LWjS+iyP@Zt
z;?@-c*8i?d$`E8sQ`gEk(poQ`^{aY?%Bstk=cJ2O%FR5o(~58AzH*5Zye29C)+p<6
zF5Q=Fl(4*LM~2fbAxHjLEsn2!hu14|Y`s~@xLUxfsqoI%e&uIsD>r`Qh`KAY)y(V=
zo6Vu0|MsqPe4J)u(^NfUhW;xlpI^d<)(N~h5E*|a(9-m;P>w)&Nio;rX`#UzB;3!|
zcx=|`cv;5pT>L`9ZW@D}x8lp>>M!zP>k_JEas)4K6N}Dp5BbP^;{82FpKXkXlg_to
Q1BMcVr>mdKI;Vst0LhPx&Hw-a
literal 0
HcmV?d00001
diff --git a/static/images/group-icon.png b/static/images/group-icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..5d1bb153ff07d6303946ab7bcbeb7ddf56c7f5ce
GIT binary patch
literal 502
zcmVr~a1_vO(FP*`VqMbb{Un+y=c3jt$xkxDDt{02^={q(Qt0V-X-wwLd(`N^l%M
zJ9YxmjvfCAV~n;^SZPd$(#pk(!g_-5qtpEcX=Zv
z*rAzOzhfLL$0MyEcA37?G+u-~u|~X6DA1)18bx_*-cieo(0@X&X$|loxX#uhsEn;C
z=oM?WGi^cJnWl$3fku+T_6&VF=l3u}s_pR1RBZz(8a9$c}xcH_JdfgseAP2
s{zyL5B!ad9)%KX*?DR@IcKii?0P_(_wd!UjNB{r;07*qoM6N<$f`9PQGynhq
literal 0
HcmV?d00001
diff --git a/static/images/group.png b/static/images/group.png
new file mode 100644
index 0000000000000000000000000000000000000000..90181ab5b8337425b11939d00e7b63bf3d191aa5
GIT binary patch
literal 1778
zcmVC0i&BDn8-#7
zB)Sn=5{!w6B5{GT=ycO!6XY3a18GBtwy{#_T+eskyvg+aznQl5OQzGg@80{)x#v6Q
zo_nsKX+T#z9wmxw(@4}yjp8J!fk<$hj23j$@ez{JS(^NnNJ}U6+d7p9K}Et4y0S?K
z+R8suR2zF~xcp-{+gSK9sTovf9tpnfN=oy2l)Y=tK9CUl`=mQ3Au<~;UQcgA|^}bGP
zyG;SZVZr%lLK`n+*cwTzW7{ai&hyJ06ds7?BHyDqj_&$uDvc0zs*>`t#!~ygj--
zIQb<#>lysWQ%I0@)K7T4*hzQ*0xj6^
zBKaY*uFg?-U;$fY$==<5Q?QKl@=-}CfYm1{0-|BU!5>1eAP-?
z{n>G~n$|u8AAd@ZuU=Dq*Iv1lSE>+bD_>R(<2`>Wir8MpZRYAPVy^oE*1-y6jMIE*_n}V#a0(1@ntF86o?T3ijO71!
z;q=Mh<^+uVc+Gdb>G1O{n;zzT
z5N`E>sPBoa5Lhv6P}Y^J-qEPO!CyZI9RAMmqeWXb+vcv`;d=nHA}Z1~3F+4hlP}>N
zQtgs1j@tXmW|hz(&ae6QoMRuX{=N4PtIaNP<0r>Hpb!$fhhLlylpvPvuU>|{BI4Eo
z=|Ihy(^U29%cNCQxK31=s53US{sF^M$$Fb$*n#Df35tJp%5}ehV}p{5*{WIY`%k)?
zyueN@Gj0#D>BNqF6ev7au1hE-fMJJlYx)i>guZ^jJ7Y&a%x`=y(BT7VrzbG;3h%)R
z5DL))6f%>!NFw1l9{@k;dvkl>hyxj1QwvFY*}mw~mpB}4v_69ni{J8gDkNricJ86k
z8^0-PVAYoGf16)=z(%*onp&!;tyQ7+$(_4Ok&&xED=Oic7c$@fKu-sU9OS3Vf^#9R
zZ;G?lhY$vi=0+c^pc(eCy35}?;H$6+99-^wa5(RzSYI#Q-<>i8fpQZBue
znJW`7sBevdQ%u+D;^#vOEPUIMD^pLLqY&WqI>VT+z-NWhf;4a{r(Ra)5Ebb^K8Z
zB=vO0)F=$bdwQurIjGEhS&?Y%`L**}gLX=Ruo3&zgIfYgW#e!W95Y0B-ObgIu$Q
zy6842rM1kQHH69tSn$@sBPb+twN&`V9-20YTq^T#n|W~&cqA2xoFXYfT-g>yZhTT+
zmn1H8R@EL>a1%u8tk8w{A9JMD
U%k)AxtpET307*qoM6N<$f@siDH~;_u
literal 0
HcmV?d00001
diff --git a/static/images/im.gif b/static/images/im.gif
new file mode 100644
index 0000000000000000000000000000000000000000..db65d9ac46c55779678fec98f71bd2760397ec48
GIT binary patch
literal 313497
zcmd42XHZjL^!FPAgr0<=h=2rCKm`q`NR`lwsECSm0@9>|A|SnmfPg_ndX-*7?+|+E
zK~Oq`-mCPIJpBH1=g!=jd+&?q)$^To&ffd%J?FeQYwt5>efCmOQx=ynai)j|x&Z*Z
zWQG??Z(b|NX)1}{hf`7jE-#%>Q88YYLYD{NzhT*zB?JOtW@hH$;o;@weemFcsHo`E
zr%wR@Ej~U|Edaojk53DrrS%qoKp4q4YHI4>;Na-!=!p#X0)
z`Al+6`B0`NmzE3g59ABJJa_N~_iJS>n+Err=3m-cAOL{k{($~qzJ-+WV*T~4ogvc9Ky&?n12DGivb1y0t#j`Lg#XRY}JRZ
z)h8b`1*{Ze7fLf1>I+s%GuP`gSLzFn@(Tz-y+d(bgFpU`<=_W%CZ?+Klcj%$@>a*H
zmj6`kFXm5m%xA9kgdVj9{Obrk=m{lv2A+3>l6z33p3sA?nDe37h1Rm2*0S^Q{G&f*
z^YfMDxlGbx9%;3lyjzRI;d*;}CohFx9P2ut969eN_xF?6I>_XHGI?r=ywHZ9?;P9w
zjX&wY?@TtIPfjg%?9GoXuT7pDO_G<%9Y=Fr`wL^FT|Al8PQK_PUyLs`G7<|JJ=(UE{$~Q
z{In#%1s#_vHnll`mi4Xy9-G!0485tIs9KWV{`J~J>q&gcug-5Ug-|ZF(u}Ss!Pj{P
zU8R{lG4S`@iE3q8eF@?p*CxBlvSmH*^V2D2%jYy9Ip(+-m301}k3v=e<$i(j
zETU-Dd)mdXTSFUGf&&p*S{KnW$NZ}=l}YLMf85h#rlte{uBE103AMOD>D8*&Ak0CY
zCxX&9k2f4m6@$3%9X_i`<)v_cXfs`}>&qM8jG*;=sQO6r7gdatraH!6zGj_kK?a)E
z;s*e5733Q@P9c3x?n1pQqoHQZ<+TQUuWU-H$!r=8P48GnAw#v$?Xbe3KuFyw31EWXTv*^%jZY
zrQiZgAsP*@aG4O^V7@uA(^G|0tvyz>}CEkXzp@jQYPLg(}?7D$D
zIs(K6T(ala7KHNZpD5r>fVp)1)a%$9{zU#{MRsm^>WA`I9(0KR;u)S=fqX-sy5$5g
z$fS-@lEmXVGQKYJX4ZT8ngUUvu-05+%`6^R-EGf760
zKwQ+%xzp1S+!2D!k|B$=_X=;Ptw1FC5nXapz80<&kH6juyqPY2H=bRA+3KpF?3hKR
zU6s36zHrq%I79Ec7WKTGrV%)h%|
zGr}t0I{c#wz5NF!7@s!0WwrT@UM7GOKp~XnWW;^1$_dZ&abp*M#dCuO5^s%SsCaP}7X4k0N-sEE1i+Z|Z*{2+WS4a>fhAbKZDr8SGm86|L1o
z@kQv!jTk_<#RiQbNsDcwongq~Uym!2@?P=gb6;O&A^t=tdVj`}5(gepQ^c#(jj=U)
zvF65!zcSk~F0nj`yTR1g{=X*4yzfPSWko{n>k7!qX|Wh!nJqQ70GOYQe$&@H)Y
z>vWyY&_cp8x!L|~vwFGcY^(lg>ueiG^Wc1^josmV7cWqJzSsL?`}`k4<-x`N@H>Z#
z-IIc{2+2v;?TaH~@B`BEf=97T^GJ5_&>`;dM}wqQ-h(EMlbir=or4)!GU?6-ND}#+
zAysX|GX0cp$CsBEnaqgBfvhvU+Pae|1$3C{KurE@y{QVrkx?#eKwvC00y)GXK+R#4BfU?sL8*M>t&{6xkZ72@~S8Dbyn)MFME~n4gk_Y
z%vtQby`%7y>WP(Bn}udI7=WSqZkl-OcDAM3eQS#+SBPN&>Rq?K8a#uQ42S)-muP!a
z>J&||F>e%Q1=Kq!J8)70*lq&5Yr{leaM_^Ykl%&Iz#;c!HauWYtCiUa1!u+g0O0<3
zKf%(s;Z(Sro3xR(xm)prb?I`+CSu$cSrIo$rCCeP3Q(mtfS`OJ;AwO0bL)7mlpRH*
zYwt_JlbFF+3PHix40NX}f4Te6rx5PmhzLzmGk$IayL4sYPvjI`rim
z9d(a1o*oqYG8G_co{hF#Kg6P+7nrj5jCM#KmSDmQ%mki|b?Y6L#`YChJn0$h|8iKC
z%2a5j@@)Kf{9$>{^Fr%)J>!3C4=b?Yq(WQEXA|S&hn3ZRh4!vJ6Vs=MRXC<1$KYp^
zbJvfm@z0B#V|pf+B#&wc;YFXapG~dl9o3HZ6@9MincDhtR7YegcI$jLy%&E}zw*5J
z%Sg}kVeL`FPI$5B^0S%K@uSA$zGCmAo*B~VQ4;`+^`+4u0y&Ryv@fv!?7c*gM-k5+
z8XI^ti;Nw8f8_;+f>Eq@mT4KlSc>daMfnA?BpkO1-WL%v3WnTCbz|6uqhVUMrUuf1
zZI6`wA5E)+1wI{jB2a$9ihN-(PWRSgq!ZNJHs~6A8$^YlGKk~%H(oCHPW^^X)(Fo9
z-rx+p0m%8gVeq%xuGL*2zfw}9i*Gmz9?+u`arfaDsKreWj$6p0hrykbwW1Hlx~N{2
zC2_LQit8U-`*LGHhPA=+Hd&(2fCQ|E&Mn_lap6#jptvR$8U7+Foke|RCrS3ZC3qRu
zdH3^v97V&O`!koHi&x6A^ITe=2psq)vF$$~@)44>ad^aygRQ%LCXZxKI-`%Bgs!#(
zgB2+7<&9Q1{3oehA5rnl0uH|g6-t>pjE?$|MEJeKg4Eb=aaT?6RQqt*xtU(azlT>T
z?}?ZM|M=268G#O5EU`iFD75^u3aK&yY-zhzvp!tGG9kmErk`|Z5`D#9X7`!dI4s=A
zPz7R~U?{8o3D^34*Mm1m=LPoCRMX)8xJ88j^4|$hi5HUqOZ=mE1@hUwB3RSbQ
zXhDu00NrW%mbroPV^3U}SZe>p4+m4KT;QdZc&w}QMRtq6^ddqy_xknYuc160l?*SK1b
zM{iV82}_ca=wX1kP0HMho@VTYZ3Z9x^~SO-34>FAXvzh2RVG6!(JZ*Q7PdTEsfU3K
z_%%tcn_P9EO8bs9}V0Sh+{Ok>-G>4gj25Dp~DX$7Z`%+y%PK-za=qkpM
zdx?7Kr+Zbf3nQ=3=!g~1@%vr@sQ-?mU5DF`Y?WKpCbl471Abox3bWsq&?AD2kPej5
z&CJ1)k;M&qZ`7px^OxJB&|g(1WEJS)J5SjdsNgmGeF5l8T1w(FWUkZ1Rv_Tz(fcQY
zfi77Aq#b`%SExp{iQx)F!OF~`6RIL$oy%(xUB6rXzJadx+l;`TCyc6dSehv2u5e36iBiyJ~;xs6^8R*~_9lFm9*<5l6mQsJ>OmaIp1=D|8V~uMYpL
zg8CeL{9Xd~kxnG2SRDG7meLXvOz#vF6KqEL%8Hx+1IZEctJ?B)*4GOfG@RfMY-W_U
zr27nTC>TUL#QJNZT?%NJTOFN4P@hSda6|_+|B4?BZ_;}dn840FE&eqe+vKLPXT5o_ejVc
zsP%|R84>bQJPO=p@-Rom+RcZ4Hu6Bwg5J-qBgc=a*$r+yE;r-kHc?vy>5h>Njt`T
zqKVN{jiqh=IdS~e8xM8qvJ-KSVi-i3eXw70v%rj*Y~ek=lJ20((cxwS-{H_7Uxp!y
zP_vq!!LfIsK#l}zCz_7jL{HV=9tmgzHl9h&k6SDT$QvQ%Mu$3%o{;b%tU?AjB3slF
zW^>>C{$L%K%EY1#&LcXXNJwsogYL0=ACH;nBkg%TA)Tru=)0ADl>4iukne4|$#PjA#4Kq7bR)-VLgbdrec_lv7-SJk
zqkQQ>SsyBJ;MdyVw@*^2(jwyplBcev@^O4;7>@wv{e1N{MOreAsB4)Q91yl-&KCFK
zP?tN*f|?y4ue=lmkW2_ygGPUdc|V>I{L@~#Hk~BUWo6TyRrUm`47Dgm#=GWN%Alct
z=*+C{BqVet_;#fr#oZ+6Ump`Q1mh^Nnn9VVmdhbfUmK;wX
zMAMBi3Js#X3q4^E^odWk`~%5Kbu#!#X$}C3oU(c#D3$s1*hBLyC9U7km`Nagv1@)S
zu4Z@M1?><1a{rPmtD8Vm5}GuctN#`}1`QRf@_KWZc^~P$p2swx&T@=l%1HIXUuQnS
zGPNT=#$lL&oNyS;gX;vE9~9bP|jQ|TqHhn75jiU>E7KEK0nBF7^pmz{*|u{
z^@kGno(M_0QbCc+#VGIA#3MciO5r$C>0JghX(-eE1QzI*k4c!~hY4kpb!E~MWlzt_
z5S-=mQss*J<;ouAstM)lb>$ip`w=p7WYxybamT%YHlB-)ZujBKpV@hMBhXRK(>tJcso&dmABonB)uD!>$
zD?g~Gr>^myA?}R1mH6*
zx6x*y*APggYt%fQ6jT?Vl%fU@JersC!wRB
zq3uIhJNHJ*z(EJ;8gpm8eq*mqy{G{Y0PnD~ZN4Uilcob)IcK;z+5SW$tS7&f@>Odq
z1ArDl_tqfd9-_YE441xv|CY!erP1Z?Q57wO^z~#e4l^tm`UW36z=um;dA5Z9rZEL<~WuIsDRIl-v
z0raC(|AImPl4t))V*j#j<;GgPx`)oC7fRAr)nm&FA{(hgRQp*S%eMP`no6TtLQJPIm3g^^39jS6~oF+TKZ-$c{UX%Fe{STm^AAa<0Ns|Qb
zsox}-Ddj_8v&>Z$#MF4h)a1pe3XxrqFf}JLz2Ma^NMsk#oL*~~-UzQ0@SWb|p4pR`
z;X0gtA343BG;=CEeJFhOxM2oxgV;4SW3oE~^d{04jZ!_m0!k)=NyL?U;?qGQ)M)l9
zsgh-ymCbvWYkF2NZdSTv_Qun>TVOL-BP*ZL+})>h0!6HM8t35lv%<-&4{pqhB@-V;
zus-&lcaUPZfYERsG8uNw(}QOer{_6ism}n6YCplrnv`5w3k*)4D&Q&!Z&oRzMa$Z#
zj{wRU81&5z@S7VJ+NqHDMT@o`i(-+BR{iE>NU&zInW63yI%{ExNcTm1$#Qy$^dW!g
zar2Vr4YRaV$ZG24t(RXtHCK4y2+W6i+*pxbTYf~bf&rTuCxhoSp_5q1S0l?WMyp=-
z3#k#`r9xKZu7YKgtx}$@Mc!X4FtRkBUQNt_T)^nNy0a0aH5dNXhjOc_Tl3DupR|!{
zDp?DLMeAFeYxu3DRluUD@`fGVrux%`{KgG~j&;%5^@67ieK*XC0h>hhihA;zdNMc_
zxoLvh6iM6s)xX*A{h8QrGUMo}-p{mzVK4~Usw>(!eGy8>ydAPde+H+KI$60Oyf
zZuGCsI&SQ3F#)!Jenf7-0UI_KYdPOQ%>TWP6{q2Muf*xc}~-1Pcw{9vwTi-Qcm-lP77vEi^!)~p0iTfvvT9J
zN}sdpl(X8Vv-+8{M)Dbs=e$Msyv_K$!{;2Ia^Br^-aB*NPa>ZacrJd+UJM&w{PnpQ
zO}Q9vx|p1~m?mEkc}R1zqy=Nrk`HMmg|ya0+L$42kx4r|}7F56Aex9rt
zr?sg5Q`oREkmL1|M*L&D>9)An%P*2;yhv2n->hh+D}~=sihr}JmGwO7Y0&kzYj5)2
z78i)CnPm=LT199u^Tm8;_o}!$jcVPXu(69-`
zt$%1cUST)Wk)(fQKi%L(2Hh|?cARSu=TS*EIB{O;OOg$~VR-s!?N6a`XR_hh=dGy*
z9~vH`bGN<4z7&-dql+(xn^R4}JjNu?(}T^K&J<&^H|gSnOan(y(A>fzC?Se)Icjz@
ztQ-wbAY7hKAOkDU@SqK@!1QDRs{oOu5>aGUxmBXb`f}qw>_0w3!2qBF2*m@kDcAw*
zfJ|%VE1y}}zg)TQ#my($CyL_Y4duBNdFxKxJ$URRiP$GnC0sWP`GvFZKg_=i&lM6)
ze=M0HCY32JQ^6-#!FLD8BhbzxFu;4~5-dbVN2jN!XJ=;@6%|!hR#sJ2UE+o2=H`}`
zme$r*JRaZO-QCyMH#j(W8Ka+?np$69-`o4Y!sM5c^8Ys0A=`7kK6>4tG3hdi3Ne&qNUcXMUjCG2r=NQK><
z)@TOpf~B$#+1h`4|J;CNcuOt&&-3v`{m15R-6Px*8oVp=`@YzasKoFiso}@c!{V~T5{SR@
zYNP6}Mm1iKy?i_VR)50i{iLz^l-Y-AYlmqYr)gWK8HZ0ZjxIzOccQB&(ap=>-{03S
z*v}vB=N}dj7#bKD8iWc9Mn{B(hN96CmqMWZ+^jYHAxUr%h6BM^g1zUT#NT^;l(YM?(Xyv8gSubF8wnBd>F#a+KVW
zn>1FP)RCLlky|S`
z=kMRYqoX5ZqyK4qeEc$~IXO8oH8nXkGfnJRCy$Xg#>nKY=GLv2wjF%;E@5!*&)>bV
z@qgo!|E8w*r)P*Wvm5Ij8hZn*EcuTH!iic
zxpk=xGI@V)ZhwAZe_`QZd1YsN`*3aTaC4KqzOl2tbF{rfCU5NR?(XmH?f=_9JUBQ$
zJUlr$IX^!qlgXEhf-5bTDu&qP|NjNu=9|88{|DWiRU~QN(ep6`K;tL?;NMI@M45$p
z6zq5Of6(n^2#*|zOa0TrQ)oaDp**De0!8+`mTSr@Z8*5oS@!K5L+(Ul2=JP4DDAx6hb>{{Q(
zS6~-=5CNvsy|0UnJ0;`*0E*z^K5jgGB`o2Evt)MG2Y;*Hn$NS%l^-3r*v9u^W?+m6
z>fT7nj9fs0X_7RybZgnyNg?Hhb2VaJ%DGQ0gd)zl=-YczO4f_U?ESqJ_GGpP9z630
z9TLxL?>kGeW?F0(aaLKmOJK{Q
z)ujYcdfMg0PmQYDNp4HI%gLUHtIH|Apt~!nfmf|p($M^QE9qfkYb(Dn%6C^YerQ{-
zX2x3Nt!5>DUR%vhMcrM?$@po#mYb86x0Y8>xwe*1!gk!Xk)QvugQG;hbzU#B-VYdH
ztkZ6ZDyr)D9RN3h?m