|
|
|
@ -2,17 +2,19 @@ |
|
|
|
<el-container class="chat-box"> |
|
|
|
<el-header height="60px"> |
|
|
|
<span>{{title}}</span> |
|
|
|
<span title="群聊信息" v-show="this.chat.type=='GROUP'" class="btn-side el-icon-more" @click="showSide=!showSide"></span> |
|
|
|
<span title="群聊信息" v-show="this.chat.type=='GROUP'" class="btn-side el-icon-more" |
|
|
|
@click="showSide=!showSide"></span> |
|
|
|
</el-header> |
|
|
|
<el-main style="padding: 0;"> |
|
|
|
<el-container> |
|
|
|
<el-container class="content-box"> |
|
|
|
<el-main class="im-chat-main" id="chatScrollBox"> |
|
|
|
<el-main class="im-chat-main" id="chatScrollBox" @scroll="handleScroll"> |
|
|
|
<div class="im-chat-box"> |
|
|
|
<ul> |
|
|
|
<li v-for="(msgInfo,idx) in chat.messages" :key="idx"> |
|
|
|
<chat-message-item :mine="msgInfo.sendId == mine.id" :headImage="headImage(msgInfo)" :showName="showName(msgInfo)" |
|
|
|
:msgInfo="msgInfo" @delete="deleteMessage" @recall="recallMessage"> |
|
|
|
<chat-message-item v-show="idx>=showMinIdx" :mine="msgInfo.sendId == mine.id" :headImage="headImage(msgInfo)" |
|
|
|
:showName="showName(msgInfo)" :msgInfo="msgInfo" @delete="deleteMessage" |
|
|
|
@recall="recallMessage"> |
|
|
|
</chat-message-item> |
|
|
|
</li> |
|
|
|
</ul> |
|
|
|
@ -20,31 +22,44 @@ |
|
|
|
</el-main> |
|
|
|
<el-footer height="240px" class="im-chat-footer"> |
|
|
|
<div class="chat-tool-bar"> |
|
|
|
<div title="表情" class="icon iconfont icon-biaoqing" ref="emotion" @click="switchEmotionBox()"> |
|
|
|
<div title="表情" class="icon iconfont icon-biaoqing" ref="emotion" |
|
|
|
@click="switchEmotionBox()"> |
|
|
|
</div> |
|
|
|
<div title="发送图片"> |
|
|
|
<file-upload :action="imageAction" :maxSize="5*1024*1024" :fileTypes="['image/jpeg', 'image/png', 'image/jpg', 'image/webp','image/gif']" |
|
|
|
<file-upload :action="imageAction" :maxSize="5*1024*1024" |
|
|
|
:fileTypes="['image/jpeg', 'image/png', 'image/jpg', 'image/webp','image/gif']" |
|
|
|
@before="handleImageBefore" @success="handleImageSuccess" @fail="handleImageFail"> |
|
|
|
<i class="el-icon-picture-outline"></i> |
|
|
|
</file-upload> |
|
|
|
</div> |
|
|
|
<div title="发送文件"> |
|
|
|
<file-upload :action="fileAction" :maxSize="10*1024*1024" @before="handleFileBefore" @success="handleFileSuccess" |
|
|
|
@fail="handleFileFail"> |
|
|
|
<file-upload :action="fileAction" :maxSize="10*1024*1024" @before="handleFileBefore" |
|
|
|
@success="handleFileSuccess" @fail="handleFileFail"> |
|
|
|
<i class="el-icon-wallet"></i> |
|
|
|
</file-upload> |
|
|
|
</div> |
|
|
|
<div title="发送语音" class="el-icon-microphone" @click="showVoiceBox()"> |
|
|
|
</div> |
|
|
|
<div title="视频聊天" v-show="chat.type=='PRIVATE'" class="el-icon-phone-outline" @click="showVideoBox()"> |
|
|
|
<div title="视频聊天" v-show="chat.type=='PRIVATE'" class="el-icon-phone-outline" |
|
|
|
@click="showVideoBox()"> |
|
|
|
</div> |
|
|
|
<div title="聊天记录" class="el-icon-chat-dot-round" @click="showHistoryBox()"></div> |
|
|
|
</div> |
|
|
|
<textarea v-model="sendText" ref="sendBox" class="send-text-area" |
|
|
|
:disabled="lockMessage" @keydown.enter="sendTextMessage()" |
|
|
|
placeholder="聊点什么吧~"></textarea> |
|
|
|
<div class="im-chat-send"> |
|
|
|
<el-button type="primary" size="small" @click="sendTextMessage()">发送</el-button> |
|
|
|
<div class="send-content-area"> |
|
|
|
<textarea v-show="!sendImageUrl" v-model="sendText" ref="sendBox" class="send-text-area" |
|
|
|
:disabled="lockMessage" @keydown.enter="sendTextMessage()" @paste="handlePaste" |
|
|
|
placeholder="温馨提示:可以粘贴截图到这里了哦~"></textarea> |
|
|
|
|
|
|
|
<div v-show="sendImageUrl" class="send-image-area"> |
|
|
|
<div class="send-image-box"> |
|
|
|
<img class="send-image" :src="sendImageUrl" /> |
|
|
|
<span class="send-image-close el-icon-close" title="删除" |
|
|
|
@click="removeSendImage()"></span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="send-btn-area"> |
|
|
|
<el-button type="primary" size="small" @click="handleSendMessage()">发送</el-button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</el-footer> |
|
|
|
</el-container> |
|
|
|
@ -56,7 +71,8 @@ |
|
|
|
</el-main> |
|
|
|
<emotion v-show="showEmotion" :pos="emoBoxPos" @emotion="handleEmotion"></Emotion> |
|
|
|
<chat-voice :visible="showVoice" @close="closeVoiceBox" @send="handleSendVoice"></chat-voice> |
|
|
|
<chat-history :visible="showHistory" :chat="chat" :friend="friend" :group="group" :groupMembers="groupMembers" @close="closeHistoryBox"></chat-history> |
|
|
|
<chat-history :visible="showHistory" :chat="chat" :friend="friend" :group="group" :groupMembers="groupMembers" |
|
|
|
@close="closeHistoryBox"></chat-history> |
|
|
|
</el-container> |
|
|
|
</template> |
|
|
|
|
|
|
|
@ -89,6 +105,8 @@ |
|
|
|
group: {}, |
|
|
|
groupMembers: [], |
|
|
|
sendText: "", |
|
|
|
sendImageUrl: "", |
|
|
|
sendImageFile: "", |
|
|
|
showVoice: false, // 是否显示语音录制弹窗 |
|
|
|
showSide: false, // 是否显示群聊信息栏 |
|
|
|
showEmotion: false, // 是否显示emoji表情 |
|
|
|
@ -97,13 +115,34 @@ |
|
|
|
y: 0 |
|
|
|
}, |
|
|
|
showHistory: false, // 是否显示历史聊天记录 |
|
|
|
lockMessage: false // 是否锁定发送 |
|
|
|
lockMessage: false, // 是否锁定发送, |
|
|
|
showMinIdx: 0 // 下标低于showMinIdx的消息不显示,否则页面会很卡 |
|
|
|
} |
|
|
|
}, |
|
|
|
methods: { |
|
|
|
handleImageSuccess(res, file) { |
|
|
|
let msgInfo = JSON.parse(JSON.stringify(file.raw.msgInfo)); |
|
|
|
msgInfo.content = JSON.stringify(res.data); |
|
|
|
handlePaste(e) { |
|
|
|
let txt = event.clipboardData.getData('Text') |
|
|
|
if (typeof(txt) == 'string') { |
|
|
|
this.sendText += txt |
|
|
|
} |
|
|
|
const items = (event.clipboardData || window.clipboardData).items |
|
|
|
if (items.length) { |
|
|
|
for (let i = 0; i < items.length; i++) { |
|
|
|
if (items[i].type.indexOf('image') !== -1) { |
|
|
|
let file = items[i].getAsFile(); |
|
|
|
this.sendImageFile = file; |
|
|
|
this.sendImageUrl = URL.createObjectURL(file); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
removeSendImage() { |
|
|
|
this.sendImageUrl = ""; |
|
|
|
this.sendImageFile = null; |
|
|
|
}, |
|
|
|
handleImageSuccess(data, file) { |
|
|
|
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo || file.raw.msgInfo)); |
|
|
|
msgInfo.content = JSON.stringify(data); |
|
|
|
this.$http({ |
|
|
|
url: this.messageAction, |
|
|
|
method: 'post', |
|
|
|
@ -114,8 +153,8 @@ |
|
|
|
this.$store.commit("insertMessage", msgInfo); |
|
|
|
}) |
|
|
|
}, |
|
|
|
handleImageFail(res, file) { |
|
|
|
let msgInfo = JSON.parse(JSON.stringify(file.raw.msgInfo)); |
|
|
|
handleImageFail(e, file) { |
|
|
|
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo || file.raw.msgInfo)); |
|
|
|
msgInfo.loadStatus = 'fail'; |
|
|
|
this.$store.commit("insertMessage", msgInfo); |
|
|
|
}, |
|
|
|
@ -133,7 +172,8 @@ |
|
|
|
sendTime: new Date().getTime(), |
|
|
|
selfSend: true, |
|
|
|
type: 1, |
|
|
|
loadStatus: "loading" |
|
|
|
loadStatus: "loading", |
|
|
|
status: this.$enums.MESSAGE_STATUS.UNSEND |
|
|
|
} |
|
|
|
// 填充对方id |
|
|
|
this.fillTargetId(msgInfo, this.chat.targetId); |
|
|
|
@ -144,11 +184,11 @@ |
|
|
|
// 借助file对象保存 |
|
|
|
file.msgInfo = msgInfo; |
|
|
|
}, |
|
|
|
handleFileSuccess(res, file) { |
|
|
|
handleFileSuccess(url, file) { |
|
|
|
let data = { |
|
|
|
name: file.name, |
|
|
|
size: file.size, |
|
|
|
url: res.data |
|
|
|
url: url |
|
|
|
} |
|
|
|
let msgInfo = JSON.parse(JSON.stringify(file.raw.msgInfo)); |
|
|
|
msgInfo.content = JSON.stringify(data); |
|
|
|
@ -162,7 +202,8 @@ |
|
|
|
this.$store.commit("insertMessage", msgInfo); |
|
|
|
}) |
|
|
|
}, |
|
|
|
handleFileFail(res, file) { |
|
|
|
handleFileFail(e, file) { |
|
|
|
|
|
|
|
let msgInfo = JSON.parse(JSON.stringify(file.raw.msgInfo)); |
|
|
|
msgInfo.loadStatus = 'fail'; |
|
|
|
this.$store.commit("insertMessage", msgInfo); |
|
|
|
@ -181,7 +222,8 @@ |
|
|
|
sendTime: new Date().getTime(), |
|
|
|
selfSend: true, |
|
|
|
type: 2, |
|
|
|
loadStatus: "loading" |
|
|
|
loadStatus: "loading", |
|
|
|
status: this.$enums.MESSAGE_STATUS.UNSEND |
|
|
|
} |
|
|
|
// 填充对方id |
|
|
|
this.fillTargetId(msgInfo, this.chat.targetId); |
|
|
|
@ -195,6 +237,18 @@ |
|
|
|
handleCloseSide() { |
|
|
|
this.showSide = false; |
|
|
|
}, |
|
|
|
handleScrollToTop() { |
|
|
|
// 多展示10条信息 |
|
|
|
this.showMinIdx = this.showMinIdx > 10 ? this.showMinIdx - 10 : 0; |
|
|
|
}, |
|
|
|
handleScroll(e) { |
|
|
|
let scrollElement = e.target |
|
|
|
let scrollTop = scrollElement.scrollTop |
|
|
|
if (scrollTop < 30 ) { // 在顶部,不滚动的情况 |
|
|
|
// 多展示20条信息 |
|
|
|
this.showMinIdx = this.showMinIdx > 20 ? this.showMinIdx - 20 : 0; |
|
|
|
} |
|
|
|
}, |
|
|
|
switchEmotionBox() { |
|
|
|
this.showEmotion = !this.showEmotion; |
|
|
|
let width = this.$refs.emotion.offsetWidth; |
|
|
|
@ -216,7 +270,6 @@ |
|
|
|
this.showVoice = false; |
|
|
|
}, |
|
|
|
showVideoBox() { |
|
|
|
console.log(this.friend) |
|
|
|
this.$store.commit("showChatPrivateVideoBox", { |
|
|
|
friend: this.friend, |
|
|
|
master: true |
|
|
|
@ -240,11 +293,11 @@ |
|
|
|
method: 'post', |
|
|
|
data: msgInfo |
|
|
|
}).then((id) => { |
|
|
|
this.$message.success("发送成功"); |
|
|
|
msgInfo.id = id; |
|
|
|
msgInfo.sendTime = new Date().getTime(); |
|
|
|
msgInfo.sendId = this.$store.state.userStore.userInfo.id; |
|
|
|
msgInfo.selfSend = true; |
|
|
|
msgInfo.status = this.$enums.MESSAGE_STATUS.UNSEND; |
|
|
|
this.$store.commit("insertMessage", msgInfo); |
|
|
|
// 保持输入框焦点 |
|
|
|
this.$refs.sendBox.focus(); |
|
|
|
@ -261,9 +314,34 @@ |
|
|
|
msgInfo.recvId = targetId; |
|
|
|
} |
|
|
|
}, |
|
|
|
handleSendMessage() { |
|
|
|
if (this.sendImageFile) { |
|
|
|
this.sendImageMessage(); |
|
|
|
} else { |
|
|
|
this.sendTextMessage(); |
|
|
|
} |
|
|
|
}, |
|
|
|
sendImageMessage() { |
|
|
|
let file = this.sendImageFile; |
|
|
|
this.handleImageBefore(this.sendImageFile); |
|
|
|
let formData = new FormData() |
|
|
|
formData.append('file', file.raw || file) |
|
|
|
this.$http.post("/image/upload", formData, { |
|
|
|
headers: { |
|
|
|
'Content-Type': 'multipart/form-data' |
|
|
|
} |
|
|
|
}).then((data) => { |
|
|
|
this.handleImageSuccess(data, file); |
|
|
|
}).catch((res) => { |
|
|
|
this.handleImageSuccess(res, file); |
|
|
|
}) |
|
|
|
this.sendImageFile = null; |
|
|
|
this.sendImageUrl = ""; |
|
|
|
this.$nextTick(() => this.$refs.sendBox.focus()); |
|
|
|
this.scrollToBottom(); |
|
|
|
}, |
|
|
|
sendTextMessage() { |
|
|
|
if (!this.sendText.trim()) { |
|
|
|
this.$message.error("不能发送空白信息"); |
|
|
|
return |
|
|
|
} |
|
|
|
let msgInfo = { |
|
|
|
@ -278,12 +356,12 @@ |
|
|
|
method: 'post', |
|
|
|
data: msgInfo |
|
|
|
}).then((id) => { |
|
|
|
this.$message.success("发送成功"); |
|
|
|
this.sendText = ""; |
|
|
|
msgInfo.id = id; |
|
|
|
msgInfo.sendTime = new Date().getTime(); |
|
|
|
msgInfo.sendId = this.$store.state.userStore.userInfo.id; |
|
|
|
msgInfo.selfSend = true; |
|
|
|
msgInfo.status = this.$enums.MESSAGE_STATUS.UNSEND; |
|
|
|
this.$store.commit("insertMessage", msgInfo); |
|
|
|
}).finally(() => { |
|
|
|
// 解除锁定 |
|
|
|
@ -324,10 +402,24 @@ |
|
|
|
msgInfo = JSON.parse(JSON.stringify(msgInfo)); |
|
|
|
msgInfo.type = 10; |
|
|
|
msgInfo.content = '你撤回了一条消息'; |
|
|
|
msgInfo.status = this.$enums.MESSAGE_STATUS.RECALL; |
|
|
|
this.$store.commit("insertMessage", msgInfo); |
|
|
|
}) |
|
|
|
}); |
|
|
|
|
|
|
|
}, |
|
|
|
readedMessage() { |
|
|
|
if (this.chat.type == "GROUP") { |
|
|
|
var url = `/message/group/readed?groupId=${this.chat.targetId}` |
|
|
|
} else { |
|
|
|
url = `/message/private/readed?friendId=${this.chat.targetId}` |
|
|
|
} |
|
|
|
this.$http({ |
|
|
|
url: url, |
|
|
|
method: 'put' |
|
|
|
}).then(() => { |
|
|
|
this.$store.commit("resetUnreadCount", this.chat) |
|
|
|
this.scrollToBottom(); |
|
|
|
}) |
|
|
|
}, |
|
|
|
loadGroup(groupId) { |
|
|
|
this.$http({ |
|
|
|
@ -354,7 +446,6 @@ |
|
|
|
method: 'get' |
|
|
|
}).then((friend) => { |
|
|
|
this.friend = friend; |
|
|
|
console.log(this.friend) |
|
|
|
this.$store.commit("updateChatFromFriend", friend); |
|
|
|
this.$store.commit("updateFriend", friend); |
|
|
|
}) |
|
|
|
@ -378,7 +469,7 @@ |
|
|
|
}, |
|
|
|
scrollToBottom() { |
|
|
|
this.$nextTick(() => { |
|
|
|
const div = document.getElementById("chatScrollBox"); |
|
|
|
let div = document.getElementById("chatScrollBox"); |
|
|
|
div.scrollTop = div.scrollHeight; |
|
|
|
}); |
|
|
|
} |
|
|
|
@ -403,19 +494,29 @@ |
|
|
|
}, |
|
|
|
messageAction() { |
|
|
|
return `/message/${this.chat.type.toLowerCase()}/send`; |
|
|
|
}, |
|
|
|
unreadCount() { |
|
|
|
return this.chat.unreadCount; |
|
|
|
} |
|
|
|
}, |
|
|
|
watch: { |
|
|
|
chat: { |
|
|
|
handler(newChat, oldChat) { |
|
|
|
if (newChat.targetId > 0 && (!oldChat || newChat.type != oldChat.type || newChat.targetId != oldChat.targetId)) { |
|
|
|
if (newChat.targetId > 0 && (!oldChat || newChat.type != oldChat.type || |
|
|
|
newChat.targetId != oldChat.targetId)) { |
|
|
|
if (this.chat.type == "GROUP") { |
|
|
|
this.loadGroup(this.chat.targetId); |
|
|
|
} else { |
|
|
|
this.loadFriend(this.chat.targetId); |
|
|
|
} |
|
|
|
// 滚到底部 |
|
|
|
this.scrollToBottom(); |
|
|
|
this.sendText = ""; |
|
|
|
// 消息已读 |
|
|
|
this.readedMessage() |
|
|
|
// 初始状态只显示30条消息 |
|
|
|
let size = this.chat.messages.length; |
|
|
|
this.showMinIdx = size > 30 ? size - 30 : 0; |
|
|
|
// 保持输入框焦点 |
|
|
|
this.$nextTick(() => { |
|
|
|
this.$refs.sendBox.focus(); |
|
|
|
@ -423,9 +524,21 @@ |
|
|
|
} |
|
|
|
}, |
|
|
|
immediate: true |
|
|
|
}, |
|
|
|
unreadCount: { |
|
|
|
handler(newCount, oldCount) { |
|
|
|
if (newCount > 0) { |
|
|
|
// 消息已读 |
|
|
|
this.readedMessage() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
mounted() { |
|
|
|
let div = document.getElementById("chatScrollBox"); |
|
|
|
div.addEventListener('scroll', this.handleScroll) |
|
|
|
} |
|
|
|
} |
|
|
|
</script> |
|
|
|
|
|
|
|
<style lang="scss"> |
|
|
|
@ -498,6 +611,13 @@ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.send-content-area { |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
height: 100%; |
|
|
|
background-color: #f8f8f8 !important; |
|
|
|
outline-color: rgba(83, 160, 231, 0.61); |
|
|
|
|
|
|
|
.send-text-area { |
|
|
|
box-sizing: border-box; |
|
|
|
padding: 5px; |
|
|
|
@ -508,12 +628,48 @@ |
|
|
|
color: black; |
|
|
|
background-color: #f8f8f8 !important; |
|
|
|
outline-color: rgba(83, 160, 231, 0.61); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
.send-image-area { |
|
|
|
text-align: left; |
|
|
|
|
|
|
|
.send-image-box { |
|
|
|
position: relative; |
|
|
|
display: inline-block; |
|
|
|
|
|
|
|
.send-image { |
|
|
|
max-height: 190px; |
|
|
|
border: 1px solid #ccc; |
|
|
|
border-radius: 2%; |
|
|
|
margin: 2px; |
|
|
|
} |
|
|
|
|
|
|
|
.im-chat-send { |
|
|
|
text-align: right; |
|
|
|
padding: 7px; |
|
|
|
.send-image-close { |
|
|
|
position: absolute; |
|
|
|
padding: 3px; |
|
|
|
right: 7px; |
|
|
|
top: 7px; |
|
|
|
color: white; |
|
|
|
cursor: pointer; |
|
|
|
font-size: 15px; |
|
|
|
font-weight: 600; |
|
|
|
background-color: #aaa; |
|
|
|
border-radius: 50%; |
|
|
|
border: 1px solid #ccc; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
.send-btn-area { |
|
|
|
padding: 10px; |
|
|
|
position: absolute; |
|
|
|
bottom: 0; |
|
|
|
right: 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
.chat-group-side-box { |
|
|
|
|