Commit 16f688d2 authored by caicaicai's avatar caicaicai

修改

parent 2939f995
'use strict'
import { app, protocol, BrowserWindow } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
const isDevelopment = process.env.NODE_ENV !== 'production'
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
{ scheme: 'app', privileges: { secure: true, standard: true } }
])
async function createWindow() {
// Create the browser window.
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
// Required for Spectron testing
enableRemoteModule: !!process.env.IS_TEST,
// Use pluginOptions.nodeIntegration, leave this alone
// See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION
}
})
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
if (!process.env.IS_TEST) win.webContents.openDevTools()
} else {
createProtocol('app')
// Load the index.html when not in development
win.loadURL('app://./index.html')
}
}
// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
if (isDevelopment && !process.env.IS_TEST) {
// Install Vue Devtools
try {
await installExtension(VUEJS_DEVTOOLS)
} catch (e) {
console.error('Vue Devtools failed to install:', e.toString())
}
}
createWindow()
})
// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
if (process.platform === 'win32') {
process.on('message', (data) => {
if (data === 'graceful-exit') {
app.quit()
}
})
} else {
process.on('SIGTERM', () => {
app.quit()
})
}
}
...@@ -130,6 +130,13 @@ ...@@ -130,6 +130,13 @@
<script> <script>
import { Tools, HttpReq, Dates} from '@/assets/js/common.js'; import { Tools, HttpReq, Dates} from '@/assets/js/common.js';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
//加载必须要的core,demo简化起见采用的直接加载类库,实际使用时应当采用异步按需加载
import Recorder from 'recorder-core'
// //需要使用到的音频格式编码引擎的js文件统统加载进来,这些引擎文件会比较大
import 'recorder-core/src/engine/mp3'
import 'recorder-core/src/engine/mp3-engine'
// //可选的扩展
import 'recorder-core/src/extensions/waveview'
export default { export default {
data() { data() {
...@@ -147,6 +154,16 @@ export default { ...@@ -147,6 +154,16 @@ export default {
dispatchDetailsList:[], //智能调度情况表 dispatchDetailsList:[], //智能调度情况表
dispatchBtnTitle:'人工调度', dispatchBtnTitle:'人工调度',
dispatchBtnTitle1:'人工调度', dispatchBtnTitle1:'人工调度',
type:"mp3",
bitRate:16,
sampleRate:16000,
rec:0,
duration:0,
powerLevel:0,
recOpenDialogShow:0,
logs:[],
} }
}, },
created(){ }, created(){ },
...@@ -201,14 +218,17 @@ export default { ...@@ -201,14 +218,17 @@ export default {
//车辆区域分布Echarts //车辆区域分布Echarts
this.carsAreaDistributionEcharts(); this.carsAreaDistributionEcharts();
}) })
//开启录音
this.recOpen();
//页面刚进入时开启长连接 //页面刚进入时开启长连接
this.initWebSocket(); // this.initWebSocket();
this.heartbeatTimer = setInterval(() => { // this.heartbeatTimer = setInterval(() => {
this.reconnect(); // this.reconnect();
}, 10000) // }, 10000)
}else{ }else{
//关闭录音
this.recClose();
this.whetherShow = false; this.whetherShow = false;
} }
}, },
...@@ -448,12 +468,389 @@ export default { ...@@ -448,12 +468,389 @@ export default {
} }
}, },
recOpen:function(){
var This=this;
var rec=this.rec=Recorder({
type:This.type
,bitRate:This.bitRate
,sampleRate:This.sampleRate
,onProcess:function(buffers,powerLevel,duration,sampleRate){
This.duration=duration;
This.powerLevel=powerLevel;
This.wave.input(buffers[buffers.length-1],powerLevel,sampleRate);
}
});
This.dialogInt=setTimeout(function(){//定时8秒后打开弹窗,用于监测浏览器没有发起权限请求的情况
This.showDialog();
},8000);
rec.open(function(){
This.dialogCancel();
This.reclog("已打开:"+This.type+" "+This.sampleRate+"hz "+This.bitRate+"kbps",2);
This.wave=Recorder.WaveView({elem:".ctrlProcessWave"});
},function(msg,isUserNotAllow){
This.dialogCancel();
This.reclog((isUserNotAllow?"UserNotAllow,":"")+"打开失败:"+msg,1);
});
This.waitDialogClickFn=function(){
This.dialogCancel();
This.reclog("打开失败:权限请求被忽略,用户主动点击的弹窗",1);
};
},
recClose:function(){
var rec=this.rec;
this.rec=null;
if(rec){
rec.close();
this.reclog("已关闭");
}else{
this.reclog("未打开录音",1);
};
},
recStart:function(){
if(!this.rec||!Recorder.IsOpen()){
//this.reclog("未打开录音",1);
this.recOpen()
//return;
}
this.rec.start();
var set=this.rec.set;
this.reclog("录制中:"+set.type+" "+set.sampleRate+"hz "+set.bitRate+"kbps");
this.$notify({
title: '录制中!',
type: 'success',
duration: 5000
});
},
recPause:function(){
if(this.rec&&Recorder.IsOpen()){
this.rec.pause();
}else{
this.reclog("未打开录音",1);
};
},
recResume:function(){
if(this.rec&&Recorder.IsOpen()){
this.rec.resume();
}else{
this.reclog("未打开录音",1);
};
},
recStop:function(){
if(!(this.rec&&Recorder.IsOpen())){
This.reclog("未打开录音",1);
This.$notify({
title: '未打开录音!',
type: 'warning',
duration: 2500
});
return;
}
var This=this;
var rec=This.rec;
rec.stop(function(blob,duration){
This.reclog("已录制:","",{
blob:blob,
duration:duration,
rec:rec
});
This.$notify({
title: '已录制!',
type: 'success',
duration: 2500
});
},function(s){
This.$notify({
title: '录音失败,'+s,
type: 'warning',
duration: 2500
});
This.reclog("录音失败:"+s,1);
});
this.recOpen();
},
recPlayLast:function(){
if(!this.recLogLast){
this.reclog("请先录音,然后停止后再播放",1);
return;
};
this.recplay(this.recLogLast.idx);
},
recUploadLast:function(){
let carNumberString = '';
this.selectCarRadioArray.forEach((item,index)=>{
carNumberString = carNumberString + item.number + ';';
})
carNumberString = carNumberString + 'pcBigScreen';
this.carAllNumberString = carNumberString;
var This=this;
if(!this.recLogLast){
this.reclog("请先录音,然后停止后再上传",1);
This.$notify({
title: '请先录音,然后停止后再上传!',
type: 'warning',
duration: 2500
});
return;
};
var blob=this.recLogLast.res.blob;
//本例子假设使用原始XMLHttpRequest请求方式,实际使用中自行调整为自己的请求方式
//录音结束时拿到了blob文件对象,可以用FileReader读取出内容,或者用FormData上传
var api="https://xx.xx/test_request";
var onreadystatechange=function(title){
return function(){
if(xhr.readyState==4){
if(xhr.status==200){
This.reclog(title+"上传成功",2);
}else{
This.reclog(title+"没有完成上传,演示上传地址无需关注上传结果,只要浏览器控制台内Network面板内看到的请求数据结构是预期的就ok了。", "#d8c1a0");
console.error(title+"上传失败",xhr.status,xhr.responseText);
};
};
};
};
This.reclog("开始上传到"+api+",请求稍后...","#f60");
/***方式一:将blob文件转成base64纯文本编码,使用普通application/x-www-form-urlencoded表单上传***/
var reader=new FileReader();
reader.onloadend=function(){
var postData="";
postData+="mime="+encodeURIComponent(blob.type);//告诉后端,这个录音是什么格式的,可能前后端都固定的mp3可以不用写
postData+="&upfile_b64="+encodeURIComponent((/.+;\s*base64\s*,\s*(.+)$/i.exec(reader.result)||[])[1]) //录音文件内容,后端进行base64解码成二进制
//...其他表单参数
// var xhr=new XMLHttpRequest();
// xhr.open("POST", api);
// xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
// xhr.onreadystatechange=onreadystatechange("上传方式一【Base64】");
// xhr.send(postData);
//发送语音数据
if (This.websocket.readyState === 1) { // that.websock.readyState = 1 表示连接成功,可以立即发送信息
//console.log(`{"toUserId":"${This.userName1}","contentText":"${postData}"}`);
This.websocketsend(`{"toUserId":"${This.carAllNumberString}","contentText":"${postData}","fromUserId":"调度中心大屏"}`);
This.$notify({
title: '上传成功!',
type: 'success',
duration: 2500
});
} else if (This.websocket.readyState === 0) { // 表示正在连接,设置300ms后发送信息
setTimeout(function () {
This.websocketsend(`{"toUserId":"${This.carAllNumberString}","contentText":"${postData}","fromUserId":"调度中心大屏"}`);
This.$notify({
title: '上传成功!',
type: 'success',
duration: 2500
});
}, 300);
} else { // 连接未创建或者创建失败,则重新创建连接,并设置500ms后发送信息
This.initWebSocket();
setTimeout(function () {
This.websocketsend(`{"toUserId":"${This.carAllNumberString}","contentText":"${postData}","fromUserId":"调度中心大屏"}`);
This.$notify({
title: '上传成功!',
type: 'success',
duration: 2500
});
}, 500);
}
};
reader.readAsDataURL(blob);
},
reclog:function(msg,color,res){
var obj={
idx:this.logs.length
,msg:msg
,color:color
,res:res
,playMsg:""
,down:0
,down64Val:""
};
if(res&&res.blob){
this.recLogLast=obj;
};
this.logs.splice(0,0,obj);
},
recplay:function(idx){
var This=this;
var o=this.logs[this.logs.length-idx-1];
o.play=(o.play||0)+1;
var logmsg=function(msg){
o.playMsg='<span style="color:green">'+o.play+'</span> '+This.getTime()+" "+msg;
};
logmsg("");
var audio=this.$refs.LogAudioPlayer;
audio.controls=true;
if(!(audio.ended || audio.paused)){
audio.pause();
};
audio.onerror=function(e){
logmsg('<span style="color:red">播放失败['+audio.error.code+']'+audio.error.message+'</span>');
};
audio.src=(window.URL||webkitURL).createObjectURL(o.res.blob);
audio.play();
},
recdown:function(idx){
var This=this;
var o=this.logs[this.logs.length-idx-1];
o.down=(o.down||0)+1;
o=o.res;
var name="rec-"+o.duration+"ms-"+(o.rec.set.bitRate||"-")+"kbps-"+(o.rec.set.sampleRate||"-")+"hz."+(o.rec.set.type||(/\w+$/.exec(o.blob.type)||[])[0]||"unknown");
var downA=document.createElement("A");
downA.href=(window.URL||webkitURL).createObjectURL(o.blob);
downA.download=name;
downA.click();
},
recdown64:function(idx){
var This=this;
var o=this.logs[this.logs.length-idx-1];
var reader = new FileReader();
reader.onloadend = function() {
o.down64Val=reader.result;
};
reader.readAsDataURL(o.res.blob);
},
getTime:function(){
var now=new Date();
var t=("0"+now.getHours()).substr(-2)
+":"+("0"+now.getMinutes()).substr(-2)
+":"+("0"+now.getSeconds()).substr(-2);
return t;
},
intp:function(s,len){
s=s==null?"-":s+"";
if(s.length>=len)return s;
return ("_______"+s).substr(-len);
},
showDialog:function(){
//我们可以选择性的弹一个对话框:为了防止移动端浏览器存在第三种情况:用户忽略,并且(或者国产系统UC系)浏览器没有任何回调
if(!/mobile/i.test(navigator.userAgent)){
return;//只在移动端开启没有权限请求的检测
};
this.recOpenDialogShow=1;
},
dialogCancel:function(){
clearTimeout(this.dialogInt);
this.recOpenDialogShow=0;
},
waitDialogClick:function(){
this.dialogCancel();
this.waitDialogClickFn();
},
selectSituation(item){
this.recClose();
// Blobdata 就是后端返回给你的Blob数据
const reader = new FileReader();
const booo = new Blob([item.contentText]);
reader.readAsArrayBuffer(booo);
reader.onload = (e) => {
const bufer = e.srcElement.result;
const blob = this.addWavHeader(bufer, 16000, 16, 1);
this.srcUrl = window.URL.createObjectURL(blob);
};
},
addWavHeader(samples, sampleRateTmp, sampleBits, channelCount) {
const dataLength = samples.byteLength;
/* 新的buffer类,预留44bytes的heaer空间 */
const buffer = new ArrayBuffer(44 + dataLength);
/* 转为 Dataview, 利用API来填充字节 */
const view = new DataView(buffer);
let offset = 0;
/* ChunkID, 4 bytes, 资源交换文件标识符 */
this.writeString(view, offset, 'RIFF'); offset += 4;
/* ChunkSize, 4 bytes, 下个地址开始到文件尾总字节数,即文件大小-8 */
view.setUint32(offset, /* 32 */ 36 + dataLength, true); offset += 4;
/* Format, 4 bytes, WAV文件标志 */
this.writeString(view, offset, 'WAVE'); offset += 4;
/* Subchunk1 ID, 4 bytes, 波形格式标志 */
this.writeString(view, offset, 'fmt '); offset += 4;
/* Subchunk1 Size, 4 bytes, 过滤字节,一般为 0x10 = 16 */
view.setUint32(offset, 16, true); offset += 4;
/* Audio Format, 2 bytes, 格式类别 (PCM形式采样数据) */
view.setUint16(offset, 1, true); offset += 2;
/* Num Channels, 2 bytes, 通道数 */
view.setUint16(offset, channelCount, true); offset += 2;
/* SampleRate, 4 bytes, 采样率,每秒样本数,表示每个通道的播放速度 */
view.setUint32(offset, sampleRateTmp, true); offset += 4;
/* ByteRate, 4 bytes, 波形数据传输率 (每秒平均字节数) 通道数×每秒数据位数×每样本数据位/8 */
view.setUint32(offset, sampleRateTmp * channelCount * (sampleBits / 8), true); offset += 4;
/* BlockAlign, 2 bytes, 快数据调整数 采样一次占用字节数 通道数×每样本的数据位数/8 */
view.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;
/* BitsPerSample, 2 bytes, 每样本数据位数 */
view.setUint16(offset, sampleBits, true); offset += 2;
/* Subchunk2 ID, 4 bytes, 数据标识符 */
this.writeString(view, offset, 'data'); offset += 4;
/* Subchunk2 Size, 4 bytes, 采样数据总数,即数据总大小-44 */
view.setUint32(offset, dataLength, true); offset += 4;
if (sampleBits === 16) {
this.floatTo16BitPCM(view, samples);
} else if (sampleBits === 8) {
this.floatTo8BitPCM(view, samples);
} else {
this.floatTo32BitPCM(view, samples);
}
return new Blob([view], { type: 'audio/wav' });
},
writeString(view, offset, string) {
for (let i = 0; i < string.length; i += 1) {
view.setUint8(offset + i, string.charCodeAt(i));
}
},
floatTo32BitPCM(output, input) {
const oinput = new Int32Array(input);
let newoffset = 44;
for (let i = 0; i < oinput.length; i += 1, newoffset += 4) {
output.setInt32(newoffset, oinput[i], true);
}
},
floatTo16BitPCM(output, input) {
const oinput = new Int16Array(input);
let newoffset = 44;
for (let i = 0; i < oinput.length; i += 1, newoffset += 2) {
output.setInt16(newoffset, oinput[i], true);
}
},
floatTo8BitPCM(output, input) {
const oinput = new Int8Array(input);
let newoffset = 44;
for (let i = 0; i < oinput.length; i += 1, newoffset += 1) {
output.setInt8(newoffset, oinput[i], true);
}
},
}, },
beforeDestroy() { beforeDestroy() {
//页面销毁时关闭长连接 //页面销毁时关闭长连接
this.websocketclose(); this.websocketclose();
//关闭心跳 //关闭心跳
this.heartbeatTimer = null; if(this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
}
}, },
} }
</script> </script>
......
...@@ -39,9 +39,9 @@ ...@@ -39,9 +39,9 @@
<span class="el-icon-location"></span> <span class="el-icon-location"></span>
</div> --> </div> -->
<!-- 地图区域 --> <!-- 地图区域 -->
<div id="centerDiv" class="mapcontainer1"> <!-- <div id="centerDiv" class="mapcontainer1">
<mars3dViewerMap :url="configUrl" @onload="onMapload"/> <mars3dViewerMap :url="configUrl" @onload="onMapload"/>
</div> </div> -->
</div> </div>
</template> </template>
......
...@@ -24,8 +24,8 @@ module.exports = { ...@@ -24,8 +24,8 @@ module.exports = {
lintOnSave: process.env.NODE_ENV === 'development', lintOnSave: process.env.NODE_ENV === 'development',
productionSourceMap: false, productionSourceMap: false,
devServer: { devServer: {
// host: 'localhost', // 也可以直接写IP地址这样方便真机测试 host: 'localhost', // 也可以直接写IP地址这样方便真机测试
host: '192.168.0.101', // 也可以直接写IP地址这样方便真机测试 //host: '192.168.0.101', // 也可以直接写IP地址这样方便真机测试
port: port, port: port,
open: true, open: true,
overlay: { overlay: {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment