Browse Source

群聊@功能(开发中)

master
xsx 2 years ago
parent
commit
4cc8c1d0f3
  1. 14
      im-ui/src/api/emotion.js
  2. 1
      im-ui/src/api/wssocket.js
  3. 162
      im-ui/src/components/chat/ChatAtBox.vue
  4. 402
      im-ui/src/components/chat/ChatBox.vue
  5. 58
      im-ui/src/components/common/Emotion.vue

14
im-ui/src/api/emotion.js

@ -19,12 +19,22 @@ let textToImg = (emoText) => {
return emoText; return emoText;
} }
let url = require(`@/assets/emoji/${idx}.gif`); let url = require(`@/assets/emoji/${idx}.gif`);
return `<img src="${url}" style="width:40px;height:40px;vertical-align:bottom;"/>` return `<img src="${url}" style="width:35px;height:35px;vertical-align:bottom;"/>`
} }
let textToUrl = (emoText) => {
let word = emoText.replace(/\#|\;/gi, '');
let idx = emoTextList.indexOf(word);
if(idx==-1){
return "";
}
let url = require(`@/assets/emoji/${idx}.gif`);
return url;
}
export default { export default {
emoTextList, emoTextList,
transform, transform,
textToImg textToImg,
textToUrl
} }

1
im-ui/src/api/wssocket.js

@ -20,7 +20,6 @@ let connect = (wsurl,accessToken) => {
} else if (sendInfo.cmd == 1) { } else if (sendInfo.cmd == 1) {
// 重新开启心跳定时 // 重新开启心跳定时
heartCheck.reset(); heartCheck.reset();
console.log("")
} else { } else {
// 其他消息转发出去 // 其他消息转发出去
console.log("收到消息:",sendInfo); console.log("收到消息:",sendInfo);

162
im-ui/src/components/chat/ChatAtBox.vue

@ -0,0 +1,162 @@
<template>
<el-scrollbar v-show="show" ref="scrollBox" class="group-member-choose"
:style="{'left':pos.x+'px','top':pos.y-300+'px'}">
<div v-for="(member,idx) in showMembers" :key="member.id">
<div class="member-item"
:class="idx==activeIdx?'active':''" @click="onSelectMember(member)">
<div class="member-avatar">
<head-image :size="30" :name="member.aliasName" :url="member.headImage"> </head-image>
</div>
<div class="member-name">
<div>{{member.aliasName}}</div>
</div>
</div>
</div>
</el-scrollbar>
</template>
<script>
import HeadImage from '../common/HeadImage.vue';
export default {
name: "chatAtBox",
components: {
HeadImage
},
props: {
searchText: {
type: String,
default: ""
},
members: {
type: Array
}
},
data() {
return {
show: false,
pos: {
x: 0,
y: 0
},
activeIdx: 0,
showMembers:[]
};
},
methods: {
init(){
this.activeIdx = 0;
this.$refs.scrollBox.wrap.scrollTop = 0;
this.showMembers=[];
this.members.forEach((m)=>{
if(m.aliasName.startsWith(this.searchText)){
this.showMembers.push(m);
}
})
},
open(pos) {
this.show = true;
this.pos = pos;
this.init();
},
close() {
this.show = false;
},
moveUp() {
if (this.activeIdx > 0) {
this.activeIdx--;
this.scrollToActive()
}
},
moveDown() {
console.log(this.activeIdx)
if (this.activeIdx < this.showMembers.length - 1) {
this.activeIdx++;
this.scrollToActive()
}
},
select() {
this.onSelectMember(this.showMembers[this.activeIdx])
},
scrollToActive() {
console.log(this.$refs.scrollBox.wrap)
if (this.activeIdx * 35 - this.$refs.scrollBox.wrap.clientHeight > this.$refs.scrollBox.wrap.scrollTop) {
this.$refs.scrollBox.wrap.scrollTop += 140;
if (this.$refs.scrollBox.wrap.scrollTop > this.$refs.scrollBox.wrap.scrollHeight) {
this.$refs.scrollBox.wrap.scrollTop = this.$refs.scrollBox.wrap.scrollHeight
}
}
if (this.activeIdx * 35 < this.$refs.scrollBox.wrap.scrollTop) {
this.$refs.scrollBox.wrap.scrollTop -= 140;
if (this.$refs.scrollBox.wrap.scrollTop < 0) {
this.$refs.scrollBox.wrap.scrollTop = 0;
}
}
},
onSelectMember(member) {
this.$emit("select", member);
this.show = false;
}
},
watch: {
searchText: {
handler(newText, oldText) {
this.init();
}
}
}
}
</script>
<style scoped lang="scss">
.group-member-choose {
position: fixed;
width: 200px;
height: 300px;
border: 1px solid #b4b4b4;
border-radius: 5px;
background-color: #f5f5f5;
box-shadow: 0px 0px 10px #ccc;
.member-item {
display: flex;
height: 35px;
margin-bottom: 1px;
position: relative;
padding-left: 10px;
align-items: center;
padding-right: 5px;
background-color: #fafafa;
white-space: nowrap;
&:hover {
background-color: #eeeeee;
}
&.active {
background-color: #eeeeee;
}
.member-avatar {
width: 30px;
height: 30px;
}
.member-name {
padding-left: 10px;
height: 100%;
text-align: left;
line-height: 35px;
white-space: nowrap;
overflow: hidden;
font-size: 14px;
font-weight: 600;
}
}
}
</style>

402
im-ui/src/components/chat/ChatBox.vue

@ -1,79 +1,88 @@
<template> <template>
<el-container class="chat-box"> <div class="chat-box" @click="closeRefBox()">
<el-header height="60px"> <el-container>
<span>{{title}}</span> <el-header height="60px">
<span title="群聊信息" v-show="this.chat.type=='GROUP'" class="btn-side el-icon-more" <span>{{title}}</span>
@click="showSide=!showSide"></span> <span title="群聊信息" v-show="this.chat.type=='GROUP'" class="btn-side el-icon-more"
</el-header> @click="showSide=!showSide"></span>
<el-main style="padding: 0;"> </el-header>
<el-container> <el-main style="padding: 0;">
<el-container class="content-box"> <el-container>
<el-main class="im-chat-main" id="chatScrollBox" @scroll="handleScroll"> <el-container class="content-box">
<div class="im-chat-box"> <el-main class="im-chat-main" id="chatScrollBox" @scroll="onScroll">
<ul> <div class="im-chat-box">
<li v-for="(msgInfo,idx) in chat.messages" :key="idx"> <ul>
<chat-message-item v-show="idx>=showMinIdx" :mine="msgInfo.sendId == mine.id" :headImage="headImage(msgInfo)" <li v-for="(msgInfo,idx) in chat.messages" :key="idx">
:showName="showName(msgInfo)" :msgInfo="msgInfo" @delete="deleteMessage" <chat-message-item v-show="idx>=showMinIdx" :mine="msgInfo.sendId == mine.id"
@recall="recallMessage"> :headImage="headImage(msgInfo)" :showName="showName(msgInfo)"
</chat-message-item> :msgInfo="msgInfo" @delete="deleteMessage" @recall="recallMessage">
</li> </chat-message-item>
</ul> </li>
</div> </ul>
</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> </div>
<div title="发送图片"> </el-main>
<file-upload :action="imageAction" :maxSize="5*1024*1024" <el-footer height="240px" class="im-chat-footer">
:fileTypes="['image/jpeg', 'image/png', 'image/jpg', 'image/webp','image/gif']" <div class="chat-tool-bar">
@before="handleImageBefore" @success="handleImageSuccess" @fail="handleImageFail"> <div title="表情" class="icon iconfont icon-biaoqing" ref="emotion"
<i class="el-icon-picture-outline"></i> @click.stop="showEmotionBox()">
</file-upload> </div>
</div> <div title="发送图片">
<div title="发送文件"> <file-upload :action="imageAction" :maxSize="5*1024*1024"
<file-upload :action="fileAction" :maxSize="10*1024*1024" @before="handleFileBefore" :fileTypes="['image/jpeg', 'image/png', 'image/jpg', 'image/webp','image/gif']"
@success="handleFileSuccess" @fail="handleFileFail"> @before="onImageBefore" @success="onImageSuccess"
<i class="el-icon-wallet"></i> @fail="onImageFail">
</file-upload> <i class="el-icon-picture-outline"></i>
</div> </file-upload>
<div title="发送语音" class="el-icon-microphone" @click="showVoiceBox()"> </div>
</div> <div title="发送文件">
<div title="视频聊天" v-show="chat.type=='PRIVATE'" class="el-icon-phone-outline" <file-upload :action="fileAction" :maxSize="10*1024*1024" @before="onFileBefore"
@click="showVideoBox()"> @success="onFileSuccess" @fail="onFileFail">
</div> <i class="el-icon-wallet"></i>
<div title="聊天记录" class="el-icon-chat-dot-round" @click="showHistoryBox()"></div> </file-upload>
</div> </div>
<div class="send-content-area"> <div title="发送语音" class="el-icon-microphone" @click="showVoiceBox()">
<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 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> </div>
<div class="send-btn-area"> <div class="send-content-area">
<el-button type="primary" size="small" @click="handleSendMessage()">发送</el-button> <div contenteditable="true" v-show="!sendImageUrl" ref="editBox" class="send-text-area"
:disabled="lockMessage" @paste.prevent="onEditorPaste"
@compositionstart="onEditorCompositionStart"
@compositionend="onEditorCompositionEnd" @input="onEditorInput"
placeholder="温馨提示:可以粘贴截图到这里了哦~" @blur="onEditBoxBlur()"
@keydown.down="onKeyDown" @keydown.up="onKeyUp" @keydown.enter.prevent="onKeyEnter">
</div>
<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="sendMessage()">发送</el-button>
</div>
</div> </div>
</div> </el-footer>
</el-footer> </el-container>
<el-aside class="chat-group-side-box" width="300px" v-show="showSide">
<chat-group-side :group="group" :groupMembers="groupMembers" @reload="loadGroup(group.id)">
</chat-group-side>
</el-aside>
</el-container> </el-container>
<el-aside class="chat-group-side-box" width="300px" v-show="showSide"> </el-main>
<chat-group-side :group="group" :groupMembers="groupMembers" @reload="loadGroup(group.id)"> <emotion ref="emoBox" @emotion="onEmotion"></Emotion>
</chat-group-side> <chat-at-box ref="atBox" :members="groupMembers" :search-text="atSearchText"
</el-aside> @select="onAtSelect"></chat-at-box>
</el-container> <chat-voice :visible="showVoice" @close="closeVoiceBox" @send="onSendVoice"></chat-voice>
</el-main> <chat-history :visible="showHistory" :chat="chat" :friend="friend" :group="group"
<emotion v-show="showEmotion" :pos="emoBoxPos" @emotion="handleEmotion"></Emotion> :groupMembers="groupMembers" @close="closeHistoryBox"></chat-history>
<chat-voice :visible="showVoice" @close="closeVoiceBox" @send="handleSendVoice"></chat-voice> </el-container>
<chat-history :visible="showHistory" :chat="chat" :friend="friend" :group="group" :groupMembers="groupMembers" </div>
@close="closeHistoryBox"></chat-history>
</el-container>
</template> </template>
<script> <script>
@ -83,6 +92,7 @@
import Emotion from "../common/Emotion.vue"; import Emotion from "../common/Emotion.vue";
import ChatVoice from "./ChatVoice.vue"; import ChatVoice from "./ChatVoice.vue";
import ChatHistory from "./ChatHistory.vue"; import ChatHistory from "./ChatHistory.vue";
import ChatAtBox from "./ChatAtBox.vue"
export default { export default {
name: "chatPrivate", name: "chatPrivate",
@ -92,7 +102,8 @@
ChatGroupSide, ChatGroupSide,
Emotion, Emotion,
ChatVoice, ChatVoice,
ChatHistory ChatHistory,
ChatAtBox
}, },
props: { props: {
chat: { chat: {
@ -104,28 +115,142 @@
friend: {}, friend: {},
group: {}, group: {},
groupMembers: [], groupMembers: [],
sendText: "",
sendImageUrl: "", sendImageUrl: "",
sendImageFile: "", sendImageFile: "",
showVoice: false, // showVoice: false, //
showSide: false, // showSide: false, //
showEmotion: false, // emoji
emoBoxPos: { // emoji
x: 0,
y: 0
},
showHistory: false, // showHistory: false, //
lockMessage: false, // lockMessage: false, //
showMinIdx: 0 // showMinIdx showMinIdx: 0, // showMinIdx
atSearchText: "",
focusNode: null, //
focusOffset: null, //
zhLock: false //
} }
}, },
methods: { methods: {
handlePaste(e) { closeRefBox() {
this.$refs.emoBox.close();
this.$refs.atBox.close();
},
onKeyDown() {
if (this.$refs.atBox.show) {
this.$refs.atBox.moveDown()
}
},
onKeyUp() {
if (this.$refs.atBox.show) {
this.$refs.atBox.moveUp()
}
},
onKeyEnter() {
if (this.$refs.atBox.show) {
// ,
this.focusOffset += this.atSearchText.length;
this.$refs.atBox.select();
} else {
this.sendMessage();
}
},
onEditBoxBlur() {
let selection = window.getSelection()
//
this.focusNode = selection.focusNode;
this.focusOffset = selection.focusOffset;
},
onEditorInput(e) {
// @
if (this.chat.type == "GROUP" && !this.zhLock) {
if (e.data == '@') {
//
this.showAtBox(e);
} else {
let selection = window.getSelection()
let range = selection.getRangeAt(0)
this.focusNode = selection.focusNode;
// @
let stIdx = this.focusNode.textContent.lastIndexOf('@');
this.atSearchText = this.focusNode.textContent.substring(stIdx + 1);
}
}
},
onEditorCompositionStart() {
this.zhLock = true;
},
onEditorCompositionEnd(e) {
this.zhLock = false;
this.onEditorInput(e);
},
showAtBox(e) {
this.atSearchText = "";
let selection = window.getSelection()
let range = selection.getRangeAt(0)
//
this.focusNode = selection.focusNode;
this.focusOffset = selection.focusOffset;
//
let pos = range.getBoundingClientRect();
this.$refs.atBox.open({
x: pos.x,
y: pos.y
})
},
onAtSelect(member) {
let range = window.getSelection().getRangeAt(0)
// @xx
range.setStart(this.focusNode, this.focusOffset - 1 -this.atSearchText.length)
range.setEnd(this.focusNode, this.focusOffset)
range.deleteContents()
//
let element = document.createElement('SPAN')
element.className = "at"
element.dataset.id = member.userId;
element.contentEditable = 'false'
element.innerText = `@${member.aliasName}`
range.insertNode(element)
//
range.collapse()
//
let textNode = document.createTextNode('\u00A0');
range.insertNode(textNode)
range.collapse()
this.atSearchText = "";
this.$refs.editBox.focus()
},
createSendText() {
let sendText = ""
this.$refs.editBox.childNodes.forEach((node) => {
if (node.nodeName == "#text") {
sendText += node.textContent;
} else if (node.nodeName == "SPAN") {
sendText += node.innerText;
} else if (node.nodeName == "IMG") {
sendText += node.dataset.code;
}
})
return sendText;
},
createAtUserIds() {
let ids = [];
this.$refs.editBox.childNodes.forEach((node) => {
if (node.nodeName == "SPAN") {
ids.push(node.dataset.id);
}
})
return ids;
},
onEditorPaste(e) {
let txt = event.clipboardData.getData('Text') let txt = event.clipboardData.getData('Text')
if (typeof(txt) == 'string') { if (typeof(txt) == 'string') {
this.sendText += txt let range = window.getSelection().getRangeAt(0)
let textNode = document.createTextNode(txt);
range.insertNode(textNode)
range.collapse();
} }
const items = (event.clipboardData || window.clipboardData).items let items = (event.clipboardData || window.clipboardData).items
if (items.length) { if (items.length) {
for (let i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) { if (items[i].type.indexOf('image') !== -1) {
@ -140,7 +265,7 @@
this.sendImageUrl = ""; this.sendImageUrl = "";
this.sendImageFile = null; this.sendImageFile = null;
}, },
handleImageSuccess(data, file) { onImageSuccess(data, file) {
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo || file.raw.msgInfo)); let msgInfo = JSON.parse(JSON.stringify(file.msgInfo || file.raw.msgInfo));
msgInfo.content = JSON.stringify(data); msgInfo.content = JSON.stringify(data);
this.$http({ this.$http({
@ -153,12 +278,12 @@
this.$store.commit("insertMessage", msgInfo); this.$store.commit("insertMessage", msgInfo);
}) })
}, },
handleImageFail(e, file) { onImageFail(e, file) {
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo || file.raw.msgInfo)); let msgInfo = JSON.parse(JSON.stringify(file.msgInfo || file.raw.msgInfo));
msgInfo.loadStatus = 'fail'; msgInfo.loadStatus = 'fail';
this.$store.commit("insertMessage", msgInfo); this.$store.commit("insertMessage", msgInfo);
}, },
handleImageBefore(file) { onImageBefore(file) {
let url = URL.createObjectURL(file); let url = URL.createObjectURL(file);
let data = { let data = {
originUrl: url, originUrl: url,
@ -184,7 +309,7 @@
// file // file
file.msgInfo = msgInfo; file.msgInfo = msgInfo;
}, },
handleFileSuccess(url, file) { onFileSuccess(url, file) {
let data = { let data = {
name: file.name, name: file.name,
size: file.size, size: file.size,
@ -202,13 +327,12 @@
this.$store.commit("insertMessage", msgInfo); this.$store.commit("insertMessage", msgInfo);
}) })
}, },
handleFileFail(e, file) { onFileFail(e, file) {
let msgInfo = JSON.parse(JSON.stringify(file.raw.msgInfo)); let msgInfo = JSON.parse(JSON.stringify(file.raw.msgInfo));
msgInfo.loadStatus = 'fail'; msgInfo.loadStatus = 'fail';
this.$store.commit("insertMessage", msgInfo); this.$store.commit("insertMessage", msgInfo);
}, },
handleFileBefore(file) { onFileBefore(file) {
let url = URL.createObjectURL(file); let url = URL.createObjectURL(file);
let data = { let data = {
name: file.name, name: file.name,
@ -234,34 +358,47 @@
// file // file
file.msgInfo = msgInfo; file.msgInfo = msgInfo;
}, },
handleCloseSide() { onCloseSide() {
this.showSide = false; this.showSide = false;
}, },
handleScrollToTop() { onScrollToTop() {
// 10 // 10
this.showMinIdx = this.showMinIdx > 10 ? this.showMinIdx - 10 : 0; this.showMinIdx = this.showMinIdx > 10 ? this.showMinIdx - 10 : 0;
}, },
handleScroll(e) { onScroll(e) {
let scrollElement = e.target let scrollElement = e.target
let scrollTop = scrollElement.scrollTop let scrollTop = scrollElement.scrollTop
if (scrollTop < 30 ) { // , if (scrollTop < 30) { // ,
// 20 // 20
this.showMinIdx = this.showMinIdx > 20 ? this.showMinIdx - 20 : 0; this.showMinIdx = this.showMinIdx > 20 ? this.showMinIdx - 20 : 0;
} }
}, },
switchEmotionBox() { showEmotionBox() {
this.showEmotion = !this.showEmotion;
let width = this.$refs.emotion.offsetWidth; let width = this.$refs.emotion.offsetWidth;
let left = this.$elm.fixLeft(this.$refs.emotion); let left = this.$elm.fixLeft(this.$refs.emotion);
let top = this.$elm.fixTop(this.$refs.emotion); let top = this.$elm.fixTop(this.$refs.emotion);
this.emoBoxPos.y = top; this.$refs.emoBox.open({
this.emoBoxPos.x = left + width / 2; x: left + width / 2,
y: top
})
}, },
handleEmotion(emoText) { onEmotion(emoText) {
this.sendText += emoText;
this.showEmotion = false;
// //
this.$refs.sendBox.focus(); this.$refs.editBox.focus();
let range = window.getSelection().getRangeAt(0);
//
range.setStart(this.focusNode, this.focusOffset)
let element = document.createElement('IMG')
element.className = "emo"
element.dataset.code = emoText;
element.contentEditable = 'true'
element.setAttribute("src", this.$emo.textToUrl(emoText));
//
range.insertNode(element)
//
range.collapse()
}, },
showVoiceBox() { showVoiceBox() {
this.showVoice = true; this.showVoice = true;
@ -281,7 +418,7 @@
closeHistoryBox() { closeHistoryBox() {
this.showHistory = false; this.showHistory = false;
}, },
handleSendVoice(data) { onSendVoice(data) {
let msgInfo = { let msgInfo = {
content: JSON.stringify(data), content: JSON.stringify(data),
type: 3 type: 3
@ -300,7 +437,7 @@
msgInfo.status = this.$enums.MESSAGE_STATUS.UNSEND; msgInfo.status = this.$enums.MESSAGE_STATUS.UNSEND;
this.$store.commit("insertMessage", msgInfo); this.$store.commit("insertMessage", msgInfo);
// //
this.$refs.sendBox.focus(); this.$refs.editBox.focus();
// //
this.scrollToBottom(); this.scrollToBottom();
// //
@ -314,7 +451,7 @@
msgInfo.recvId = targetId; msgInfo.recvId = targetId;
} }
}, },
handleSendMessage() { sendMessage() {
if (this.sendImageFile) { if (this.sendImageFile) {
this.sendImageMessage(); this.sendImageMessage();
} else { } else {
@ -323,7 +460,7 @@
}, },
sendImageMessage() { sendImageMessage() {
let file = this.sendImageFile; let file = this.sendImageFile;
this.handleImageBefore(this.sendImageFile); this.onImageBefore(this.sendImageFile);
let formData = new FormData() let formData = new FormData()
formData.append('file', file.raw || file) formData.append('file', file.raw || file)
this.$http.post("/image/upload", formData, { this.$http.post("/image/upload", formData, {
@ -331,21 +468,25 @@
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data'
} }
}).then((data) => { }).then((data) => {
this.handleImageSuccess(data, file); this.onImageSuccess(data, file);
}).catch((res) => { }).catch((res) => {
this.handleImageSuccess(res, file); this.onImageSuccess(res, file);
}) })
this.sendImageFile = null; this.sendImageFile = null;
this.sendImageUrl = ""; this.sendImageUrl = "";
this.$nextTick(() => this.$refs.sendBox.focus()); this.$nextTick(() => this.$refs.editBox.focus());
this.scrollToBottom(); this.scrollToBottom();
}, },
sendTextMessage() { sendTextMessage() {
if (!this.sendText.trim()) { let sendText = this.createSendText();
//
this.$refs.editBox.innerHTML = "";
if (!sendText.trim()) {
return return
} }
this.$refs.editBox.cle
let msgInfo = { let msgInfo = {
content: this.sendText, content: sendText,
type: 0 type: 0
} }
// id // id
@ -356,7 +497,6 @@
method: 'post', method: 'post',
data: msgInfo data: msgInfo
}).then((id) => { }).then((id) => {
this.sendText = "";
msgInfo.id = id; msgInfo.id = id;
msgInfo.sendTime = new Date().getTime(); msgInfo.sendTime = new Date().getTime();
msgInfo.sendId = this.$store.state.userStore.userInfo.id; msgInfo.sendId = this.$store.state.userStore.userInfo.id;
@ -366,17 +506,10 @@
}).finally(() => { }).finally(() => {
// //
this.lockMessage = false; this.lockMessage = false;
//
this.$nextTick(() => this.$refs.sendBox.focus());
//
this.scrollToBottom(); this.scrollToBottom();
this.resetEditor();
}); });
const e = window.event || arguments[0];
if (e.key === 'Enter' || e.code === 'Enter' || e.keyCode === 13) {
e.returnValue = false;
e.preventDefault();
return false;
}
}, },
deleteMessage(msgInfo) { deleteMessage(msgInfo) {
this.$confirm('确认删除消息?', '删除消息', { this.$confirm('确认删除消息?', '删除消息', {
@ -467,6 +600,13 @@
return msgInfo.sendId == this.mine.id ? this.mine.headImageThumb : this.chat.headImage return msgInfo.sendId == this.mine.id ? this.mine.headImageThumb : this.chat.headImage
} }
}, },
resetEditor(){
this.sendImageUrl = "";
this.sendImageFile = null;
this.$refs.editBox.innerHTML = "";
this.$refs.editBox.foucs();
},
scrollToBottom() { scrollToBottom() {
this.$nextTick(() => { this.$nextTick(() => {
let div = document.getElementById("chatScrollBox"); let div = document.getElementById("chatScrollBox");
@ -511,18 +651,14 @@
} }
// //
this.scrollToBottom(); this.scrollToBottom();
this.sendText = "";
this.showSide = false; this.showSide = false;
// //
this.readedMessage() this.readedMessage()
// 30 // 30
let size = this.chat.messages.length; let size = this.chat.messages.length;
this.showMinIdx = size > 30 ? size - 30 : 0; this.showMinIdx = size > 30 ? size - 30 : 0;
//
// this.resetEditor();
this.$nextTick(() => {
this.$refs.sendBox.focus();
})
} }
}, },
immediate: true immediate: true
@ -538,13 +674,15 @@
}, },
mounted() { mounted() {
let div = document.getElementById("chatScrollBox"); let div = document.getElementById("chatScrollBox");
div.addEventListener('scroll', this.handleScroll) div.addEventListener('scroll', this.onScroll)
} }
} }
</script> </script>
<style lang="scss"> <style lang="scss">
.chat-box { .chat-box {
position: relative;
width: 100%;
background: white; background: white;
border: #dddddd solid 1px; border: #dddddd solid 1px;
@ -631,7 +769,19 @@
color: black; color: black;
background-color: #f8f8f8 !important; background-color: #f8f8f8 !important;
outline-color: rgba(83, 160, 231, 0.61); outline-color: rgba(83, 160, 231, 0.61);
text-align: left;
line-height: 30 px;
.at {
color: blue;
font-weight: 600;
}
.emo {
width: 30px;
height: 30px;
vertical-align: bottom;
}
} }
.send-image-area { .send-image-area {

58
im-ui/src/components/common/Emotion.vue

@ -1,9 +1,10 @@
<template> <template>
<div class="emotion-mask" @click="$emit('emotion','')"> <div v-show="show" @click="close()">
<div class="emotion-box" :style="{'left':x+'px','top':y+'px'}"> <div class="emotion-box" :style="{'left':x+'px','top':y+'px'}">
<el-scrollbar style="height:250px"> <el-scrollbar style="height:250px">
<div class="emotion-item-list"> <div class="emotion-item-list">
<div class="emotion-item" v-for="(emoText, i) in $emo.emoTextList" :key="i" @click="clickHandler(emoText)" v-html="$emo.textToImg(emoText)"> <div class="emotion-item" v-for="(emoText, i) in $emo.emoTextList" :key="i"
@click="clickHandler(emoText)" v-html="$emo.textToImg(emoText)">
</div> </div>
</div> </div>
</el-scrollbar> </el-scrollbar>
@ -14,18 +15,26 @@
<script> <script>
export default { export default {
name: "emotion", name: "emotion",
props: {
pos: {
type: Object
}
},
data() { data() {
return {} return {
show: false,
pos: {
x: 0,
y: 0
}
}
}, },
methods: { methods: {
clickHandler(emoText) { clickHandler(emoText) {
let emotion = `#${emoText};` let emotion = `#${emoText};`
this.$emit('emotion', emotion) this.$emit('emotion', emotion)
},
open(pos) {
this.pos = pos;
this.show = true;
},
close() {
this.show = false;
} }
}, },
computed: { computed: {
@ -39,15 +48,7 @@
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.emotion-mask {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
}
.emotion-box { .emotion-box {
position: fixed; position: fixed;
@ -57,7 +58,8 @@
border: 1px solid #b4b4b4; border: 1px solid #b4b4b4;
border-radius: 5px; border-radius: 5px;
background-color: #f5f5f5; background-color: #f5f5f5;
box-shadow: 0px 0px 10px #ccc; box-shadow: 0px 0px 10px #ccc;
.emotion-item-list { .emotion-item-list {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@ -71,16 +73,16 @@
} }
&:after { &:after {
content: ""; content: "";
position: absolute; position: absolute;
left: 185px; left: 185px;
bottom: -30px; bottom: -30px;
width: 0; width: 0;
height: 0; height: 0;
border-style: solid dashed dashed; border-style: solid dashed dashed;
border-color: #f5f5f5 transparent transparent; border-color: #f5f5f5 transparent transparent;
overflow: hidden; overflow: hidden;
border-width: 15px; border-width: 15px;
} }
} }
</style> </style>
Loading…
Cancel
Save