Browse Source

!130 优化

Merge pull request !130 from blue/v_3.0.0
master
blue 1 year ago
committed by Gitee
parent
commit
677ab0fee5
No known key found for this signature in database GPG Key ID: 173E9B9CA92EEF8F
  1. 6
      im-platform/src/main/java/com/bx/implatform/vo/GroupVO.java
  2. 5
      im-platform/src/main/java/com/bx/implatform/vo/UserVO.java
  3. 18
      im-uniapp/App.vue
  4. 8
      im-uniapp/common/recorder-app.js
  5. 52
      im-uniapp/common/recorder-h5.js
  6. 12
      im-uniapp/components/chat-group-readed/chat-group-readed.vue
  7. 16
      im-uniapp/components/chat-item/chat-item.vue
  8. 25
      im-uniapp/components/chat-message-item/chat-message-item.vue
  9. 23
      im-uniapp/components/chat-record/chat-record.vue
  10. 272
      im-uniapp/pages/chat/chat-box.vue
  11. 27
      im-uniapp/pages/chat/chat.vue
  12. 35
      im-uniapp/store/chatStore.js
  13. 75
      im-web/src/components/chat/ChatBox.vue
  14. 2
      im-web/src/components/chat/ChatInput.vue
  15. 35
      im-web/src/store/chatStore.js
  16. 5
      im-web/src/view/Home.vue

6
im-platform/src/main/java/com/bx/implatform/vo/GroupVO.java

@ -50,5 +50,11 @@ public class GroupVO {
@Schema(description = "是否已退出") @Schema(description = "是否已退出")
private Boolean quit; private Boolean quit;
@Schema(description = "账号是否被封禁")
private Boolean isBanned;
@Schema(description = "被封禁原因")
private String reason;
} }

5
im-platform/src/main/java/com/bx/implatform/vo/UserVO.java

@ -43,4 +43,9 @@ public class UserVO {
@Schema(description = "是否在线") @Schema(description = "是否在线")
private Boolean online; private Boolean online;
@Schema(description = "账号是否被封禁")
private Boolean isBanned;
@Schema(description = "被封禁原因")
private String reason;
} }

18
im-uniapp/App.vue

@ -9,6 +9,7 @@ import UNI_APP from '@/.env.js'
export default { export default {
data() { data() {
return { return {
isInit: false, //
isExit: false, // 退 isExit: false, // 退
audioTip: null, audioTip: null,
reconnecting: false // reconnecting: false //
@ -21,6 +22,7 @@ export default {
this.loadStore().then(() => { this.loadStore().then(() => {
// websocket // websocket
this.initWebSocket(); this.initWebSocket();
this.isInit = true;
}).catch((e) => { }).catch((e) => {
console.log(e); console.log(e);
this.exit(); this.exit();
@ -169,7 +171,7 @@ export default {
// //
this.chatStore.openChat(chatInfo); this.chatStore.openChat(chatInfo);
// //
this.chatStore.insertMessage(msg); this.chatStore.insertMessage(msg, chatInfo);
// //
this.playAudioTip(); this.playAudioTip();
@ -192,6 +194,10 @@ export default {
} }
// //
if (msg.type == enums.MESSAGE_TYPE.RECEIPT) { if (msg.type == enums.MESSAGE_TYPE.RECEIPT) {
let chatInfo = {
type: 'GROUP',
targetId: msg.groupId
}
// //
let msgInfo = { let msgInfo = {
id: msg.id, id: msg.id,
@ -199,7 +205,7 @@ export default {
readedCount: msg.readedCount, readedCount: msg.readedCount,
receiptOk: msg.receiptOk receiptOk: msg.receiptOk
}; };
this.chatStore.updateMessage(msgInfo) this.chatStore.updateMessage(msgInfo,chatInfo)
return; return;
} }
// //
@ -259,7 +265,7 @@ export default {
// //
this.chatStore.openChat(chatInfo); this.chatStore.openChat(chatInfo);
// //
this.chatStore.insertMessage(msg); this.chatStore.insertMessage(msg, chatInfo);
// //
this.playAudioTip(); this.playAudioTip();
}, },
@ -374,6 +380,12 @@ export default {
// //
let loginInfo = uni.getStorageSync("loginInfo") let loginInfo = uni.getStorageSync("loginInfo")
this.refreshToken(loginInfo).then(() => { this.refreshToken(loginInfo).then(() => {
// #ifdef H5
//
uni.switchTab({
url: "/pages/chat/chat"
})
// #endif
// //
this.init(); this.init();
this.closeSplashscreen(0); this.closeSplashscreen(0);

8
im-uniapp/common/recorder-app.js

@ -3,6 +3,9 @@ import UNI_APP from '@/.env.js';
const rc = uni.getRecorderManager(); const rc = uni.getRecorderManager();
// 录音开始时间 // 录音开始时间
let startTime = null; let startTime = null;
let checkIsEnable = ()=>{
return true;
}
let start = () => { let start = () => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -20,10 +23,6 @@ let start = () => {
}) })
} }
let pause = () => {
rc.stop();
}
let close = () => { let close = () => {
rc.stop(); rc.stop();
} }
@ -61,6 +60,7 @@ let upload = () => {
} }
export { export {
checkIsEnable,
start, start,
close, close,
upload upload

52
im-uniapp/common/recorder-h5.js

@ -4,26 +4,39 @@ let rc = null;
let duration = 0; let duration = 0;
let chunks = []; let chunks = [];
let stream = null; let stream = null;
let startTime = null;
let checkIsEnable = () => {
if (origin.indexOf('https') === -1 && origin.indexOf('localhost') === -1 &&
origin.indexOf('127.0.0.1') === -1) {
uni.showToast({
title: '请在https环境中使用录音功能',
icon: 'error'
})
return false;
}
if (!navigator.mediaDevices || !window.MediaRecorder) {
uni.showToast({
title: '当前浏览器不支持录音',
icon: 'error'
})
return false;
}
return true;
}
let start = () => { let start = () => {
return navigator.mediaDevices.getUserMedia({ audio: true }).then(audioStream => { return navigator.mediaDevices.getUserMedia({ audio: true }).then(audioStream => {
const startTime = new Date().getTime(); console.log("start record")
startTime = new Date().getTime();
chunks = []; chunks = [];
stream = audioStream; stream = audioStream;
rc = new MediaRecorder(stream) rc = new MediaRecorder(stream)
rc.ondataavailable = (e) => {
console.log("ondataavailable")
chunks.push(e.data)
}
rc.onstop = () => {
duration = (new Date().getTime() - startTime) / 1000;
console.log("时长:", duration)
}
rc.start() rc.start()
}) })
} }
let close = () => { let close = () => {
console.log("stream:", stream)
stream.getTracks().forEach((track) => { stream.getTracks().forEach((track) => {
track.stop() track.stop()
}) })
@ -33,11 +46,23 @@ let close = () => {
let upload = () => { let upload = () => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
setTimeout(() => { rc.ondataavailable = (e) => {
console.log("ondataavailable:",e.data)
console.log("size:",e.data.size)
console.log("type:",e.data.type)
chunks.push(e.data)
}
rc.onstop = () => {
if(!chunks[0].size){
chunks = [];
return;
}
duration = (new Date().getTime() - startTime) / 1000;
console.log("时长:", duration)
console.log("上传,chunks:", chunks.length)
const newbolb = new Blob(chunks, { 'type': 'audio/mpeg' }); const newbolb = new Blob(chunks, { 'type': 'audio/mpeg' });
const name = new Date().getDate() + '.mp3'; const name = new Date().getDate() + '.mp3';
const file = new File([newbolb], name) const file = new File([newbolb], name)
console.log("upload")
uni.uploadFile({ uni.uploadFile({
url: UNI_APP.BASE_URL + '/file/upload', url: UNI_APP.BASE_URL + '/file/upload',
header: { header: {
@ -62,11 +87,12 @@ let upload = () => {
reject(e); reject(e);
} }
}) })
}, 100) }
}) })
} }
export { export {
checkIsEnable,
start, start,
close, close,
upload upload

12
im-uniapp/components/chat-group-readed/chat-group-readed.vue

@ -79,12 +79,18 @@ export default {
}) })
this.items[0] = `已读(${this.readedMembers.length})`; this.items[0] = `已读(${this.readedMembers.length})`;
this.items[1] = `未读(${this.unreadMembers.length})`; this.items[1] = `未读(${this.unreadMembers.length})`;
//
this.chatStore.updateMessage({ let chatInfo = {
type: 'GROUP',
targetId: this.msgInfo.groupId
}
let msgInfo = {
id: this.msgInfo.id, id: this.msgInfo.id,
groupId: this.msgInfo.groupId, groupId: this.msgInfo.groupId,
readedCount: this.readedMembers.length readedCount: this.readedMembers.length
}) }
//
this.chatStore.updateMessage(msgInfo, chatInfo)
}) })
}, },
onClickItem(e) { onClickItem(e) {

16
im-uniapp/components/chat-item/chat-item.vue

@ -42,8 +42,15 @@ export default {
} }
}, },
methods: { methods: {
showChatBox() { showChatBox() {
//
if(!getApp().$vm.isInit || this.chatStore.isLoading()){
uni.showToast({
title: "正在初始化页面,请稍后...",
icon: 'none'
})
return;
}
uni.navigateTo({ uni.navigateTo({
url: "/pages/chat/chat-box?chatIdx=" + this.index url: "/pages/chat/chat-box?chatIdx=" + this.index
}) })
@ -155,7 +162,8 @@ export default {
font-size: $im-font-size-smaller; font-size: $im-font-size-smaller;
color: $im-text-color-lighter; color: $im-text-color-lighter;
padding-top: 8rpx; padding-top: 8rpx;
align-items: center;
.chat-at-text { .chat-at-text {
color: $im-color-danger; color: $im-color-danger;
} }
@ -170,10 +178,6 @@ export default {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
img {
width: 40rpx !important;
height: 40rpx !important;
}
} }
} }

25
im-uniapp/components/chat-message-item/chat-message-item.vue

@ -17,7 +17,7 @@
<view class="chat-msg-bottom"> <view class="chat-msg-bottom">
<view v-if="msgInfo.type == $enums.MESSAGE_TYPE.TEXT"> <view v-if="msgInfo.type == $enums.MESSAGE_TYPE.TEXT">
<long-press-menu :items="menuItems" @select="onSelectMenu"> <long-press-menu :items="menuItems" @select="onSelectMenu">
<rich-text class="chat-msg-text" :nodes="$emo.transform(msgInfo.content, 'emoji-normal')"></rich-text> <rich-text class="chat-msg-text" :nodes="$emo.transform(msgInfo.content,'emoji-normal')"></rich-text>
</long-press-menu> </long-press-menu>
</view> </view>
<view class="chat-msg-image" v-if="msgInfo.type == $enums.MESSAGE_TYPE.IMAGE"> <view class="chat-msg-image" v-if="msgInfo.type == $enums.MESSAGE_TYPE.IMAGE">
@ -65,12 +65,12 @@
</view> </view>
</long-press-menu> </long-press-menu>
<view class="chat-msg-status" v-if="!isAction"> <view class="chat-msg-status" v-if="!isAction">
<text class="chat-readed" v-show="msgInfo.selfSend && !msgInfo.groupId <text class="chat-readed" v-if="msgInfo.selfSend && !msgInfo.groupId
&& msgInfo.status == $enums.MESSAGE_STATUS.READED">已读</text> && msgInfo.status == $enums.MESSAGE_STATUS.READED">已读</text>
<text class="chat-unread" v-show="msgInfo.selfSend && !msgInfo.groupId <text class="chat-unread" v-if="msgInfo.selfSend && !msgInfo.groupId
&& msgInfo.status != $enums.MESSAGE_STATUS.READED">未读</text> && msgInfo.status != $enums.MESSAGE_STATUS.READED">未读</text>
</view> </view>
<view class="chat-receipt" v-show="msgInfo.receipt" @click="onShowReadedBox"> <view class="chat-receipt" v-if="msgInfo.receipt" @click="onShowReadedBox">
<text v-if="msgInfo.receiptOk" class="tool-icon iconfont icon-ok"></text> <text v-if="msgInfo.receiptOk" class="tool-icon iconfont icon-ok"></text>
<text v-else>{{ msgInfo.readedCount }}人已读</text> <text v-else>{{ msgInfo.readedCount }}人已读</text>
</view> </view>
@ -125,14 +125,16 @@ export default {
this.innerAudioContext = uni.createInnerAudioContext(); this.innerAudioContext = uni.createInnerAudioContext();
let url = JSON.parse(this.msgInfo.content).url; let url = JSON.parse(this.msgInfo.content).url;
this.innerAudioContext.src = url; this.innerAudioContext.src = url;
console.log(url);
this.innerAudioContext.onEnded((e) => { this.innerAudioContext.onEnded((e) => {
console.log('停止') console.log('停止')
this.audioPlayState = "STOP" this.audioPlayState = "STOP"
this.emit();
}) })
this.innerAudioContext.onError((e) => { this.innerAudioContext.onError((e) => {
this.audioPlayState = "STOP"
console.log("播放音频出错"); console.log("播放音频出错");
console.log(e) console.log(e)
this.emit();
}); });
} }
if (this.audioPlayState == 'STOP') { if (this.audioPlayState == 'STOP') {
@ -145,6 +147,7 @@ export default {
this.innerAudioContext.play(); this.innerAudioContext.play();
this.audioPlayState = "PLAYING" this.audioPlayState = "PLAYING"
} }
this.emit();
}, },
onSelectMenu(item) { onSelectMenu(item) {
this.$emit(item.key.toLowerCase(), this.msgInfo); this.$emit(item.key.toLowerCase(), this.msgInfo);
@ -158,6 +161,16 @@ export default {
}, },
onShowReadedBox() { onShowReadedBox() {
this.$refs.chatGroupReaded.open(); this.$refs.chatGroupReaded.open();
},
emit(){
this.$emit("audioStateChange",this.audioPlayState,this.msgInfo);
},
stopPlayAudio(){
if (this.innerAudioContext) {
this.innerAudioContext.stop();
this.innerAudioContext = null;
this.audioPlayState = "STOP"
}
} }
}, },
computed: { computed: {
@ -262,6 +275,7 @@ export default {
.chat-msg-bottom { .chat-msg-bottom {
display: inline-block; display: inline-block;
padding-right: 80rpx; padding-right: 80rpx;
margin-top: 5rpx;
.chat-msg-text { .chat-msg-text {
position: relative; position: relative;
@ -289,7 +303,6 @@ export default {
border-color: $im-bg transparent transparent; border-color: $im-bg transparent transparent;
overflow: hidden; overflow: hidden;
border-width: 18rpx; border-width: 18rpx;
//box-shadow: $im-box-shadow-dark;
} }
} }

23
im-uniapp/components/chat-record/chat-record.vue

@ -50,12 +50,14 @@ export default {
/* 使@touchend /* 使@touchend
一直处于录音状态这里允许用户再次点击发送语音并结束录音 */ 一直处于录音状态这里允许用户再次点击发送语音并结束录音 */
if (this.recording) { if (this.recording) {
this.onEndRecord();
return; return;
} }
console.log("开始录音") console.log("开始录音")
this.moveToCancel = false; this.moveToCancel = false;
this.initRecordBar(); this.initRecordBar();
if(!this.$rc.checkIsEnable()){
return;
}
this.$rc.start().then(() => { this.$rc.start().then(() => {
this.recording = true; this.recording = true;
console.log("开始录音成功") console.log("开始录音成功")
@ -70,9 +72,12 @@ export default {
}); });
}, },
onEndRecord() { onEndRecord() {
if(!this.recording){
return;
}
this.recording = false; this.recording = false;
// //
this.StopTimer(); this.stopTimer();
// //
this.$rc.close(); this.$rc.close();
// //
@ -80,8 +85,8 @@ export default {
console.log("录音取消") console.log("录音取消")
return; return;
} }
// 1 // 1
if (this.druation == 0) { if (this.druation <= 1) {
uni.showToast({ uni.showToast({
title: "说话时间太短", title: "说话时间太短",
icon: 'none' icon: 'none'
@ -95,13 +100,11 @@ export default {
title: e, title: e,
icon: 'none' icon: 'none'
}) })
}).finally(() => {
this.$rc.close();
}) })
}, },
startTimer() { startTimer() {
this.druation = 0; this.druation = 0;
this.StopTimer(); this.stopTimer();
this.rcTimer = setInterval(() => { this.rcTimer = setInterval(() => {
this.druation++; this.druation++;
// 60s, // 60s,
@ -110,7 +113,7 @@ export default {
} }
}, 1000) }, 1000)
}, },
StopTimer() { stopTimer() {
this.rcTimer && clearInterval(this.rcTimer); this.rcTimer && clearInterval(this.rcTimer);
this.rcTimer = null; this.rcTimer = null;
}, },
@ -134,6 +137,10 @@ export default {
} }
return `录音时长:${this.druation}s`; return `录音时长:${this.druation}s`;
} }
},
unmounted() {
this.stopTimer();
this.recording = false;
} }
} }

272
im-uniapp/pages/chat/chat-box.vue

@ -6,10 +6,11 @@
<scroll-view class="scroll-box" scroll-y="true" upper-threshold="200" @scrolltoupper="onScrollToTop" <scroll-view class="scroll-box" scroll-y="true" upper-threshold="200" @scrolltoupper="onScrollToTop"
:scroll-into-view="'chat-item-' + scrollMsgIdx"> :scroll-into-view="'chat-item-' + scrollMsgIdx">
<view v-if="chat" v-for="(msgInfo, idx) in chat.messages" :key="idx"> <view v-if="chat" v-for="(msgInfo, idx) in chat.messages" :key="idx">
<chat-message-item v-if="idx >= showMinIdx" :headImage="headImage(msgInfo)" <chat-message-item :ref="'message'+msgInfo.id" v-if="idx >= showMinIdx"
@call="onRtCall(msgInfo)" :showName="showName(msgInfo)" @recall="onRecallMessage" :headImage="headImage(msgInfo)" @call="onRtCall(msgInfo)" :showName="showName(msgInfo)"
@copy="onCopyMessage" @delete="onDeleteMessage" @longPressHead="onLongPressHead(msgInfo)" @recall="onRecallMessage" @delete="onDeleteMessage" @copy="onCopyMessage"
@download="onDownloadFile" :id="'chat-item-' + idx" :msgInfo="msgInfo" @longPressHead="onLongPressHead(msgInfo)" @download="onDownloadFile"
@audioStateChange="onAudioStateChange" :id="'chat-item-' + idx" :msgInfo="msgInfo"
:groupMembers="groupMembers"> :groupMembers="groupMembers">
</chat-message-item> </chat-message-item>
</view> </view>
@ -19,7 +20,7 @@
<view class="iconfont icon-at">:&nbsp;</view> <view class="iconfont icon-at">:&nbsp;</view>
<scroll-view v-if="atUserIds.length > 0" class="chat-at-scroll-box" scroll-x="true" scroll-left="120"> <scroll-view v-if="atUserIds.length > 0" class="chat-at-scroll-box" scroll-x="true" scroll-left="120">
<view class="chat-at-items"> <view class="chat-at-items">
<view v-for="m in atUserItems" class="chat-at-item"> <view v-for="m in atUserItems" class="chat-at-item" :key="m.userId">
<head-image :name="m.showNickName" :url="m.headImage" size="minier"></head-image> <head-image :name="m.showNickName" :url="m.headImage" size="minier"></head-image>
</view> </view>
</view> </view>
@ -31,24 +32,26 @@
<chat-record v-if="showRecord" class="chat-record" @send="onSendRecord"></chat-record> <chat-record v-if="showRecord" class="chat-record" @send="onSendRecord"></chat-record>
<view v-else class="send-text"> <view v-else class="send-text">
<editor id="editor" class="send-text-area" :placeholder="isReceipt ? '[回执消息]' : ''" <editor id="editor" class="send-text-area" :placeholder="isReceipt ? '[回执消息]' : ''"
:read-only="isReadOnly" @focus="onEditorFocus" @blur="onEditorBlur" @ready="onEditorReady" :read-only="isReadOnly" @focus="onEditorFocus" @blur="onEditorBlur" @ready="onEditorReady" @input="onTextInput">
@input="onTextInput">
</editor> </editor>
<!-- <textarea class="send-text-area" v-model="sendText" auto-height :show-confirm-bar="false"
:placeholder="isReceipt ? '[回执消息]' : ''" :adjust-position="false" @confirm="sendTextMessage()"
@keyboardheightchange="onKeyboardheightchange" @input="onTextInput" confirm-type="send"
confirm-hold :hold-keyboard="true"></textarea> -->
</view> </view>
<view v-if="chat && chat.type == 'GROUP'" class="iconfont icon-at" @click="openAtBox()"></view> <view v-if="chat && chat.type == 'GROUP'" class="iconfont icon-at" @click="openAtBox()"></view>
<view class="iconfont icon-icon_emoji" @click="onShowEmoChatTab()"></view> <view class="iconfont icon-icon_emoji" @click="onShowEmoChatTab()"></view>
<view v-if="isEmpty" class="iconfont icon-add" @click="onShowToolsChatTab()"> <view v-if="isEmpty" class="iconfont icon-add" @click="onShowToolsChatTab()">
</view> </view>
<button v-if="!isEmpty || atUserIds.length > 0" class="btn-send" type="primary" <button v-if="!isEmpty || atUserIds.length" class="btn-send" type="primary"
@touchend.prevent="sendTextMessage()" size="mini">发送</button> @touchend.prevent="sendTextMessage()" size="mini">发送</button>
</view> </view>
</view> </view>
<view class="chat-tab-bar"> <view class="chat-tab-bar">
<view v-if="chatTabBox == 'tools'" class="chat-tools" :style="{height: keyboardHeight+'px'}"> <view v-if="chatTabBox == 'tools'" class="chat-tools" :style="{height: keyboardHeight+'px'}">
<view class="chat-tools-item">
<file-upload ref="fileUpload" :onBefore="onUploadFileBefore" :onSuccess="onUploadFileSuccess"
:onError="onUploadFileFail">
<view class="tool-icon iconfont icon-folder"></view>
</file-upload>
<view class="tool-name">文件</view>
</view>
<view class="chat-tools-item"> <view class="chat-tools-item">
<image-upload :maxCount="9" sourceType="album" :onBefore="onUploadImageBefore" <image-upload :maxCount="9" sourceType="album" :onBefore="onUploadImageBefore"
:onSuccess="onUploadImageSuccess" :onError="onUploadImageFail"> :onSuccess="onUploadImageSuccess" :onError="onUploadImageFail">
@ -63,15 +66,6 @@
</image-upload> </image-upload>
<view class="tool-name">拍摄</view> <view class="tool-name">拍摄</view>
</view> </view>
<view class="chat-tools-item">
<file-upload ref="fileUpload" :onBefore="onUploadFileBefore" :onSuccess="onUploadFileSuccess"
:onError="onUploadFileFail">
<view class="tool-icon iconfont icon-folder"></view>
</file-upload>
<view class="tool-name">文件</view>
</view>
<view class="chat-tools-item" @click="onRecorderInput()"> <view class="chat-tools-item" @click="onRecorderInput()">
<view class="tool-icon iconfont icon-microphone"></view> <view class="tool-icon iconfont icon-microphone"></view>
<view class="tool-name">语音消息</view> <view class="tool-name">语音消息</view>
@ -131,8 +125,10 @@ export default {
scrollMsgIdx: 0, // scrollMsgIdx: 0, //
chatTabBox: 'none', chatTabBox: 'none',
showRecord: false, showRecord: false,
keyboardHeight: 300,
chatMainHeight: 0, // chatMainHeight: 0, //
keyboardHeight: 290, //
windowHeight: 1000, //
initHeight: 1000, // h5
atUserIds: [], atUserIds: [],
needScrollToBottom: false, // needScrollToBottom: false, //
showMinIdx: 0, // showMinIdx showMinIdx: 0, // showMinIdx
@ -142,7 +138,8 @@ export default {
editorCtx: null, // editorCtx: null, //
isEmpty: true, // isEmpty: true, //
isFocus: false, // isFocus: false, //
isReadOnly: false // isReadOnly: false, //
playingAudio: null //
} }
}, },
methods: { methods: {
@ -155,6 +152,11 @@ export default {
this.switchChatTabBox('none'); this.switchChatTabBox('none');
}, },
onSendRecord(data) { onSendRecord(data) {
//
if (this.isBanned) {
this.showBannedTip();
return;
}
let msgInfo = { let msgInfo = {
content: JSON.stringify(data), content: JSON.stringify(data),
type: this.$enums.MESSAGE_TYPE.AUDIO, type: this.$enums.MESSAGE_TYPE.AUDIO,
@ -164,7 +166,7 @@ export default {
this.fillTargetId(msgInfo, this.chat.targetId); this.fillTargetId(msgInfo, this.chat.targetId);
this.sendMessageRequest(msgInfo).then((m) => { this.sendMessageRequest(msgInfo).then((m) => {
m.selfSend = true; m.selfSend = true;
this.chatStore.insertMessage(m); this.chatStore.insertMessage(m, this.chat);
// //
this.moveChatToTop(); this.moveChatToTop();
// //
@ -260,6 +262,15 @@ export default {
sendTextMessage() { sendTextMessage() {
this.editorCtx.getContents({ this.editorCtx.getContents({
success: (e) => { success: (e) => {
//
this.editorCtx.clear();
this.atUserIds = [];
this.isReceipt = false;
//
if (this.isBanned) {
this.showBannedTip();
return;
}
let sendText = this.isReceipt ? "【回执消息】" : ""; let sendText = this.isReceipt ? "【回执消息】" : "";
e.delta.ops.forEach((op) => { e.delta.ops.forEach((op) => {
if (op.insert.image) { if (op.insert.image) {
@ -294,13 +305,10 @@ export default {
}).finally(() => { }).finally(() => {
// //
this.scrollToBottom(); this.scrollToBottom();
//
this.atUserIds = [];
this.isReceipt = false;
this.editorCtx.clear();
}); });
} }
}) })
}, },
createAtText() { createAtText() {
let atText = ""; let atText = "";
@ -343,7 +351,6 @@ export default {
this.$nextTick(() => { this.$nextTick(() => {
this.scrollMsgIdx = idx; this.scrollMsgIdx = idx;
}); });
}, },
onShowEmoChatTab() { onShowEmoChatTab() {
this.showRecord = false; this.showRecord = false;
@ -379,6 +386,11 @@ export default {
}) })
}, },
onUploadImageBefore(file) { onUploadImageBefore(file) {
//
if (this.isBanned) {
this.showBannedTip();
return;
}
let data = { let data = {
originUrl: file.path, originUrl: file.path,
thumbUrl: file.path thumbUrl: file.path
@ -399,7 +411,7 @@ export default {
// id // id
this.fillTargetId(msgInfo, this.chat.targetId); this.fillTargetId(msgInfo, this.chat.targetId);
// //
this.chatStore.insertMessage(msgInfo); this.chatStore.insertMessage(msgInfo, this.chat);
// //
this.moveChatToTop(); this.moveChatToTop();
// file // file
@ -416,15 +428,20 @@ export default {
msgInfo.loadStatus = 'ok'; msgInfo.loadStatus = 'ok';
msgInfo.id = m.id; msgInfo.id = m.id;
this.isReceipt = false; this.isReceipt = false;
this.chatStore.insertMessage(msgInfo); this.chatStore.insertMessage(msgInfo, this.chat);
}) })
}, },
onUploadImageFail(file, err) { onUploadImageFail(file, err) {
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo)); let msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
msgInfo.loadStatus = 'fail'; msgInfo.loadStatus = 'fail';
this.chatStore.insertMessage(msgInfo); this.chatStore.insertMessage(msgInfo, this.chat);
}, },
onUploadFileBefore(file) { onUploadFileBefore(file) {
//
if (this.isBanned) {
this.showBannedTip();
return;
}
let data = { let data = {
name: file.name, name: file.name,
size: file.size, size: file.size,
@ -445,7 +462,7 @@ export default {
// id // id
this.fillTargetId(msgInfo, this.chat.targetId); this.fillTargetId(msgInfo, this.chat.targetId);
// //
this.chatStore.insertMessage(msgInfo); this.chatStore.insertMessage(msgInfo, this.chat);
// //
this.moveChatToTop(); this.moveChatToTop();
// file // file
@ -467,13 +484,13 @@ export default {
msgInfo.loadStatus = 'ok'; msgInfo.loadStatus = 'ok';
msgInfo.id = m.id; msgInfo.id = m.id;
this.isReceipt = false; this.isReceipt = false;
this.chatStore.insertMessage(msgInfo); this.chatStore.insertMessage(msgInfo, this.chat);
}) })
}, },
onUploadFileFail(file, res) { onUploadFileFail(file, res) {
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo)); let msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
msgInfo.loadStatus = 'fail'; msgInfo.loadStatus = 'fail';
this.chatStore.insertMessage(msgInfo); this.chatStore.insertMessage(msgInfo, this.chat);
}, },
onDeleteMessage(msgInfo) { onDeleteMessage(msgInfo) {
uni.showModal({ uni.showModal({
@ -481,7 +498,7 @@ export default {
content: '确认删除消息?', content: '确认删除消息?',
success: (res) => { success: (res) => {
if (!res.cancel) { if (!res.cancel) {
this.chatStore.deleteMessage(msgInfo); this.chatStore.deleteMessage(msgInfo, this.chat);
uni.showToast({ uni.showToast({
title: "删除成功", title: "删除成功",
icon: "none" icon: "none"
@ -505,7 +522,7 @@ export default {
msgInfo.type = this.$enums.MESSAGE_TYPE.RECALL; msgInfo.type = this.$enums.MESSAGE_TYPE.RECALL;
msgInfo.content = '你撤回了一条消息'; msgInfo.content = '你撤回了一条消息';
msgInfo.status = this.$enums.MESSAGE_STATUS.RECALL; msgInfo.status = this.$enums.MESSAGE_STATUS.RECALL;
this.chatStore.insertMessage(msgInfo); this.chatStore.insertMessage(msgInfo, this.chat);
}) })
} }
} }
@ -515,7 +532,7 @@ export default {
uni.setClipboardData({ uni.setClipboardData({
data: msgInfo.content, data: msgInfo.content,
success: () => { success: () => {
uni.showToast({ title: '已复制', icon: 'none' }); uni.showToast({ title: '复制成功' });
}, },
fail: () => { fail: () => {
uni.showToast({ title: '复制失败', icon: 'none' }); uni.showToast({ title: '复制失败', icon: 'none' });
@ -552,7 +569,7 @@ export default {
// //
this.scrollToMsgIdx(this.showMinIdx); this.scrollToMsgIdx(this.showMinIdx);
// #endif // #endif
// 0 // 20
this.showMinIdx = this.showMinIdx > 20 ? this.showMinIdx - 20 : 0; this.showMinIdx = this.showMinIdx > 20 ? this.showMinIdx - 20 : 0;
}, },
onShowMore() { onShowMore() {
@ -584,6 +601,15 @@ export default {
onEditorBlur(e) { onEditorBlur(e) {
this.isFocus = false; this.isFocus = false;
}, },
onAudioStateChange(state, msgInfo) {
const playingAudio = this.$refs['message' + msgInfo.id][0]
if (state == 'PLAYING' && playingAudio != this.playingAudio) {
//
this.playingAudio && this.playingAudio.stopPlayAudio();
//
this.playingAudio = playingAudio;
}
},
loadReaded(fid) { loadReaded(fid) {
this.$http({ this.$http({
url: `/message/private/maxReadedId?friendId=${fid}`, url: `/message/private/maxReadedId?friendId=${fid}`,
@ -642,7 +668,7 @@ export default {
}) })
}, },
rpxTopx(rpx) { rpxTopx(rpx) {
// pxrpx // rpxpx
let info = uni.getSystemInfoSync() let info = uni.getSystemInfoSync()
let px = info.windowWidth * rpx / 750; let px = info.windowWidth * rpx / 750;
return Math.floor(rpx); return Math.floor(rpx);
@ -673,53 +699,122 @@ export default {
}) })
} }
}, },
listenKeyBoard() { reCalChatMainHeight() {
// #ifdef H5 setTimeout(() => {
// H5TextArea@keyboardheightchange let h = this.windowHeight;
// //
let initHeight = window.innerHeight; h -= 50;
window.addEventListener('resize', () => { //
let keyboardHeight = initHeight - window.innerHeight; if (this.isShowKeyBoard || this.chatTabBox != 'none') {
this.isShowKeyBoard = keyboardHeight > 0; console.log("减去键盘高度:", this.keyboardHeight)
if (this.isShowKeyBoard) { h -= this.keyboardHeight;
this.keyboardHeight = keyboardHeight; this.scrollToBottom();
} }
this.reCalChatMainHeight(); // #ifndef H5
}); // h5
h -= uni.getSystemInfoSync().statusBarHeight;
// #endif
this.chatMainHeight = h;
console.log("窗口高度:", this.chatMainHeight)
if (this.isShowKeyBoard || this.chatTabBox != 'none') {
this.scrollToBottom();
}
// ios
// #ifdef H5
if (uni.getSystemInfoSync().platform == 'ios') {
//
const delays = [50, 100, 500];
delays.forEach((delay) => {
setTimeout(() => {
uni.pageScrollTo({
scrollTop: 0,
duration: 10
});
}, delay);
})
}
// #endif
}, 30)
},
listenKeyBoard() {
// #ifdef H5
const userAgent = navigator.userAgent;
const regex = /(macintosh|windows)/i;
if (regex.test(userAgent)) {
//
console.log("userAgent:", userAgent)
return;
}
if (uni.getSystemInfoSync().platform == 'ios') {
// ios h5
window.addEventListener('focusin', this.focusInListener);
window.addEventListener('focusout', this.focusOutListener);
} else {
// h5
let initHeight = window.innerHeight;
window.addEventListener('resize', this.resizeListener);
}
// #endif // #endif
// #ifndef H5 // #ifndef H5
uni.onKeyboardHeightChange((res) => { // app
this.isShowKeyBoard = res.height > 0; uni.onKeyboardHeightChange(this.keyBoardListener);
if (this.isShowKeyBoard) {
this.keyboardHeight = res.height; //
}
this.reCalChatMainHeight();
});
// #endif // #endif
}, },
reCalChatMainHeight() { unListenKeyboard() {
const sysInfo = uni.getSystemInfoSync();
let h = sysInfo.windowHeight;
//
h -= 50;
// #ifdef H5 // #ifdef H5
// h5sysInfo.windowHeight // h5
if (this.chatTabBox != 'none') { window.removeEventListener('resize', this.resizeListener);
h -= this.keyboardHeight; window.removeEventListener('focusin', this.focusInListener);
} window.removeEventListener('focusout', this.focusOutListener);
// #endif // #endif
// #ifndef H5 // #ifndef H5
// uni.offKeyboardHeightChange(this.keyBoardListener);
h -= sysInfo.statusBarHeight;
if (this.isShowKeyBoard || this.chatTabBox != 'none') {
h -= this.keyboardHeight;
}
// #endif // #endif
console.log("h:", h) },
this.chatMainHeight = h; keyBoardListener(res) {
this.isShowKeyBoard = res.height > 0;
if (this.isShowKeyBoard) {
this.keyboardHeight = res.height; //
}
this.reCalChatMainHeight();
},
resizeListener() {
console.log("resize")
let keyboardHeight = this.initHeight - window.innerHeight;
this.isShowKeyBoard = keyboardHeight > 150;
if (this.isShowKeyBoard) {
this.keyboardHeight = keyboardHeight;
}
this.reCalChatMainHeight();
},
focusInListener() {
console.log("focusInListener")
this.isShowKeyBoard = true;
this.reCalChatMainHeight();
},
focusOutListener() {
console.log("focusOutListener")
this.isShowKeyBoard = false;
this.reCalChatMainHeight();
},
showBannedTip() {
let msgInfo = {
tmpId: this.generateId(),
sendId: this.mine.id,
sendTime: new Date().getTime(),
type: this.$enums.MESSAGE_TYPE.TIP_TEXT
}
if (this.chat.type == "PRIVATE") {
msgInfo.recvId = this.mine.id
msgInfo.content = "该用户已被管理员封禁,原因:" + this.friend.reason
} else {
msgInfo.groupId = this.group.id;
msgInfo.content = "本群聊已被管理员封禁,原因:" + this.group.reason
}
this.chatStore.insertMessage(msgInfo, this.chat);
}, },
generateId() { generateId() {
// id // id
return String(new Date().getTime()) + String(Math.floor(Math.random() * 1000)); return String(new Date().getTime()) + String(Math.floor(Math.random() * 1000));
} }
}, },
@ -753,6 +848,10 @@ export default {
} }
return this.chat.unreadCount; return this.chat.unreadCount;
}, },
isBanned() {
return (this.chat.type == "PRIVATE" && this.friend.isBanned) ||
(this.chat.type == "GROUP" && this.group.isBanned)
},
atUserItems() { atUserItems() {
let atUsers = []; let atUsers = [];
this.atUserIds.forEach((id) => { this.atUserIds.forEach((id) => {
@ -815,7 +914,20 @@ export default {
// //
this.listenKeyBoard(); this.listenKeyBoard();
// //
this.$nextTick(()=>this.reCalChatMainHeight()) this.$nextTick(() => {
this.windowHeight = uni.getSystemInfoSync().windowHeight;
this.reCalChatMainHeight()
// ios h5:
// #ifdef H5
this.initHeight = window.innerHeight;
document.body.addEventListener('touchmove', function(e) {
e.preventDefault();
}, { passive: false });
// #endif
});
},
onUnload() {
this.unListenKeyboard();
}, },
onShow() { onShow() {
if (this.needScrollToBottom) { if (this.needScrollToBottom) {
@ -827,14 +939,12 @@ export default {
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss">
.chat-box { .chat-box {
$icon-color: rgba(0, 0, 0, 0.88); $icon-color: rgba(0, 0, 0, 0.88);
position: relative; position: relative;
background-color: #fafafa; background-color: #fafafa;
.header { .header {
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -869,7 +979,7 @@ export default {
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
z-index: 9; z-index: 2;
.chat-msg { .chat-msg {
flex: 1; flex: 1;

27
im-uniapp/pages/chat/chat.vue

@ -1,14 +1,20 @@
<template> <template>
<view class="tab-page"> <view class="tab-page">
<nav-bar search @search="showSearch = !showSearch">消息</nav-bar> <nav-bar search @search="onSearch()">消息</nav-bar>
<view v-if="loading" class="chat-loading"> <view v-if="loading" class="chat-loading">
<loading :size="50" :mask="false"> <loading :size="50" :mask="false">
<view>消息接收中...</view> <view>消息接收中...</view>
</loading> </loading>
</view> </view>
<view v-if="initializing" class="chat-loading">
<loading :size="50" :mask="false">
<view>正在初始化...</view>
</loading>
</view>
<view class="nav-bar" v-if="showSearch"> <view class="nav-bar" v-if="showSearch">
<view class="nav-search"> <view class="nav-search">
<uni-search-bar radius="100" v-model="searchText" cancelButton="none" placeholder="搜索"></uni-search-bar> <uni-search-bar focus="true" radius="100" v-model="searchText" cancelButton="none"
placeholder="搜索"></uni-search-bar>
</view> </view>
</view> </view>
<view class="chat-tip" v-if="!loading && chatStore.chats.length == 0"> <view class="chat-tip" v-if="!loading && chatStore.chats.length == 0">
@ -77,6 +83,10 @@ export default {
} }
return !this.searchText || chat.showName.includes(this.searchText) return !this.searchText || chat.showName.includes(this.searchText)
}, },
onSearch() {
this.showSearch = !this.showSearch;
this.searchText = "";
},
refreshUnreadBadge() { refreshUnreadBadge() {
if (this.unreadCount > 0) { if (this.unreadCount > 0) {
uni.setTabBarBadge({ uni.setTabBarBadge({
@ -86,7 +96,7 @@ export default {
} else { } else {
uni.removeTabBarBadge({ uni.removeTabBarBadge({
index: 0, index: 0,
complete: () => { } complete: () => {}
}) })
} }
} }
@ -102,7 +112,10 @@ export default {
return count; return count;
}, },
loading() { loading() {
return this.chatStore.loadingGroupMsg || this.chatStore.loadingPrivateMsg return this.chatStore.isLoading();
},
initializing(){
return !getApp().$vm.isInit;
} }
}, },
watch: { watch: {
@ -116,7 +129,7 @@ export default {
} }
</script> </script>
<style scoped lang="scss"> <style lang="scss">
.tab-page { .tab-page {
position: relative; position: relative;
display: flex; display: flex;
@ -136,9 +149,7 @@ export default {
width: 100%; width: 100%;
height: 120rpx; height: 120rpx;
background: white; background: white;
position: fixed;
top: 0;
z-index: 999;
color: $im-text-color-lighter; color: $im-text-color-lighter;
.loading-box { .loading-box {

35
im-uniapp/store/chatStore.js

@ -141,9 +141,9 @@ export default defineStore('chatStore', {
this.saveToStorage(); this.saveToStorage();
} }
}, },
insertMessage(msgInfo) { insertMessage(msgInfo, chatInfo) {
// 获取对方id或群id // 获取对方id或群id
let type = msgInfo.groupId ? 'GROUP' : 'PRIVATE'; let type = chatInfo.type;
// 记录消息的最大id // 记录消息的最大id
if (msgInfo.id && type == "PRIVATE" && msgInfo.id > this.privateMsgMaxId) { if (msgInfo.id && type == "PRIVATE" && msgInfo.id > this.privateMsgMaxId) {
this.privateMsgMaxId = msgInfo.id; this.privateMsgMaxId = msgInfo.id;
@ -152,7 +152,7 @@ export default defineStore('chatStore', {
this.groupMsgMaxId = msgInfo.id; this.groupMsgMaxId = msgInfo.id;
} }
// 如果是已存在消息,则覆盖旧的消息数据 // 如果是已存在消息,则覆盖旧的消息数据
let chat = this.findChat(msgInfo); let chat = this.findChat(chatInfo);
let message = this.findMessage(chat, msgInfo); let message = this.findMessage(chat, msgInfo);
if (message) { if (message) {
Object.assign(message, msgInfo); Object.assign(message, msgInfo);
@ -228,9 +228,9 @@ export default defineStore('chatStore', {
chat.stored = false; chat.stored = false;
this.saveToStorage(); this.saveToStorage();
}, },
updateMessage(msgInfo) { updateMessage(msgInfo, chatInfo) {
// 获取对方id或群id // 获取对方id或群id
let chat = this.findChat(msgInfo); let chat = this.findChat(chatInfo);
let message = this.findMessage(chat, msgInfo); let message = this.findMessage(chat, msgInfo);
if (message) { if (message) {
// 属性拷贝 // 属性拷贝
@ -239,9 +239,9 @@ export default defineStore('chatStore', {
this.saveToStorage(); this.saveToStorage();
} }
}, },
deleteMessage(msgInfo) { deleteMessage(msgInfo, chatInfo) {
// 获取对方id或群id // 获取对方id或群id
let chat = this.findChat(msgInfo); let chat = this.findChat(chatInfo);
for (let idx in chat.messages) { for (let idx in chat.messages) {
// 已经发送成功的,根据id删除 // 已经发送成功的,根据id删除
if (chat.messages[idx].id && chat.messages[idx].id == msgInfo.id) { if (chat.messages[idx].id && chat.messages[idx].id == msgInfo.id) {
@ -261,7 +261,7 @@ export default defineStore('chatStore', {
updateChatFromFriend(friend) { updateChatFromFriend(friend) {
let chat = this.findChatByFriend(friend.id) let chat = this.findChatByFriend(friend.id)
if (chat && (chat.headImage != friend.headImageThumb || if (chat && (chat.headImage != friend.headImageThumb ||
chat.showName != friend.nickName)) { chat.showName != friend.nickName)) {
// 更新会话中的群名和头像 // 更新会话中的群名和头像
chat.headImage = friend.headImageThumb; chat.headImage = friend.headImageThumb;
chat.showName = friend.nickName; chat.showName = friend.nickName;
@ -272,7 +272,7 @@ export default defineStore('chatStore', {
updateChatFromGroup(group) { updateChatFromGroup(group) {
let chat = this.findChatByGroup(group.id); let chat = this.findChatByGroup(group.id);
if (chat && (chat.headImage != group.headImageThumb || if (chat && (chat.headImage != group.headImageThumb ||
chat.showName != group.showGroupName)) { chat.showName != group.showGroupName)) {
// 更新会话中的群名称和头像 // 更新会话中的群名称和头像
chat.headImage = group.headImageThumb; chat.headImage = group.headImageThumb;
chat.showName = group.showGroupName; chat.showName = group.showGroupName;
@ -390,21 +390,10 @@ export default defineStore('chatStore', {
} }
} }
}, },
findChat: (state) => (msgInfo) => { findChat: (state) => (chat) => {
let chats = state.curChats; let chats = state.curChats;
// 获取对方id或群id let idx = state.findChatIdx(chat);
let type = msgInfo.groupId ? 'GROUP' : 'PRIVATE'; return chats[idx];
let targetId = msgInfo.groupId ? msgInfo.groupId : msgInfo.selfSend ? msgInfo.recvId : msgInfo
.sendId;
let chat = null;
for (let idx in chats) {
if (chats[idx].type == type &&
chats[idx].targetId === targetId) {
chat = chats[idx];
break;
}
}
return chat;
}, },
findChatByFriend: (state) => (fid) => { findChatByFriend: (state) => (fid) => {
return state.curChats.find(chat => chat.type == 'PRIVATE' && return state.curChats.find(chat => chat.type == 'PRIVATE' &&

75
im-web/src/components/chat/ChatBox.vue

@ -161,15 +161,20 @@ export default {
msgInfo.loadStatus = 'ok'; msgInfo.loadStatus = 'ok';
msgInfo.id = m.id; msgInfo.id = m.id;
this.isReceipt = false; this.isReceipt = false;
this.$store.commit("insertMessage", msgInfo); this.$store.commit("insertMessage", [msgInfo, this.chat]);
}) })
}, },
onImageFail(e, file) { onImageFail(e, file) {
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo)); let msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
msgInfo.loadStatus = 'fail'; msgInfo.loadStatus = 'fail';
this.$store.commit("insertMessage", msgInfo); this.$store.commit("insertMessage", [msgInfo, this.chat]);
}, },
onImageBefore(file) { onImageBefore(file) {
//
if (this.isBanned) {
this.showBannedTip();
return;
}
let url = URL.createObjectURL(file); let url = URL.createObjectURL(file);
let data = { let data = {
originUrl: url, originUrl: url,
@ -191,7 +196,7 @@ export default {
// id // id
this.fillTargetId(msgInfo, this.chat.targetId); this.fillTargetId(msgInfo, this.chat.targetId);
// //
this.$store.commit("insertMessage", msgInfo); this.$store.commit("insertMessage", [msgInfo, this.chat]);
// //
this.moveChatToTop(); this.moveChatToTop();
// //
@ -213,15 +218,20 @@ export default {
msgInfo.id = m.id; msgInfo.id = m.id;
this.isReceipt = false; this.isReceipt = false;
this.refreshPlaceHolder(); this.refreshPlaceHolder();
this.$store.commit("insertMessage", msgInfo); this.$store.commit("insertMessage", [msgInfo, this.chat]);
}) })
}, },
onFileFail(e, file) { onFileFail(e, file) {
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo)); let msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
msgInfo.loadStatus = 'fail'; msgInfo.loadStatus = 'fail';
this.$store.commit("insertMessage", msgInfo); this.$store.commit("insertMessage", [msgInfo, this.chat]);
}, },
onFileBefore(file) { onFileBefore(file) {
//
if (this.isBanned) {
this.showBannedTip();
return;
}
let url = URL.createObjectURL(file); let url = URL.createObjectURL(file);
let data = { let data = {
name: file.name, name: file.name,
@ -243,7 +253,7 @@ export default {
// id // id
this.fillTargetId(msgInfo, this.chat.targetId); this.fillTargetId(msgInfo, this.chat.targetId);
// //
this.$store.commit("insertMessage", msgInfo); this.$store.commit("insertMessage", [msgInfo, this.chat]);
// //
this.moveChatToTop(); this.moveChatToTop();
// //
@ -285,6 +295,12 @@ export default {
this.showRecord = false; this.showRecord = false;
}, },
showPrivateVideo(mode) { showPrivateVideo(mode) {
//
if (this.isBanned) {
this.showBannedTip();
return;
}
let rtcInfo = { let rtcInfo = {
mode: mode, mode: mode,
isHost: true, isHost: true,
@ -294,6 +310,11 @@ export default {
this.$eventBus.$emit("openPrivateVideo", rtcInfo); this.$eventBus.$emit("openPrivateVideo", rtcInfo);
}, },
onGroupVideo() { onGroupVideo() {
//
if (this.isBanned) {
this.showBannedTip();
return;
}
// //
let ids = [this.mine.id]; let ids = [this.mine.id];
let maxChannel = this.$store.state.configStore.webrtc.maxChannel; let maxChannel = this.$store.state.configStore.webrtc.maxChannel;
@ -329,6 +350,11 @@ export default {
this.showHistory = false; this.showHistory = false;
}, },
onSendRecord(data) { onSendRecord(data) {
//
if (this.isBanned) {
this.showBannedTip();
return;
}
let msgInfo = { let msgInfo = {
content: JSON.stringify(data), content: JSON.stringify(data),
type: 3, type: 3,
@ -338,7 +364,7 @@ export default {
this.fillTargetId(msgInfo, this.chat.targetId); this.fillTargetId(msgInfo, this.chat.targetId);
this.sendMessageRequest(msgInfo).then((m) => { this.sendMessageRequest(msgInfo).then((m) => {
m.selfSend = true; m.selfSend = true;
this.$store.commit("insertMessage", m); this.$store.commit("insertMessage", [m, this.chat]);
// //
this.moveChatToTop(); this.moveChatToTop();
// //
@ -364,6 +390,11 @@ export default {
async sendMessage(fullList) { async sendMessage(fullList) {
this.resetEditor(); this.resetEditor();
this.readedMessage(); this.readedMessage();
//
if (this.isBanned) {
this.showBannedTip();
return;
}
let sendText = this.isReceipt ? "【回执消息】" : ""; let sendText = this.isReceipt ? "【回执消息】" : "";
let promiseList = []; let promiseList = [];
for (let i = 0; i < fullList.length; i++) { for (let i = 0; i < fullList.length; i++) {
@ -421,7 +452,7 @@ export default {
this.lockMessage = true; this.lockMessage = true;
this.sendMessageRequest(msgInfo).then((m) => { this.sendMessageRequest(msgInfo).then((m) => {
m.selfSend = true; m.selfSend = true;
this.$store.commit("insertMessage", m); this.$store.commit("insertMessage", [m, this.chat]);
// //
this.moveChatToTop(); this.moveChatToTop();
}).finally(() => { }).finally(() => {
@ -465,7 +496,7 @@ export default {
msgInfo.type = 10; msgInfo.type = 10;
msgInfo.content = '你撤回了一条消息'; msgInfo.content = '你撤回了一条消息';
msgInfo.status = this.$enums.MESSAGE_STATUS.RECALL; msgInfo.status = this.$enums.MESSAGE_STATUS.RECALL;
this.$store.commit("insertMessage", msgInfo); this.$store.commit("insertMessage", [msgInfo, this.chat]);
}) })
}); });
}, },
@ -482,7 +513,7 @@ export default {
this.$http({ this.$http({
url: url, url: url,
method: 'put' method: 'put'
}).then(() => { }) }).then(() => {})
}, },
loadReaded(fId) { loadReaded(fId) {
this.$http({ this.$http({
@ -588,6 +619,22 @@ export default {
}) })
} }
}, },
showBannedTip() {
let msgInfo = {
tmpId: this.generateId(),
sendId: this.mine.id,
sendTime: new Date().getTime(),
type: this.$enums.MESSAGE_TYPE.TIP_TEXT
}
if (this.chat.type == "PRIVATE") {
msgInfo.recvId = this.mine.id
msgInfo.content = "该用户已被管理员封禁,原因:" + this.friend.reason
} else {
msgInfo.groupId = this.group.id;
msgInfo.content = "本群聊已被管理员封禁,原因:" + this.group.reason
}
this.$store.commit("insertMessage", [msgInfo, this.chat]);
},
generateId() { generateId() {
// id // id
return String(new Date().getTime()) + String(Math.floor(Math.random() * 1000)); return String(new Date().getTime()) + String(Math.floor(Math.random() * 1000));
@ -616,13 +663,17 @@ export default {
return 0; return 0;
} }
return this.chat.messages.length; return this.chat.messages.length;
},
isBanned() {
return (this.chat.type == "PRIVATE" && this.friend.isBanned) ||
(this.chat.type == "GROUP" && this.group.isBanned)
} }
}, },
watch: { watch: {
chat: { chat: {
handler(newChat, oldChat) { handler(newChat, oldChat) {
if (newChat.targetId > 0 && (!oldChat || newChat.type != oldChat.type || if (newChat.targetId > 0 && (!oldChat || newChat.type != oldChat.type ||
newChat.targetId != oldChat.targetId)) { newChat.targetId != oldChat.targetId)) {
if (this.chat.type == "GROUP") { if (this.chat.type == "GROUP") {
this.loadGroup(this.chat.targetId); this.loadGroup(this.chat.targetId);
} else { } else {
@ -831,4 +882,4 @@ export default {
} }
} }
</style> </style>

2
im-web/src/components/chat/ChatInput.vue

@ -443,7 +443,7 @@ export default {
if (node.dataset.id) { if (node.dataset.id) {
tempText += node.innerHTML; tempText += node.innerHTML;
atUserIds.push(node.dataset.id) atUserIds.push(node.dataset.id)
} else { } else if(node.outerHtml) {
tempText += node.outerHtml; tempText += node.outerHtml;
} }
} }

35
im-web/src/store/chatStore.js

@ -137,8 +137,8 @@ export default {
this.commit("saveToStorage"); this.commit("saveToStorage");
} }
}, },
insertMessage(state, msgInfo) { insertMessage(state, [msgInfo, chatInfo]) {
let type = msgInfo.groupId ? 'GROUP' : 'PRIVATE'; let type = chatInfo.type;
// 记录消息的最大id // 记录消息的最大id
if (msgInfo.id && type == "PRIVATE" && msgInfo.id > state.privateMsgMaxId) { if (msgInfo.id && type == "PRIVATE" && msgInfo.id > state.privateMsgMaxId) {
state.privateMsgMaxId = msgInfo.id; state.privateMsgMaxId = msgInfo.id;
@ -147,7 +147,7 @@ export default {
state.groupMsgMaxId = msgInfo.id; state.groupMsgMaxId = msgInfo.id;
} }
// 如果是已存在消息,则覆盖旧的消息数据 // 如果是已存在消息,则覆盖旧的消息数据
let chat = this.getters.findChat(msgInfo); let chat = this.getters.findChat(chatInfo);
let message = this.getters.findMessage(chat, msgInfo); let message = this.getters.findMessage(chat, msgInfo);
if (message) { if (message) {
Object.assign(message, msgInfo); Object.assign(message, msgInfo);
@ -178,7 +178,8 @@ export default {
chat.lastSendTime = msgInfo.sendTime; chat.lastSendTime = msgInfo.sendTime;
chat.sendNickName = msgInfo.sendNickName; chat.sendNickName = msgInfo.sendNickName;
// 未读加1 // 未读加1
if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED && msgInfo.type != MESSAGE_TYPE.TIP_TEXT) { if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED &&
msgInfo.type != MESSAGE_TYPE.TIP_TEXT) {
chat.unreadCount++; chat.unreadCount++;
} }
// 是否有人@我 // 是否有人@我
@ -216,9 +217,9 @@ export default {
chat.stored = false; chat.stored = false;
this.commit("saveToStorage"); this.commit("saveToStorage");
}, },
updateMessage(state, msgInfo) { updateMessage(state, [msgInfo, chatInfo]) {
// 获取对方id或群id // 获取对方id或群id
let chat = this.getters.findChat(msgInfo); let chat = this.getters.findChat(chatInfo);
let message = this.getters.findMessage(chat, msgInfo); let message = this.getters.findMessage(chat, msgInfo);
if (message) { if (message) {
// 属性拷贝 // 属性拷贝
@ -227,8 +228,8 @@ export default {
this.commit("saveToStorage"); this.commit("saveToStorage");
} }
}, },
deleteMessage(state, msgInfo) { deleteMessage(state, [msgInfo, chatInfo]) {
let chat = this.getters.findChat(msgInfo); let chat = this.getters.findChat(chatInfo);
for (let idx in chat.messages) { for (let idx in chat.messages) {
// 已经发送成功的,根据id删除 // 已经发送成功的,根据id删除
if (chat.messages[idx].id && chat.messages[idx].id == msgInfo.id) { if (chat.messages[idx].id && chat.messages[idx].id == msgInfo.id) {
@ -289,7 +290,7 @@ export default {
}); });
// 将消息一次性装载回来 // 将消息一次性装载回来
state.chats = cacheChats; state.chats = cacheChats;
// 清空缓存,不再使用 // 清空缓存
cacheChats = null; cacheChats = null;
this.commit("saveToStorage"); this.commit("saveToStorage");
}, },
@ -384,20 +385,10 @@ export default {
} }
} }
}, },
findChat: (state, getters) => (msgInfo) => { findChat: (state, getters) => (chat) => {
let chats = getters.findChats(); let chats = getters.findChats();
// 获取对方id或群id let idx = getters.findChatIdx(chat);
let type = msgInfo.groupId ? 'GROUP' : 'PRIVATE'; return chats[idx];
let targetId = msgInfo.groupId ? msgInfo.groupId : msgInfo.selfSend ? msgInfo.recvId : msgInfo.sendId;
let chat = null;
for (let idx in chats) {
if (chats[idx].type == type &&
chats[idx].targetId === targetId) {
chat = chats[idx];
break;
}
}
return chat;
}, },
findChatByFriend: (state, getters) => (fid) => { findChatByFriend: (state, getters) => (fid) => {
let chats = getters.findChats(); let chats = getters.findChats();

5
im-web/src/view/Home.vue

@ -201,7 +201,7 @@ export default {
// //
this.$store.commit("openChat", chatInfo); this.$store.commit("openChat", chatInfo);
// //
this.$store.commit("insertMessage", msg); this.$store.commit("insertMessage", [msg, chatInfo]);
// //
if (!msg.selfSend && this.$msgType.isNormal(msg.type) && if (!msg.selfSend && this.$msgType.isNormal(msg.type) &&
msg.status != this.$enums.MESSAGE_STATUS.READED) { msg.status != this.$enums.MESSAGE_STATUS.READED) {
@ -251,7 +251,6 @@ export default {
}) })
}, },
insertGroupMessage(group, msg) { insertGroupMessage(group, msg) {
let chatInfo = { let chatInfo = {
type: 'GROUP', type: 'GROUP',
targetId: group.id, targetId: group.id,
@ -261,7 +260,7 @@ export default {
// //
this.$store.commit("openChat", chatInfo); this.$store.commit("openChat", chatInfo);
// //
this.$store.commit("insertMessage", msg); this.$store.commit("insertMessage", [msg, chatInfo]);
// //
if (!msg.selfSend && msg.type <= this.$enums.MESSAGE_TYPE.VIDEO && if (!msg.selfSend && msg.type <= this.$enums.MESSAGE_TYPE.VIDEO &&
msg.status != this.$enums.MESSAGE_STATUS.READED) { msg.status != this.$enums.MESSAGE_STATUS.READED) {

Loading…
Cancel
Save