13 changed files with 177 additions and 543 deletions
@ -0,0 +1,19 @@ |
|||||
|
package com.bx.implatform.config; |
||||
|
|
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
|
||||
|
@Data |
||||
|
public class ICEServer { |
||||
|
|
||||
|
|
||||
|
private String urls; |
||||
|
|
||||
|
|
||||
|
private String username; |
||||
|
|
||||
|
|
||||
|
private String credential; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
package com.bx.implatform.config; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Data |
||||
|
@Component |
||||
|
@ConfigurationProperties(prefix="webrtc") |
||||
|
public class ICEServerConfig { |
||||
|
|
||||
|
private List<ICEServer> iceServers = new ArrayList<>(); |
||||
|
|
||||
|
} |
||||
Binary file not shown.
Binary file not shown.
@ -1,369 +0,0 @@ |
|||||
<template> |
|
||||
<div class="im-msg-item"> |
|
||||
<div class="im-msg-tip" v-show="msgInfo.type==10">{{msgInfo.content}}</div> |
|
||||
<div class="im-msg-normal" v-show="msgInfo.type!=10" :class="{'im-chat-mine':mine}"> |
|
||||
<div class="head-image"> |
|
||||
<head-image :url="headImage" :id="msgInfo.sendId"></head-image> |
|
||||
</div> |
|
||||
<div class="im-msg-content"> |
|
||||
<div class="im-msg-top"> |
|
||||
<span>{{showName}}</span> |
|
||||
<chat-time :time="msgInfo.sendTime"></chat-time> |
|
||||
</div> |
|
||||
<div class="im-msg-bottom" @contextmenu.prevent="showRightMenu($event)"> |
|
||||
<span class="im-msg-text" v-if="msgInfo.type==0" v-html="$emo.transform(msgInfo.content)"></span> |
|
||||
<div class="im-msg-image" v-if="msgInfo.type==1"> |
|
||||
<div class="img-load-box" v-loading="loading" element-loading-text="上传中.." element-loading-background="rgba(0, 0, 0, 0.4)"> |
|
||||
<img class="send-image" :src="JSON.parse(msgInfo.content).thumbUrl" @click="showFullImageBox()" /> |
|
||||
</div> |
|
||||
<span title="发送失败" v-show="loadFail" @click="handleSendFail" class="send-fail el-icon-warning"></span> |
|
||||
</div> |
|
||||
<div class="im-msg-file" v-if="msgInfo.type==2"> |
|
||||
<div class="im-file-box" v-loading="loading"> |
|
||||
<div class="im-file-info"> |
|
||||
<el-link class="im-file-name" :underline="true" target="_blank" type="primary" :href="data.url">{{data.name}}</el-link> |
|
||||
<div class="im-file-size">{{fileSize}}</div> |
|
||||
</div> |
|
||||
<div class="im-file-icon"> |
|
||||
<span type="primary" class="el-icon-document"></span> |
|
||||
</div> |
|
||||
</div> |
|
||||
<span title="发送失败" v-show="loadFail" @click="handleSendFail" class="send-fail el-icon-warning"></span> |
|
||||
</div> |
|
||||
<div class="im-msg-voice" v-if="msgInfo.type==3" @click="handlePlayVoice()"> |
|
||||
<audio controls :src="JSON.parse(msgInfo.content).url"></audio> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
</div> |
|
||||
<right-menu v-show="menu && rightMenu.show" :pos="rightMenu.pos" :items="menuItems" @close="rightMenu.show=false" |
|
||||
@select="handleSelectMenu"></right-menu> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
import ChatTime from "./ChatTime.vue"; |
|
||||
import HeadImage from "../common/HeadImage.vue"; |
|
||||
import RightMenu from '../common/RightMenu.vue'; |
|
||||
|
|
||||
export default { |
|
||||
name: "messageItem", |
|
||||
components: { |
|
||||
ChatTime, |
|
||||
HeadImage, |
|
||||
RightMenu |
|
||||
}, |
|
||||
props: { |
|
||||
mine: { |
|
||||
type: Boolean, |
|
||||
required: true |
|
||||
}, |
|
||||
headImage: { |
|
||||
type: String, |
|
||||
required: true |
|
||||
}, |
|
||||
showName: { |
|
||||
type: String, |
|
||||
required: true |
|
||||
}, |
|
||||
msgInfo: { |
|
||||
type: Object, |
|
||||
required: true |
|
||||
}, |
|
||||
menu:{ |
|
||||
type: Boolean, |
|
||||
default: true |
|
||||
} |
|
||||
}, |
|
||||
data() { |
|
||||
return { |
|
||||
audioPlayState: 'STOP', |
|
||||
rightMenu: { |
|
||||
show: false, |
|
||||
pos: { |
|
||||
x: 0, |
|
||||
y: 0 |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
}, |
|
||||
methods: { |
|
||||
handleSendFail() { |
|
||||
this.$message.error("该文件已发送失败,目前不支持自动重新发送,建议手动重新发送") |
|
||||
}, |
|
||||
showFullImageBox() { |
|
||||
let imageUrl = JSON.parse(this.msgInfo.content).originUrl; |
|
||||
if (imageUrl) { |
|
||||
this.$store.commit('showFullImageBox', imageUrl); |
|
||||
} |
|
||||
}, |
|
||||
handlePlayVoice() { |
|
||||
if (!this.audio) { |
|
||||
this.audio = new Audio(); |
|
||||
} |
|
||||
this.audio.src = JSON.parse(this.msgInfo.content).url; |
|
||||
this.audio.play(); |
|
||||
this.handlePlayVoice = 'RUNNING'; |
|
||||
}, |
|
||||
showRightMenu(e) { |
|
||||
this.rightMenu.pos = { |
|
||||
x: e.x, |
|
||||
y: e.y |
|
||||
}; |
|
||||
this.rightMenu.show = "true"; |
|
||||
}, |
|
||||
handleSelectMenu(item) { |
|
||||
this.$emit(item.key.toLowerCase(), this.msgInfo); |
|
||||
} |
|
||||
}, |
|
||||
computed: { |
|
||||
loading() { |
|
||||
return this.msgInfo.loadStatus && this.msgInfo.loadStatus === "loading"; |
|
||||
}, |
|
||||
loadFail() { |
|
||||
return this.msgInfo.loadStatus && this.msgInfo.loadStatus === "fail"; |
|
||||
}, |
|
||||
data() { |
|
||||
return JSON.parse(this.msgInfo.content) |
|
||||
}, |
|
||||
fileSize() { |
|
||||
let size = this.data.size; |
|
||||
if (size > 1024 * 1024) { |
|
||||
return Math.round(size / 1024 / 1024) + "M"; |
|
||||
} |
|
||||
if (size > 1024) { |
|
||||
return Math.round(size / 1024) + "KB"; |
|
||||
} |
|
||||
return size + "B"; |
|
||||
}, |
|
||||
menuItems() { |
|
||||
let items = []; |
|
||||
items.push({ |
|
||||
key: 'DELETE', |
|
||||
name: '删除', |
|
||||
icon: 'el-icon-delete' |
|
||||
}); |
|
||||
if (this.msgInfo.selfSend && this.msgInfo.id > 0) { |
|
||||
items.push({ |
|
||||
key: 'RECALL', |
|
||||
name: '撤回', |
|
||||
icon: 'el-icon-refresh-left' |
|
||||
}); |
|
||||
} |
|
||||
return items; |
|
||||
} |
|
||||
}, |
|
||||
mounted() { |
|
||||
//console.log(this.msgInfo); |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style lang="scss"> |
|
||||
.im-msg-item { |
|
||||
|
|
||||
.im-msg-tip { |
|
||||
line-height: 50px; |
|
||||
} |
|
||||
|
|
||||
.im-msg-normal { |
|
||||
position: relative; |
|
||||
font-size: 0; |
|
||||
margin-bottom: 10px; |
|
||||
padding-left: 60px; |
|
||||
min-height: 68px; |
|
||||
|
|
||||
.head-image { |
|
||||
position: absolute; |
|
||||
width: 40px; |
|
||||
height: 40px; |
|
||||
top: 0; |
|
||||
left: 0; |
|
||||
} |
|
||||
|
|
||||
.im-msg-content { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
|
|
||||
.im-msg-top { |
|
||||
display: flex; |
|
||||
flex-wrap: nowrap; |
|
||||
color: #333; |
|
||||
font-size: 14px; |
|
||||
line-height: 20px; |
|
||||
|
|
||||
span { |
|
||||
margin-right: 12px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.im-msg-bottom { |
|
||||
text-align: left; |
|
||||
|
|
||||
.im-msg-text { |
|
||||
position: relative; |
|
||||
line-height: 22px; |
|
||||
margin-top: 10px; |
|
||||
padding: 10px; |
|
||||
background-color: #eeeeee; |
|
||||
border-radius: 3px; |
|
||||
color: #333; |
|
||||
display: inline-block; |
|
||||
font-size: 14px; |
|
||||
|
|
||||
&:after { |
|
||||
content: ""; |
|
||||
position: absolute; |
|
||||
left: -10px; |
|
||||
top: 13px; |
|
||||
width: 0; |
|
||||
height: 0; |
|
||||
border-style: solid dashed dashed; |
|
||||
border-color: #eeeeee transparent transparent; |
|
||||
overflow: hidden; |
|
||||
border-width: 10px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.im-msg-image { |
|
||||
display: flex; |
|
||||
flex-wrap: nowrap; |
|
||||
flex-direction: row; |
|
||||
align-items: center; |
|
||||
|
|
||||
.send-image { |
|
||||
min-width: 300px; |
|
||||
min-height: 200px; |
|
||||
max-width: 600px; |
|
||||
max-height: 400px; |
|
||||
border: #dddddd solid 1px; |
|
||||
cursor: pointer; |
|
||||
} |
|
||||
|
|
||||
.send-fail { |
|
||||
color: #e60c0c; |
|
||||
font-size: 30px; |
|
||||
cursor: pointer; |
|
||||
margin: 0 20px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.im-msg-file { |
|
||||
display: flex; |
|
||||
flex-wrap: nowrap; |
|
||||
flex-direction: row; |
|
||||
align-items: center; |
|
||||
cursor: pointer; |
|
||||
|
|
||||
.im-file-box { |
|
||||
display: flex; |
|
||||
flex-wrap: nowrap; |
|
||||
align-items: center; |
|
||||
width: 20%; |
|
||||
min-height: 80px; |
|
||||
border: #dddddd solid 1px; |
|
||||
border-radius: 3px; |
|
||||
background-color: #eeeeee; |
|
||||
padding: 10px 15px; |
|
||||
|
|
||||
.im-file-info { |
|
||||
flex: 1; |
|
||||
height: 100%; |
|
||||
text-align: left; |
|
||||
font-size: 14px; |
|
||||
|
|
||||
.im-file-name { |
|
||||
font-size: 16px; |
|
||||
font-weight: 600; |
|
||||
margin-bottom: 15px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.im-file-icon { |
|
||||
font-size: 50px; |
|
||||
color: #d42e07; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.send-fail { |
|
||||
color: #e60c0c; |
|
||||
font-size: 30px; |
|
||||
cursor: pointer; |
|
||||
margin: 0 20px; |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
|
|
||||
.im-msg-voice { |
|
||||
font-size: 14px; |
|
||||
cursor: pointer; |
|
||||
|
|
||||
audio { |
|
||||
height: 45px; |
|
||||
padding: 5px 0; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
&.im-chat-mine { |
|
||||
text-align: right; |
|
||||
padding-left: 0; |
|
||||
padding-right: 60px; |
|
||||
|
|
||||
.head-image { |
|
||||
left: auto; |
|
||||
right: 0; |
|
||||
} |
|
||||
|
|
||||
.im-msg-content { |
|
||||
|
|
||||
.im-msg-top { |
|
||||
flex-direction: row-reverse; |
|
||||
|
|
||||
span { |
|
||||
margin-left: 12px; |
|
||||
margin-right: 0; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.im-msg-bottom { |
|
||||
text-align: right; |
|
||||
|
|
||||
.im-msg-text { |
|
||||
margin-left: 10px; |
|
||||
background-color: #5fb878; |
|
||||
color: #fff; |
|
||||
display: inline-block; |
|
||||
vertical-align: top; |
|
||||
font-size: 14px; |
|
||||
|
|
||||
&:after { |
|
||||
left: auto; |
|
||||
right: -10px; |
|
||||
border-top-color: #5fb878; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.im-msg-image { |
|
||||
flex-direction: row-reverse; |
|
||||
} |
|
||||
|
|
||||
.im-msg-file { |
|
||||
flex-direction: row-reverse; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.message-info { |
|
||||
right: 60px !important; |
|
||||
display: inline-block; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,124 +0,0 @@ |
|||||
<template> |
|
||||
<div class="video-acceptor"> |
|
||||
<div> |
|
||||
<head-image :size="120" :url="this.friend.headImage" :id="this.friend.id"></head-image> |
|
||||
</div> |
|
||||
<div> |
|
||||
{{friend.nickName}} 请求和您进行视频通话... |
|
||||
</div> |
|
||||
<div class="video-acceptor-btn-group"> |
|
||||
<div class="icon iconfont icon-phone-accept accept" @click="accpet()"></div> |
|
||||
<div class="icon iconfont icon-phone-reject reject" @click="reject()"></div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
import HeadImage from '../common/HeadImage.vue'; |
|
||||
|
|
||||
export default { |
|
||||
name: "videoAcceptor", |
|
||||
components:{HeadImage}, |
|
||||
props: { |
|
||||
friend:{ |
|
||||
type: Object |
|
||||
} |
|
||||
}, |
|
||||
data(){ |
|
||||
return { |
|
||||
offer:{} |
|
||||
} |
|
||||
}, |
|
||||
methods:{ |
|
||||
accpet(){ |
|
||||
let info ={ |
|
||||
friend: this.friend, |
|
||||
master: false, |
|
||||
offer: this.offer |
|
||||
} |
|
||||
this.$store.commit("showChatPrivateVideoBox",info); |
|
||||
this.close(); |
|
||||
}, |
|
||||
reject(){ |
|
||||
this.$http({ |
|
||||
url: `/webrtc/private/reject?uid=${this.friend.id}`, |
|
||||
method: 'post' |
|
||||
}) |
|
||||
this.close(); |
|
||||
}, |
|
||||
failed(reason){ |
|
||||
this.$http({ |
|
||||
url: `/webrtc/private/failed?uid=${this.friend.id}&reason=${reason}`, |
|
||||
method: 'post' |
|
||||
}) |
|
||||
this.close(); |
|
||||
}, |
|
||||
onCall(msgInfo){ |
|
||||
console.log("onCall") |
|
||||
this.offer = JSON.parse(msgInfo.content); |
|
||||
if(this.$store.state.userStore.state == this.$enums.USER_STATE.BUSY){ |
|
||||
this.failed("对方正忙,暂时无法接听"); |
|
||||
return; |
|
||||
} |
|
||||
// 超时未接听 |
|
||||
this.timer && clearTimeout(this.timer); |
|
||||
this.timer = setTimeout(()=>{ |
|
||||
this.failed("对方未接听"); |
|
||||
},30000) |
|
||||
}, |
|
||||
onCancel(){ |
|
||||
this.$message.success("对方取消了呼叫"); |
|
||||
this.close(); |
|
||||
}, |
|
||||
handleMessage(msgInfo){ |
|
||||
if(msgInfo.type == this.$enums.MESSAGE_TYPE.RTC_CALL){ |
|
||||
this.onCall(msgInfo); |
|
||||
}else if(msgInfo.type == this.$enums.MESSAGE_TYPE.RTC_CANCEL){ |
|
||||
this.onCancel(); |
|
||||
} |
|
||||
}, |
|
||||
close(){ |
|
||||
this.timer && clearTimeout(this.timer); |
|
||||
this.$emit("close"); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
</script> |
|
||||
|
|
||||
<style scoped lang="scss"> |
|
||||
.video-acceptor { |
|
||||
position: absolute; |
|
||||
right: 1px; |
|
||||
bottom: 1px; |
|
||||
width: 250px; |
|
||||
height: 250px; |
|
||||
padding: 20px; |
|
||||
text-align: center; |
|
||||
background-color: #eeeeee; |
|
||||
border: #dddddd solid 1px; |
|
||||
|
|
||||
.video-acceptor-btn-group { |
|
||||
display: flex; |
|
||||
justify-content: space-around; |
|
||||
margin-top: 20px; |
|
||||
|
|
||||
.icon { |
|
||||
font-size: 50px; |
|
||||
cursor: pointer; |
|
||||
&.accept { |
|
||||
color: green; |
|
||||
} |
|
||||
&.reject { |
|
||||
color: red; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
} |
|
||||
} |
|
||||
</style> |
|
||||
@ -0,0 +1,72 @@ |
|||||
|
import Vue from 'vue' |
||||
|
|
||||
|
// v-dialogDrag: 弹窗拖拽
|
||||
|
Vue.directive('dialogDrag', { |
||||
|
bind (el, binding, vnode, oldVnode) { |
||||
|
const dialogHeaderEl = el.querySelector('.el-dialog__header') |
||||
|
const dragDom = el.querySelector('.el-dialog') |
||||
|
dialogHeaderEl.style.cursor = 'move' |
||||
|
|
||||
|
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
|
||||
|
const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null) |
||||
|
|
||||
|
dialogHeaderEl.onmousedown = (e) => { |
||||
|
// 鼠标按下,计算当前元素距离可视区的距离
|
||||
|
const disX = e.clientX - dialogHeaderEl.offsetLeft |
||||
|
const disY = e.clientY - dialogHeaderEl.offsetTop |
||||
|
const screenWidth = document.body.clientWidth; // body当前宽度
|
||||
|
const screenHeight = document.documentElement.clientHeight; // 可见区域高度(应为body高度,可某些环境下无法获取)
|
||||
|
const dragDomWidth = dragDom.offsetWidth; // 对话框宽度
|
||||
|
const dragDomheight = dragDom.offsetHeight; // 对话框高度
|
||||
|
const minDragDomLeft = dragDom.offsetLeft; |
||||
|
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth; |
||||
|
const minDragDomTop = dragDom.offsetTop; |
||||
|
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight; |
||||
|
|
||||
|
// 获取到的值带px 正则匹配替换
|
||||
|
let styL, styT |
||||
|
|
||||
|
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
|
||||
|
if (sty.left.includes('%')) { |
||||
|
styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100) |
||||
|
styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100) |
||||
|
} else { |
||||
|
styL = +sty.left.replace(/\px/g, '') |
||||
|
styT = +sty.top.replace(/\px/g, '') |
||||
|
} |
||||
|
|
||||
|
document.onmousemove = function (e) { |
||||
|
// 获取body的页面可视宽高
|
||||
|
// var clientHeight = document.documentElement.clientHeight || document.body.clientHeight
|
||||
|
// var clientWidth = document.documentElement.clientWidth || document.body.clientWidth
|
||||
|
|
||||
|
// 通过事件委托,计算移动的距离
|
||||
|
var l = e.clientX - disX |
||||
|
var t = e.clientY - disY |
||||
|
|
||||
|
// 边界处理
|
||||
|
if (-l > minDragDomLeft) { |
||||
|
l = -minDragDomLeft; |
||||
|
} else if (l > maxDragDomLeft) { |
||||
|
l = maxDragDomLeft; |
||||
|
} |
||||
|
if (-t > minDragDomTop) { |
||||
|
t = -minDragDomTop; |
||||
|
} else if (t > maxDragDomTop) { |
||||
|
t = maxDragDomTop; |
||||
|
} |
||||
|
// 移动当前元素
|
||||
|
dragDom.style.left = `${l + styL}px` |
||||
|
dragDom.style.top = `${t + styT}px` |
||||
|
|
||||
|
// 将此时的位置传出去
|
||||
|
// binding.value({x:e.pageX,y:e.pageY})
|
||||
|
} |
||||
|
|
||||
|
document.onmouseup = function (e) { |
||||
|
document.onmousemove = null |
||||
|
document.onmouseup = null |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
Loading…
Reference in new issue