谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议根进行交互的接口
[1] 。
正文开始:
socket中使用了VUEX如果不打算使用状态管理器,可以忽略下文中的某些配置。
在vue中我们使用:vue-native-websocket
第一步安装:
npm install vue-native-websocket --save
然后在main.js中进行全局注册:
Vue.use(VueNativeSock,"后台地址*****",{
// 启用Vuex集成
store: store,
// 数据发送/接收使用使用json
format: "json",
connectManually: true,
reconnection: true,
// 尝试重连的次数
reconnectionAttempts: 5,
// 重连间隔时间
reconnectionDelay: 3000,
passToStoreHandler: function (eventName, event) {
if (!eventName.startsWith('SOCKET_')) { return }
let method = 'commit';
let target = eventName.toUpperCase();
let msg = event;
if (this.format === 'json' && event.data) {
msg = JSON.parse(event.data);
if (msg.mutation) {
target = [msg.namespace || '', msg.mutation].filter((e) => !!e).join('/');
} else if (msg.action) {
method = 'dispatch';
target = [msg.namespace || '', msg.action].filter((e) => !!e).join('/');
}
}
this.store[method](target, msg);
this.store.state.socket.message = msg;
}
});
第三部写html:
在一个.vue结尾的文件中写入一下代码:
<!--公用组件:消息内容展示,实现群聊和单聊业务-->
<template>
<div id="mainContent" >
<div class="top-panel" ref="topPanel">
<div class="title-panel">
<!-- <p>当前在线人数: {{onlineUsers}}</p> -->
</div>
</div>
<!--消息显示-->
<div class="messages-panel" ref="messagesContainer">
<div class="row-panel" v-for="(item,index) of senderMessageList" :key="index">
<!--发送者消息样式-->
<div class="sender-panel" v-if="item.userID == userID">
<!--昵称展示-->
<div class="user-name-panel sender">
<p>{{item.username}}</p>
</div>
<!--消息-->
<div class="msg-body">
<!--消息尾巴-->
<div class="tail-panel">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-zbds30duihuakuangyou" color="#dce7dc"></use>
</svg>
</div>
<!--消息内容-->
<p v-html="item.msgText" @click="viewLargerImage($event)" ref="comment"/>
</div>
<!--头像-->
<div class="avatar-panel">
<img :src="item.avatarSrc" alt="">
</div>
</div>
<!--对方消息样式-->
<div class="otherSide-panel" v-else>
<!--头像-->
<div class="avatar-panel">
<img :src="item.avatarSrc" alt="">
</div>
<!--昵称展示-->
<div class="user-name-panel sender">
<p>{{item.username}}</p>
</div>
<!--消息-->
<div class="msg-body">
<!--消息尾巴-->
<div class="tail-panel">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-zbds30duihuakuangzuo"></use>
</svg>
</div>
<!--消息内容-->
<p v-html="item.msgText" @click="viewLargerImage($event)" ref="comment" />
</div>
</div>
</div>
</div>
<!--用户输入模块-->
<div class="user-input-panel" @click="getEditableDivFocus()">
<div class="toolbar-panel">
<div class="item-panel" v-for="(item ,index) of toolbarList" :key="index">
<img class="emoticon" :src="require(`../assets/img/${item.src}`)"
@mouseenter="toolbarSwitch('hover',$event,item.src,item.hover,item.down,item.name)"
@mouseleave="toolbarSwitch('leave',$event,item.src,item.hover,item.down,item.name)"
@mousedown="toolbarSwitch('down',$event,item.src,item.hover,item.down,item.name)"
@mouseup="toolbarSwitch('up',$event,item.src,item.hover,item.down,item.name)" :alt="item.info">
</div>
</div>
<div id="msgInputContainer" class="input-panel" ref="msgInputContainer" @keydown.enter.exact="sendMessage($event)"
contenteditable="true" spellcheck="false">
</div>
<div class="send-panel" ref="sendPanel" @click="mobileSend()">
<p>发送</p>
</div>
<!--表情面板-->
<div class="emoticon-panel" :style="{display: emoticonShowStatus}" ref="emoticonPanel">
<div class="row-panel">
<div class="item-panel" v-for="(item,index) of this.emojiList" :key="index">
<img :src="require(`../assets/images/emoji/${item.src}`)" :alt="item.info"
@mouseover="emojiConversion($event,'over',item.src,item.hover,item.info)"
@mouseleave="emojiConversion($event,'leave',item.src,item.hover,item.info)"
@click="emojiConversion($event,'click',item.src,item.hover,item.info)">
</div>
</div>
<div class="ico-panel"></div>
</div>
</div>
</div>
</template>
<script src="../assets/js/message-display.js"></script>
<style lang="scss" src="../assets/css/message-display.scss" scoped></style>
style文件:
#mainContent {
width: 100%;
height: 100%;
.top-panel {
width: 100%;
height: 30px;
display: flex;
align-items: center;
border-bottom: 1px solid #cecece;
.title-panel {
width: 70%;
height: 25px;
display: flex;
align-items: center;
.equipmentType {
width: 18px;
height: 18px;
margin-left: 5px;
img {
width: 100%;
height: 100%;
}
}
}
/*操作栏样式:单聊*/
.operate-panel {
width: 29%;
height: 25px;
.ico-panel {
width: 100%;
height: 100%;
display: flex;
justify-content: flex-end;
align-items: center;
.item-panel {
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
}
}
/*操作栏样式:群聊*/
// .operate-group-panel{
// }
}
::-webkit-scrollbar {
width: 2px;
/*滚动条宽度*/
height: 6px;
/*滚动条高度*/
}
.messages-panel {
width: 100%;
min-height: 400px !important;
overflow-y: auto;
max-height: 800px;
overflow-x: hidden;
padding-top: 5px;
padding-bottom: 15px;
.row-panel {
width: 100%;
min-height: 50px;
/*对方消息样式*/
.otherSide-panel {
width: 96%;
min-height: 50px;
display: flex;
margin-bottom: 15px;
position: relative;
.avatar-panel {
width: 30px;
min-width: 30px;
height: 30px;
border-radius: 50%;
overflow: hidden;
img {
width: 100%;
height: 100%;
}
}
.user-name-panel {
width: 240px;
height: 20px;
position: absolute;
left: 42px;
top: 2px;
display: flex;
justify-content: flex-start;
align-items: center;
p {
color: #9da9c6;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.msg-body {
max-width: 95%;
min-height: 40px;
background: #f4f3f3;
border-radius: 5px;
display: flex;
align-items: center;
padding: 10px;
box-sizing: border-box;
margin-top: 28px;
margin-left: 16px;
position: relative;
/*消息尾巴*/
.tail-panel {
width: 20px;
height: 100%;
position: absolute;
left: -10px;
svg {
margin-top: 8px;
color: #f3f3f3;
}
}
p {
font-size: 12px;
/*自动换行*/
word-wrap: break-word;
overflow: hidden;
cursor: pointer;
img {
width: 100%;
height: 100%;
}
}
}
}
/*发送者消息样式*/
.sender-panel {
width: 96%;
min-height: 50px;
float: right;
margin-right: 12px;
display: flex;
justify-content: flex-end;
margin-bottom: 15px;
position: relative;
.avatar-panel {
width: 30px;
min-width: 30px;
height: 30px;
border-radius: 50%;
overflow: hidden;
img {
width: 100%;
height: 100%;
}
}
.user-name-panel {
width: 240px;
height: 20px;
position: absolute;
right: 42px;
top: 2px;
overflow: hidden;
text-overflow: ellipsis;
display: flex;
justify-content: flex-end;
align-items: center;
p {
color: #9da9c6;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.msg-body {
max-width: 95%;
min-height: 40px;
background: #d8e8dc;
border-radius: 5px;
display: flex;
align-items: center;
padding: 10px;
box-sizing: border-box;
margin-top: 28px;
margin-right: 16px;
position: relative;
/*消息尾巴*/
.tail-panel {
width: 20px;
height: 100%;
position: absolute;
right: -18px;
svg {
margin-top: 8px;
color: #f3f3f3;
}
}
p {
font-size: 12px;
line-height: 23px;
// 强制换行
word-break: break-all;
display: flex;
align-items: center;
cursor: pointer;
img {
width: 100%;
height: 100%;
display: block;
}
}
}
}
}
}
.send-panel {
width: 70px;
height: 30px;
background-image: linear-gradient(-90deg, #29bdd9 0%, #276ace 100%);
text-align: center;
color: white;
border-radius: 5px;
line-height: 30px;
cursor: pointer;
float: right;
margin-right: 5px;
}
.user-input-panel {
width: 100%;
height: 160px;
position: relative;
border-top: 1px solid #cecece;
.toolbar-panel {
width: 100%;
height: 40px;
display: flex;
align-items: center;
.item-panel {
width: 24px;
height: 24px;
margin-right: 20px;
display: flex;
justify-content: center;
align-items: center;
img {
width: 100%;
height: 100%;
}
}
}
.input-panel {
width: 100%;
min-height: 30px;
max-height: 120px;
overflow-y: auto;
outline: none;
display: flex;
align-items: center;
flex-flow: row wrap;
// 强制换行
word-break: break-all;
}
/*表情面板*/
.emoticon-panel {
width: 290px;
height: 250px;
border-radius: 5px;
background: white;
border: solid 1px #dfe0e0;
padding: 20px;
box-sizing: border-box;
position: absolute;
top: -260px;
// left: -194px;
display: flex;
justify-content: flex-start;
z-index: 9999999;
.row-panel {
width: 100%;
height: 30px;
display: flex;
align-items: center;
flex-flow: row wrap;
.item-panel {
width: 25px;
height: 25px;
margin-right: 6px;
margin-bottom: 7px;
position: relative;
// 取消12的倍数的元素的右外边距
// &:nth-child(12n){
// margin-right: 0;
// }
img {
width: 100%;
height: 100%;
&:hover {
width: 26px;
height: 26px;
}
}
}
}
.ico-panel {
width: 0;
height: 0;
border-right: 6px solid transparent;
border-left: 6px solid transparent;
border-top: 6px solid #dfe0e0;
position: absolute;
bottom: -6px;
img {
width: 100%;
height: 100%;
}
}
}
}
}
JS:
import emoji from '../json/emoji';
import toolbar from '../json/toolbar';
import lodash from 'lodash';
import base from "./base";
import VueCookies from "vue-cookies";
export default {
name: "message-display",
data() {
return {
danmu: null,
id: '1',
roomid: '',
images: [],
userID: '',
worker:null,
ws:null,
isHide:true,
messagesContainerTimer: "",
onlineUsers: this.$store.state.onlineUsers,
createDisSrc: require("../img/titlebar_function_createDis_normal@2x.png"),
resourceObj: {
createDisNormal: require("../img/titlebar_function_createDis_normal@2x.png"),
createDisHover: require("../img/titlebar_function_createDis_hover@2x.png"),
createDisClick: require("../img/titlebar_function_createDis_normal_p@2x.png"),
phoneNormal: require("../img/phone_normal_ap@2x.png"),
groupMsgImg: require("../img/group-msg-img.png"),
avatarImg: require("../img/avatar.jpg"),
msgImgTest: require("../img/msg-img-test.gif"),
msgImgTestB: require("../img/msg-img-testB.gif"),
},
// 消息内容
messageContent: "",
InputContent: "",
emoticonShowStatus: "none",
emojiList: emoji,
toolbarList: toolbar,
senderMessageList: [],
audioCtx: null,
// 声音频率
arrFrequency: [
196.00, 220.00, 246.94, 261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88, 523.25, 587.33, 659.25, 698.46, 783.99, 880.00, 987.77, 1046.50
],
testWorker: null
}
},
mounted: function () {
let that = this
let uid = this.$cookies.get('u_id')
let course_id = this.$route.query.id
let s_id = this.$cookies.get('s_id')
let tokens = this.$cookies.get('PHPSESSID')
this.$store.commit("changeCourse", {
uid: uid,
s_id: s_id,
name: course_id
});
function work() {
onmessage = ({ data: { jobId, message ,classID} }) => {
postMessage({ jobId, result: message});
};
}
const makeWorker = f => {
let hide = that.isHide
let pendingJobs = {};
let worker = new Worker(
URL.createObjectURL(new Blob([`(${f.toString()})()`])) //Blob = Binary Large Object的缩写,直译为二进制大对象
);
worker.onmessage = ({ data: { result, jobId,classID } }) => {
let ws = new WebSocket('wss://apps.beiqujy.com/count' +`?room_id=${result[0][0]}&uid=${result[0][1]}&type=3&from=2&class_id=${result[1]}`)
ws.onopen = function () {
let login_data = '{"type":3,"from":2,"client_name":"' + '' + '","room_id":"' + result[0][0]+ '","class_id":"' + result[1] + '","uid":"' + result[0][1] + '"}';
console.log(login_data)
ws.send('首次发送消息'+login_data);
};
ws.onmessage = function (evt) {
var received_msg = evt.data;
};
ws.onclose = function () {
if(that.isHide){
reconnect('wss://apps.beiqujy.com/count' +`?room_id=${result[0][0]}&uid=${result[0][1]}&type=3&from=2&class_id=${result[1]}`)
}
};
function reconnect(url) {
var connects
clearTimeout(connects);
connects = setTimeout(function () { //没连接上会一直重连,设置延迟避免请求过多
var ws = new WebSocket(url);
ws.onopen = function () {
let login_data = '{"type":3,"from":2,"client_name":"' + '' + '","room_id":"' + result[0][0]+ '","class_id":"' + result[1] + '","uid":"' + result[0][1] + '"}';
console.log('重连后发送的消息'+login_data)
ws.send(login_data);
};
ws.onmessage = function (evt) {
var received_msgs = evt.data;
};
ws.onclose = function () {
if(that.isHide){
reconnect('wss://apps.beiqujy.com/count' +`?room_id=${result[0][0]}&uid=${result[0][1]}&type=3&from=2&class_id=${result[1]}`)
}
};
}, 5000);
}
// 调用resolve,改变Promise状态
pendingJobs[jobId](result);
// 删掉,防止key冲突
delete pendingJobs[jobId];
};
return (...message) =>
new Promise(resolve => {
const jobId = String(Math.random());
pendingJobs[jobId] = resolve;
worker.postMessage({ jobId, message });
});
};
this.testWorker = makeWorker(work);
this.$nextTick(() => {
this.testWorker([VueCookies.get("roomid"), this.$store.state.uid],this.$route.query.id).then(message => {
});
});
//链接socket
this.$connect(process.env.VUE_APP_SOCKET + `?course_id=${course_id}&uid=${uid}&type='students'&from=2&listen_id=123`);
this.userID = this.$store.state.uid
var i = 0
// webAudioAPI兼容性处理
window.AudioContext = window.AudioContext || window.webkitAudioContext;
// 设置列容器高度
this.$refs.messagesContainer.style.height = this.getThisWindowHeight() - 350 + "px";
// 全局点击事件,点击表情框以外的地方,隐藏当前表情框
document.addEventListener('click', (e) => {
let thisClassName = e.target.className;
if (thisClassName !== "emoticon-panel" && thisClassName !== "emoticon") {
this.emoticonShowStatus = "none";
}
});
//从本地存储中获取数据渲染页面
this.renderPage("", "", 1);
// 监听消息接收
this.$options.sockets.onmessage = (res) => {
const data = JSON.parse(res.data);
console.log(data)
// this.messageChange()
this.$store.commit("changeMessage", {
msg: data.data.message
});
if (data.code == 201 || data.code == 202 ) {
return
} else {
// this.$store.state.onlineUsers = data.onlineUsers;
// 更新在线人数
// this.onlineUsers = data.onlineUsers;
// 获取服务端推送的消息
const msgObj = {
msg: data.data.msg,
avatarSrc: data.data.user_img,
userID: data.data.uid,
username: data.data.user_nicename
};
if (lodash.isEmpty(localStorage.getItem("msgArray"))) {
this.renderPage(JSON.parse(localStorage.getItem("msgArray")), msgObj, 0);
} else {
this.renderPage(JSON.parse(localStorage.getItem("msgArray")), msgObj, 0);
}
}
};
},
beforeDestroy() {
// 页面销毁时,断开连接
localStorage.setItem("msgArray", '[]')
this.isHide = false
this.closeWorker()
this.$disconnect();
},
methods: {
closeWorker() {
// console.log(this.testWorker)
// this.worker.terminate()
this.testWorker=null
},
createDisEventFun: function (status) {
if (status === "hover") {
this.createDisSrc = this.resourceObj.createDisHover
} else if (status === "leave") {
this.createDisSrc = this.resourceObj.createDisNormal
} else {
this.createDisSrc = this.resourceObj.createDisClick
}
},
getThisWindowHeight: () => window.innerHeight,
getThisWindowWidth: () => window.innerWidth,
sendMessage: function (event) {
if (event.keyCode === 13) {
// 阻止编辑框默认生成div事件
event.preventDefault();
let msgText = "";
// 获取输入框下的所有子元素
let allNodes = event.target.childNodes;
for (let item of allNodes) {
// 判断当前元素是否为img元素
if (item.nodeName === "IMG") {
msgText += `/${item.alt}/`;
} else {
// 获取text节点的值
if (item.nodeValue !== null) {
msgText += item.nodeValue;
}
}
}
// 消息发送: 发送文字,为空则不发送
if (msgText.trim().length > 0) {
var obj = {
uid: parseInt( this.$cookies.get('u_id')),
message: msgText,
type:'students',
from:2,
event:1,
listen_id:123,
course_id: parseInt( this.$route.query.id)
}
this.$socket.sendObj(obj)
event.target.innerHTML = "";
}
}
},
mobileSend: function () {
// 模拟触发回车事件
this.fireKeyEvent(this.$refs.msgInputContainer, 'keydown', 13);
},
// 渲染页面
renderPage: function (msgArray, msgObj, status) {
if (status === 1) {
// 页面第一次加载,如果本地存储中有数据则渲染至页面
let msgArray = [];
if (localStorage.getItem("msgArray") !== null) {
msgArray = JSON.parse(localStorage.getItem("msgArray"));
for (let i = 0; i < msgArray.length; i++) {
const thisSenderMessageObj = {
"msgText": msgArray[i].msg,
"msgId": i,
"avatarSrc": msgArray[i].avatarSrc,
"userID": msgArray[i].userID,
"username": msgArray[i].username
};
// 更新消息内容
this.messageContent = msgArray[i].msg;
// 向父组件传值
this.$emit('updateLastMessage', this.messageContent);
// 解析并渲染
this.messageParsing(thisSenderMessageObj);
}
}
} else {
// 判断本地存储中是否有数据
if (localStorage.getItem("msgArray") === null) {
// 新增记录
msgArray.push(msgObj);
// 更新消息内容
this.messageContent = msgObj.msg;
// 向父组件传值
this.$emit('updateLastMessage', this.messageContent);
localStorage.setItem("msgArray", JSON.stringify(msgArray));
for (let i = 0; i < msgArray.length; i++) {
const thisSenderMessageObj = {
"msgText": msgArray[i].msg,
"msgId": i,
"avatarSrc": msgArray[i].avatarSrc,
"userID": msgArray[i].userID,
"username": msgArray[i].username
};
// 解析并渲染
this.messageParsing(thisSenderMessageObj);
}
} else {
// 更新记录
msgArray = JSON.parse(localStorage.getItem("msgArray"));
msgArray.push(msgObj);
localStorage.setItem("msgArray", JSON.stringify(msgArray));
// 更新消息内容
this.messageContent = msgObj.msg;
// 向父组件传值
this.$emit('updateLastMessage', this.messageContent);
const thisSenderMessageObj = {
"msgText": msgObj.msg,
"msgId": Date.now(),
"avatarSrc": msgObj.avatarSrc,
"userID": msgObj.userID,
"username": msgObj.username
};
// 解析并渲染
this.messageParsing(thisSenderMessageObj);
}
}
},
// 模拟触发事件
fireKeyEvent: function (el, evtType, keyCode) {
let doc = el.ownerDocument,
win = doc.defaultView || doc.parentWindow,
evtObj;
if (doc.createEvent) {
if (win.KeyEvent) {
evtObj = doc.createEvent('KeyEvents');
evtObj.initKeyEvent(evtType, true, true, win, false, false, false, false, keyCode, 0);
} else {
evtObj = doc.createEvent('UIEvents');
Object.defineProperty(evtObj, 'keyCode', {
get: function () {
return this.keyCodeVal;
}
});
Object.defineProperty(evtObj, 'which', {
get: function () {
return this.keyCodeVal;
}
});
evtObj.initUIEvent(evtType, true, true, win, 1);
evtObj.keyCodeVal = keyCode;
if (evtObj.keyCode !== keyCode) {
console.log("keyCode " + evtObj.keyCode + " 和 (" + evtObj.which + ") 不匹配");
}
}
el.dispatchEvent(evtObj);
} else if (doc.createEventObject) {
evtObj = doc.createEventObject();
evtObj.keyCode = keyCode;
el.fireEvent('on' + evtType, evtObj);
}
},
// 消息解析
messageParsing: function (msgObj) {
console.log(msgObj)
// 解析接口返回的数据进行渲染
let separateReg = /(\/[^/]+\/)/g;
let msgText = msgObj.msgText;
let finalMsgText = "";
if(msgText && msgText != undefined){
// 将符合条件的字符串放到数组里
const resultArray = msgText.match(separateReg);
if (resultArray !== null) {
for (let item of resultArray) {
// 删除字符串中的/符号
item = item.replace(/\//g, "");
// 判断是否为图片: 后缀为.jpeg
if (this.isImg(item)) {
const imgSrc = `${base.lkBaseURL}/uploads/chatImg/${item}`;
// 获取图片宽高
let imgInfo = {
"imgWidth": this.getQueryVariable(imgSrc, "width"),
"imgHeight": this.getQueryVariable(imgSrc, "height")
};
let thisImgWidth = 0;
let thisImgHeight = 0;
if (imgInfo.imgWidth < 400) {
thisImgWidth = imgInfo.imgWidth;
thisImgHeight = imgInfo.imgHeight;
} else {
// 缩放四倍
thisImgWidth = imgInfo.imgWidth / 4;
thisImgHeight = imgInfo.imgHeight / 4;
}
// 找到item中?位置,在?之前添加\\进行转义,解决正则无法匹配特殊字符问题
const charIndex = item.indexOf("?");
// 生成正则表达式条件,添加\\用于对?的转义
const regularItem = this.insertStr(item, charIndex, "\\");
// 解析为img标签
const imgTag = `<img width="${thisImgWidth}" height="${thisImgHeight}" src="${imgSrc}" alt="聊天图片">`;
// 替换匹配的字符串为img标签:全局替换
msgText = msgText.replace(new RegExp(`/${regularItem}/`, 'g'), imgTag);
}
// 表情渲染: 遍历表情配置文件
for (let emojiItem of this.emojiList) {
// 判断捕获到的字符串与配置文件中的字符串是否相同
if (emojiItem.info === item) {
const imgSrc = require(`../img/emoji/${emojiItem.hover}`);
const imgTag = `<img src="${imgSrc}" width="28" height="28" alt="${item}">`;
// 替换匹配的字符串为img标签:全局替换
msgText = msgText.replace(new RegExp(`/${item}/`, 'g'), imgTag);
}
}
}
finalMsgText = msgText;
} else {
finalMsgText = msgText;
}
}
msgObj.msgText = finalMsgText;
// 渲染页面
this.senderMessageList.push(msgObj);
let hash = {}
this.senderMessageList = this.senderMessageList.reduce((prev, array) => {
if (array.username == undefined) {
hash[array.username] ? '' : hash[array.username] = true && prev.push(array)
} else {
prev.push(array)
}
return prev
}, [])
// 修改滚动条位置
this.$nextTick(function () {
this.$refs.messagesContainer.scrollTop = this.$refs.messagesContainer.scrollHeight;
});
},
// 显示表情
toolbarSwitch: function (status, event, path, hoverPath, downPath, toolItemName) {
if (status === "hover" || status === "up") {
event.target.src = require(`../img/${hoverPath}`);
} else if (status === "leave") {
event.target.src = require(`../img/${path}`);
} else {
// 可编辑div获取焦点
this.getEditableDivFocus();
event.target.src = require(`../img/${downPath}`);
// 表情框显示条件
if (toolItemName === "emoticon") {
if (this.emoticonShowStatus === "flex") {
this.emoticonShowStatus = "none";
} else {
this.emoticonShowStatus = "flex";
}
} else {
this.emoticonShowStatus = "none";
}
}
},
// 判断一个对象是否为函数类型
isFunction: function (obj) {
return typeof obj === "function" && typeof obj.nodeType !== "number";
},
// 表情框鼠标悬浮显示动态表情
emojiConversion: function (event, status, path, hoverPath, info) {
if (status === "over") {
event.target.src = require(`../img/emoji/${hoverPath}`);
} else if (status === "click") {
// 表情输入
const imgSrc = require(`../img/emoji/${hoverPath}`);
const imgTag = `<img src="${imgSrc}" width="28" height="28" alt="${info}">`;
document.execCommand("insertHTML", false, imgTag);
} else {
event.target.src = require(`../img/emoji/${path}`);
}
},
// base64转file
convertBase64UrlToImgFile: function (urlData, fileName, fileType) {
// 转换为byte
let bytes = window.atob(urlData);
// 处理异常,将ascii码小于0的转换为大于0
let ab = new ArrayBuffer(bytes.length);
let ia = new Int8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
// 转换成文件,添加文件的type,name,lastModifiedDate属性
let blob = new Blob([ab], { type: fileType });
blob.lastModifiedDate = new Date();
blob.name = fileName;
return blob;
},
// 判断是否为图片
isImg: function (str) {
return str.indexOf(".jpeg") !== -1;
},
viewLargerImage: function (event) {
const imgSrc = event.target.src;
if (typeof imgSrc !== "undefined") {
// 清空图片数组
this.images = [];
this.images.push(imgSrc);
this.show();
}
},
// 获取url参数
getQueryVariable: function (url, variable) {
// 对url进行截取
url = url.substring(url.indexOf("?"), url.length);
var query = url.substring(1);
var vars = query.split("&");
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
if (pair[0] == variable) { return pair[1]; }
}
return false;
},
// 字符串指定位置添加字符
insertStr: function (source, start, newStr) {
return source.slice(0, start) + newStr + source.slice(start);
},
// 可编辑div获取焦点
getEditableDivFocus: function () {
// 开头获取焦点
this.$refs.msgInputContainer.focus();
},
// 图片查看插件
show() {
const viewer = this.$el.querySelector('.images').$viewer
viewer.show()
}
},
}
至于表情包和一些聊天小组件,有需要的童鞋可以留言。