551 lines
21 KiB
HTML
551 lines
21 KiB
HTML
|
<!DOCTYPE html>
|
||
|
<html lang="zh">
|
||
|
<head>
|
||
|
<meta charset="utf-8">
|
||
|
<meta name="renderer" content="webkit">
|
||
|
<title>{{.SystemTitle}}</title>
|
||
|
<link rel="stylesheet" href="/static/cdn/element-ui/2.15.1/theme-chalk/index.min.css">
|
||
|
<script src="/static/cdn/vue/2.6.11/vue.min.js"></script>
|
||
|
<script src="/static/cdn/element-ui/2.15.1/index.js"></script>
|
||
|
<script src="/static/cdn/jquery/3.6.0/jquery.min.js"></script>
|
||
|
|
||
|
<link rel="stylesheet" href="/static/css/common.css?v=xxxxxxxxssssss">
|
||
|
<script src="/static/js/reconnecting-websocket.min.js"></script>
|
||
|
<script src="/static/js/functions.js"></script>
|
||
|
<script src="/static/js/chat-lang.js?v=0.7.0"></script>
|
||
|
<script src="/static/js/peer.js"></script>
|
||
|
<script src="/static/js/interact.js"></script>
|
||
|
<link rel="icon" href="/static/images/favicon.ico">
|
||
|
</head>
|
||
|
<body class="text-center" style="overflow-y: hidden;">
|
||
|
<div id="app" style="display: flex">
|
||
|
<template>
|
||
|
{{template "nav" }}
|
||
|
<div class="mainRight">
|
||
|
|
||
|
<iframe title="mainIframe" class="mainIframe" v-bind:src="iframeUrl" frameborder="0" id="mainIframe"></iframe>
|
||
|
</div>
|
||
|
<audio id="chatMessageAudio" src="/static/images/alert.mp3"></audio>
|
||
|
<audio id="visitorAlertAudio" src="/static/images/alert2.ogg"></audio>
|
||
|
|
||
|
<!--视频-->
|
||
|
<div id="remoteVideoBox" class="kefuVideoMask" v-show="isCalling!=''">
|
||
|
<el-button size="mini" class="refuse" @click="callClose()" type="danger">挂断</el-button>
|
||
|
<div class="alignCenter">
|
||
|
<video id="chatRtc" controls autoplay></video>
|
||
|
<video id="chatLocalRtc" controls autoplay muted></video>
|
||
|
</div>
|
||
|
</div>
|
||
|
<!--//视频-->
|
||
|
</template>
|
||
|
</div>
|
||
|
</body>
|
||
|
{{.SystemKefu}}
|
||
|
|
||
|
<script>
|
||
|
var LANG=checkLang();
|
||
|
new Vue({
|
||
|
el: '#app',
|
||
|
delimiters:["<{","}>"],
|
||
|
data: {
|
||
|
window:window,
|
||
|
iframeUrl:"/mainGuide",
|
||
|
mailTotal:0,
|
||
|
adminRole:"",
|
||
|
flyLang:KEFU_LANG[LANG],
|
||
|
host:getBaseUrl(),
|
||
|
kefuId:"",
|
||
|
entId:"",
|
||
|
menuAdminShow:false,
|
||
|
superShow:false,
|
||
|
onlineType:"success",
|
||
|
adminAvator:"",
|
||
|
server:getWsBaseUrl()+"/ws_kefu?token="+localStorage.getItem("token"),
|
||
|
haveNewMessage:"",
|
||
|
countNewMessage:true,
|
||
|
alertSounding:false,
|
||
|
alertSoundingTimer:null,
|
||
|
configs:[],
|
||
|
|
||
|
peerjsId:"",
|
||
|
localStream:null,
|
||
|
isCalling:"",
|
||
|
call:null,
|
||
|
isVideo:false,
|
||
|
},
|
||
|
methods: {
|
||
|
openIframeUrl(url){
|
||
|
this.iframeUrl=url;
|
||
|
this.countNewMessage=true;
|
||
|
},
|
||
|
//退出
|
||
|
logout(){
|
||
|
if(this.onlineType=="success"){
|
||
|
this.upDownLine(2);
|
||
|
}
|
||
|
localStorage.removeItem("token");
|
||
|
this.openIframeUrl('/ironMan/signInx');
|
||
|
},
|
||
|
//跳转
|
||
|
openUrl(url){
|
||
|
window.location.href=url;
|
||
|
},
|
||
|
GetQueryString(name){
|
||
|
var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");
|
||
|
var r = window.location.search.substr(1).match(reg);
|
||
|
if(r!=null)return unescape(r[2]); return null;
|
||
|
},
|
||
|
changeOnlineStatus(command){
|
||
|
var _this=this;
|
||
|
sendAjax("/kefu/updateOnlineStatus","get",{status:command},function () {
|
||
|
_this.kefuInfo();
|
||
|
});
|
||
|
},
|
||
|
//初始化websocket
|
||
|
initConn() {
|
||
|
let socket = new ReconnectingWebSocket(this.server);//创建Socket实例
|
||
|
this.socket = socket
|
||
|
this.socket.onmessage = this.OnMessage;
|
||
|
},
|
||
|
OnMessage(e) {
|
||
|
var _this=this;
|
||
|
var redata = JSON.parse(e.data);
|
||
|
var msg = redata.data;
|
||
|
switch (redata.type){
|
||
|
case "unread_num":
|
||
|
redata.data=redata.data==0?"":redata.data;
|
||
|
_this.haveNewMessage=redata.data;
|
||
|
break;
|
||
|
case "notice":
|
||
|
//_this.haveNewMessage="new";
|
||
|
notify(msg.username, {
|
||
|
body: msg.content,
|
||
|
icon: msg.avator
|
||
|
}, function(notification) {
|
||
|
window.focus();
|
||
|
notification.close();
|
||
|
_this.openIframeUrl("/chat_main");
|
||
|
});
|
||
|
|
||
|
if(_this.getConfig("VisitorForceAlert")=="on"){
|
||
|
_this.newVisitorForceAlert(redata.data.username);
|
||
|
}else{
|
||
|
//_this.alertSound();
|
||
|
_this.alertVisitorSound();
|
||
|
}
|
||
|
break;
|
||
|
case "callpeer":
|
||
|
this.handleCall(redata.data);
|
||
|
_this.alertSound();
|
||
|
notify(redata.data.name, {
|
||
|
body: "calling",
|
||
|
icon: "/static/images/video.png"
|
||
|
}, function(notification) {
|
||
|
window.focus();
|
||
|
notification.close();
|
||
|
_this.openIframeUrl("/chat_main");
|
||
|
});
|
||
|
break;
|
||
|
case "callCancel":
|
||
|
this.handleCallCancel(redata.data);
|
||
|
break;
|
||
|
case "message":
|
||
|
if(msg.is_kefu!="no"){
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(_this.haveNewMessage==""){
|
||
|
_this.haveNewMessage=0;
|
||
|
}
|
||
|
_this.haveNewMessage++;
|
||
|
|
||
|
notify(msg.name, {
|
||
|
body: msg.content,
|
||
|
icon: msg.avator
|
||
|
}, function(notification) {
|
||
|
window.focus();
|
||
|
notification.close();
|
||
|
_this.openIframeUrl("/chat_main");
|
||
|
});
|
||
|
_this.alertSound();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
},
|
||
|
kefuInfo(){
|
||
|
let _this=this;
|
||
|
$.ajax({
|
||
|
type:"get",
|
||
|
url:"/kefu/kefuinfo",
|
||
|
headers:{
|
||
|
"token":localStorage.getItem("token")
|
||
|
},
|
||
|
success: function(data) {
|
||
|
if (data.code != 200) {
|
||
|
window.location.href="/ironMan/signInx";
|
||
|
} else {
|
||
|
var result=data.result;
|
||
|
if(result.role_id<=2){
|
||
|
_this.menuAdminShow=true;
|
||
|
}
|
||
|
if(result.role_id==1){
|
||
|
_this.superShow=true;
|
||
|
}
|
||
|
_this.kefuId=result.id;
|
||
|
_this.entId=result.ent_id;
|
||
|
_this.adminAvator=result.avator;
|
||
|
if(result.online_status==1){
|
||
|
_this.onlineType="success";
|
||
|
_this.upDownLine(1);
|
||
|
}else{
|
||
|
_this.onlineType="danger";
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
switchLang(command){
|
||
|
setLocalStorage("lang",command);
|
||
|
document.location.reload();
|
||
|
},
|
||
|
//新访客强制提醒
|
||
|
newVisitorForceAlert:function (title) {
|
||
|
var _this=this;
|
||
|
if(_this.alertSounding){
|
||
|
return;
|
||
|
}
|
||
|
var timer=_this.newVisitorForceAlertSound();
|
||
|
this.$confirm(title, '提示', {
|
||
|
confirmButtonText: '确定',
|
||
|
cancelButtonText: '取消',
|
||
|
type: 'warning'
|
||
|
}).then(function(){
|
||
|
clearInterval(timer);
|
||
|
_this.alertSounding=false;
|
||
|
}).catch(function(){
|
||
|
clearInterval(timer);
|
||
|
_this.alertSounding=false;
|
||
|
});
|
||
|
_this.alertSounding=true;
|
||
|
},
|
||
|
newVisitorForceAlertSound(){
|
||
|
var _this=this;
|
||
|
var timer=setInterval(function(){
|
||
|
_this.alertSound();
|
||
|
},3000);
|
||
|
_this.alertSoundingTimer=timer;
|
||
|
return timer;
|
||
|
},
|
||
|
getConfigs(){
|
||
|
var _this=this;
|
||
|
sendAjax("/kefu/ent_configs","get",{},function(data){
|
||
|
_this.configs=data.result;
|
||
|
});
|
||
|
},
|
||
|
getConfig(key){
|
||
|
for(index in this.configs){
|
||
|
if(key==this.configs[index].conf_key){
|
||
|
return this.configs[index].conf_value;
|
||
|
}
|
||
|
}
|
||
|
return "";
|
||
|
},
|
||
|
//提示音
|
||
|
alertSound(){
|
||
|
let b = document.getElementById("chatMessageAudio");
|
||
|
let src=this.getConfig("KefuAlertSound");
|
||
|
if(src!=""){
|
||
|
b.src=src;
|
||
|
}
|
||
|
let p = b.play();
|
||
|
p && p.then(function(){}).catch(function(e){});
|
||
|
},
|
||
|
//新访客提示音
|
||
|
alertVisitorSound(){
|
||
|
let b = document.getElementById("visitorAlertAudio");
|
||
|
let src=this.getConfig("visitorLoginSound");
|
||
|
if(src!=""){
|
||
|
b.src=src;
|
||
|
}
|
||
|
let p = b.play();
|
||
|
p && p.then(function(){}).catch(function(e){});
|
||
|
},
|
||
|
//上下线
|
||
|
upDownLine(status){
|
||
|
sendAjax("/kefu/upDownLine?online_status="+status,'get',{},function(){});
|
||
|
},
|
||
|
initPeerjs:function(visitorId){
|
||
|
var _this=this;
|
||
|
var peer = new Peer();
|
||
|
this.peer=peer;
|
||
|
this.peer.on('open', function(id) {
|
||
|
console.log('My peer ID is: ' + id);
|
||
|
_this.peerjsId=id;
|
||
|
sendAjax("/kefu/callVisitor","post",{
|
||
|
peer_id:_this.peerjsId,
|
||
|
visitor_id:visitorId,
|
||
|
"action":"accept"
|
||
|
},function(result){
|
||
|
|
||
|
if(result.code!=200){
|
||
|
_this.$message({
|
||
|
message: _this.flyLang.noVisitors,
|
||
|
type: 'error'
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// _this.$alert(retData.name+'正在通话..', '提示', {
|
||
|
// confirmButtonText: '挂断',
|
||
|
// callback: function(){
|
||
|
// if(_this.mediaConnection!=null){
|
||
|
// _this.mediaConnection.close();
|
||
|
// }
|
||
|
// console.log(_this.mediaConnection);
|
||
|
// }
|
||
|
// });
|
||
|
|
||
|
});
|
||
|
});
|
||
|
|
||
|
this.peer.on('close', function() {
|
||
|
_this.callClear();
|
||
|
console.log('My peer close');
|
||
|
});
|
||
|
this.peer.on('disconnected', function() {
|
||
|
_this.callClear();
|
||
|
console.log('My peer disconnected');
|
||
|
});
|
||
|
this.peer.on('error', function() {
|
||
|
_this.callClear();
|
||
|
console.log('My peer error');
|
||
|
});
|
||
|
|
||
|
this.peer.on('call', function(call) {
|
||
|
_this.call = call;
|
||
|
_this.$confirm(_this.flyLang.videoAudio, _this.flyLang.tips, {
|
||
|
confirmButtonText: _this.flyLang.video,
|
||
|
cancelButtonText: _this.flyLang.recoder,
|
||
|
type: 'warning'
|
||
|
}).then(() => {
|
||
|
_this.isVideo=true;
|
||
|
_this.$confirm("屏幕还是摄像头", _this.flyLang.tips, {
|
||
|
confirmButtonText: _this.flyLang.video,
|
||
|
cancelButtonText: "屏幕",
|
||
|
type: 'warning'
|
||
|
}).then(() => {
|
||
|
_this.startUserMedia(visitorId,false);
|
||
|
}).catch(() => {
|
||
|
_this.startUserMedia(visitorId,true);
|
||
|
});
|
||
|
}).catch(() => {
|
||
|
_this.isVideo=false;
|
||
|
_this.startUserMedia(visitorId,false);
|
||
|
});
|
||
|
|
||
|
|
||
|
});
|
||
|
|
||
|
},
|
||
|
handleCall:function (retData) {
|
||
|
var _this=this;
|
||
|
//正在通话中
|
||
|
if(_this.isCalling!=""){
|
||
|
sendAjax("/kefu/callVisitor","post",{
|
||
|
peer_id:_this.peerjsId,
|
||
|
visitor_id:retData.visitor_id,
|
||
|
"action":"refuse"
|
||
|
},function(res){});
|
||
|
return ;
|
||
|
}
|
||
|
if(_this.alertSounding){
|
||
|
return ;
|
||
|
}
|
||
|
var timer=_this.newVisitorForceAlertSound();
|
||
|
_this.alertSounding=true;
|
||
|
|
||
|
this.$confirm(retData.name+'请求通话?', '提示', {
|
||
|
confirmButtonText: '接通',
|
||
|
cancelButtonText: '取消',
|
||
|
type: 'warning'
|
||
|
}).then(function(){
|
||
|
_this.initPeerjs(retData.visitor_id);
|
||
|
clearInterval(timer);
|
||
|
_this.alertSounding=false;
|
||
|
|
||
|
}).catch(function(){
|
||
|
clearInterval(timer);
|
||
|
_this.alertSounding=false;
|
||
|
sendAjax("/kefu/callVisitor","post",{
|
||
|
peer_id:"222",
|
||
|
visitor_id:retData.visitor_id,
|
||
|
"action":"refuse"
|
||
|
},function(res){});
|
||
|
if(_this.call!=null){
|
||
|
_this.call.close();
|
||
|
}
|
||
|
_this.isCalling="";
|
||
|
_this.$message({
|
||
|
type: 'info',
|
||
|
message: '已拒绝'
|
||
|
});
|
||
|
});
|
||
|
},
|
||
|
handleCallCancel:function (retData) {
|
||
|
var _this=this;
|
||
|
_this.isCalling="";
|
||
|
if(_this.call!=null){
|
||
|
_this.call.close();
|
||
|
}
|
||
|
},
|
||
|
//挂断
|
||
|
callClose(visitorId){
|
||
|
if(!visitorId){
|
||
|
visitorId=this.isCalling;
|
||
|
}
|
||
|
sendAjax("/kefu/callVisitor","post",{
|
||
|
peer_id:"222",
|
||
|
visitor_id:this.isCalling,
|
||
|
"action":"refuse"
|
||
|
},function(res){});
|
||
|
this.callClear();
|
||
|
|
||
|
},
|
||
|
callClear(){
|
||
|
var _this=this;
|
||
|
_this.$message({
|
||
|
type: 'error',
|
||
|
message: "通话已关闭"
|
||
|
});
|
||
|
if(_this.localStream){
|
||
|
var tracks=_this.localStream.getTracks();
|
||
|
for(var i=0;i<tracks.length;i++){
|
||
|
tracks[i].stop();
|
||
|
}
|
||
|
_this.localStream=null;
|
||
|
}
|
||
|
_this.isCalling="";
|
||
|
if(_this.call){
|
||
|
this.call.close();
|
||
|
_this.call=null;
|
||
|
}
|
||
|
},
|
||
|
startUserMedia(visitorId,isScreen){
|
||
|
var _this=this;
|
||
|
_this.isCalling = visitorId;
|
||
|
if(isScreen){
|
||
|
getScreenStream(_this.callbackStream);
|
||
|
return;
|
||
|
}
|
||
|
var getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia).bind(navigator);;
|
||
|
getUserMedia({video: _this.isVideo, audio: true},_this.callbackStream, function(err) {
|
||
|
console.log('Failed to get local stream' ,err);
|
||
|
sendAjax("/kefu/callVisitor","post",{
|
||
|
peer_id:"222",
|
||
|
visitor_id:visitorId,
|
||
|
"action":"refuse"
|
||
|
},function(res){});
|
||
|
_this.$message({
|
||
|
type: 'error',
|
||
|
message: err
|
||
|
});
|
||
|
});
|
||
|
},
|
||
|
//媒体流回调
|
||
|
callbackStream(stream) {
|
||
|
var _this=this;
|
||
|
_this.localStream = stream;
|
||
|
//本地摄像头
|
||
|
if (_this.isVideo) {
|
||
|
let chatLocalRtc = document.getElementById('chatLocalRtc');
|
||
|
chatLocalRtc.srcObject = _this.localStream;
|
||
|
chatLocalRtc.autoplay = true;
|
||
|
}
|
||
|
|
||
|
_this.$message({
|
||
|
type: 'success',
|
||
|
message: '接通成功!'
|
||
|
});
|
||
|
|
||
|
_this.call.answer(_this.localStream);
|
||
|
_this.call.on('stream', function (remoteStream) {
|
||
|
var remoteVideo = document.querySelector('#chatRtc');
|
||
|
remoteVideo.srcObject = remoteStream;
|
||
|
remoteVideo.autoplay = true;
|
||
|
});
|
||
|
_this.call.on('close', function () {
|
||
|
_this.callClear();
|
||
|
});
|
||
|
_this.call.on('error', function (err) {
|
||
|
_this.$message({
|
||
|
type: 'error',
|
||
|
message: err
|
||
|
});
|
||
|
_this.callClear();
|
||
|
});
|
||
|
},
|
||
|
//心跳
|
||
|
ping(){
|
||
|
var _this=this;
|
||
|
var mes = {};
|
||
|
mes.type = "ping";
|
||
|
setInterval(function () {
|
||
|
if(_this.socket!=null){
|
||
|
_this.socket.send(JSON.stringify(mes));
|
||
|
}
|
||
|
},58000);
|
||
|
},
|
||
|
},
|
||
|
created: function () {
|
||
|
this.kefuInfo();
|
||
|
this.initConn();
|
||
|
this.getConfigs();
|
||
|
var _this=this;
|
||
|
$(function(){
|
||
|
interact('.kefuVideoMask')
|
||
|
.draggable({
|
||
|
onstart: function (event) {
|
||
|
console.log('drag start');
|
||
|
},
|
||
|
onmove: function (event) {
|
||
|
var target = event.target;
|
||
|
var x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
|
||
|
var y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
|
||
|
|
||
|
target.style.transform = 'translate(' + x + 'px, ' + y + 'px)';
|
||
|
target.setAttribute('data-x', x);
|
||
|
target.setAttribute('data-y', y);
|
||
|
},
|
||
|
onend: function (event) {
|
||
|
console.log('drag end');
|
||
|
}
|
||
|
});
|
||
|
$("body").on("click",".menuLeftItem",function(){
|
||
|
$(".menuLeftItem").removeClass("active");
|
||
|
$(this).addClass("active");
|
||
|
});
|
||
|
})
|
||
|
|
||
|
//监听iframe传递事件
|
||
|
window.addEventListener('message',function(e){
|
||
|
var msg=e.data;
|
||
|
if(msg.type=="read_num") {
|
||
|
var readNum=msg.data;
|
||
|
if(_this.haveNewMessage) _this.haveNewMessage=_this.haveNewMessage-readNum;
|
||
|
if(_this.haveNewMessage<=0) _this.haveNewMessage="";
|
||
|
}
|
||
|
});
|
||
|
//监听页面关闭
|
||
|
window.onbeforeunload = function(e) {
|
||
|
if(_this.onlineType=="success"){
|
||
|
_this.upDownLine(2);
|
||
|
}
|
||
|
};
|
||
|
this.ping();
|
||
|
}
|
||
|
})
|
||
|
</script>
|
||
|
|
||
|
|
||
|
</html>
|