|
|
|
|
@@ -1,17 +1,20 @@
|
|
|
|
|
<template>
|
|
|
|
|
<div ref='dataBoardBoxB' class='dataBoardBoxB'>
|
|
|
|
|
<div id="dataBoardBox" class='dataBoardBox' style="width: 1920px;height: 1080px;" :style="{ transform: 'scale(' + scaleNum + ')' }">
|
|
|
|
|
<div ref="dataBoardBoxB" class="dataBoardBoxB">
|
|
|
|
|
<div
|
|
|
|
|
id="dataBoardBox"
|
|
|
|
|
class="dataBoardBox"
|
|
|
|
|
style="width: 1920px; height: 1080px"
|
|
|
|
|
:style="{ transform: 'scale(' + scaleNum + ')' }">
|
|
|
|
|
<DataBoardHeader
|
|
|
|
|
:is-full-screen="isFullScreen"
|
|
|
|
|
@screenfullChange="screenfullChange"
|
|
|
|
|
/>
|
|
|
|
|
<LeftTop :dataObj='dataObj'/>
|
|
|
|
|
<LeftBottom :dataObj='dataObj'/>
|
|
|
|
|
<CenterTop :scaleNum='scaleNum' :dataObj='dataObj'/>
|
|
|
|
|
<CenterBottomL :dataObj='dataObj'/>
|
|
|
|
|
<CenterBottomR :dataObj='dataObj'/>
|
|
|
|
|
<RightTop :dataObj='dataObj'/>
|
|
|
|
|
<RightBottom :dataObj='dataObj'/>
|
|
|
|
|
@screenfullChange="screenfullChange" />
|
|
|
|
|
<LeftTop :dataObj="dataObj" />
|
|
|
|
|
<LeftBottom :dataObj="dataObj" />
|
|
|
|
|
<CenterTop :scaleNum="scaleNum" :dataObj="dataObj" />
|
|
|
|
|
<CenterBottomL :dataObj="dataObj" />
|
|
|
|
|
<CenterBottomR :dataObj="dataObj" />
|
|
|
|
|
<RightTop :dataObj="dataObj" />
|
|
|
|
|
<RightBottom :dataObj="dataObj" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
@@ -28,10 +31,19 @@ import RightBottom from './components/RightBottom.vue';
|
|
|
|
|
import { debounce } from '@/utils/debounce';
|
|
|
|
|
import screenfull from 'screenfull';
|
|
|
|
|
import { getAccessToken } from '@/utils/auth';
|
|
|
|
|
import store from "@/store";
|
|
|
|
|
import store from '@/store';
|
|
|
|
|
export default {
|
|
|
|
|
name: 'DataBoard',
|
|
|
|
|
components: { DataBoardHeader,LeftTop,LeftBottom,CenterTop,CenterBottomL,CenterBottomR,RightTop,RightBottom },
|
|
|
|
|
components: {
|
|
|
|
|
DataBoardHeader,
|
|
|
|
|
LeftTop,
|
|
|
|
|
LeftBottom,
|
|
|
|
|
CenterTop,
|
|
|
|
|
CenterBottomL,
|
|
|
|
|
CenterBottomR,
|
|
|
|
|
RightTop,
|
|
|
|
|
RightBottom,
|
|
|
|
|
},
|
|
|
|
|
props: {},
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
@@ -41,21 +53,21 @@ export default {
|
|
|
|
|
sseReader: null, // 保存流读取器
|
|
|
|
|
abortController: null, // 用于中止 fetch 请求
|
|
|
|
|
retryCount: 0, // 当前重试次数
|
|
|
|
|
isDestroyed: false // 标记组件是否已销毁
|
|
|
|
|
isDestroyed: false, // 标记组件是否已销毁
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
created() {
|
|
|
|
|
this.init()
|
|
|
|
|
this.init();
|
|
|
|
|
},
|
|
|
|
|
mounted() {
|
|
|
|
|
this.boxReset = debounce(() => {
|
|
|
|
|
this.resetSize()
|
|
|
|
|
}, 300)
|
|
|
|
|
this.boxReset()
|
|
|
|
|
this.resetSize();
|
|
|
|
|
}, 300);
|
|
|
|
|
this.boxReset();
|
|
|
|
|
window.addEventListener('resize', () => {
|
|
|
|
|
this.boxReset()
|
|
|
|
|
})
|
|
|
|
|
this.getData()
|
|
|
|
|
this.boxReset();
|
|
|
|
|
});
|
|
|
|
|
this.getData();
|
|
|
|
|
},
|
|
|
|
|
beforeDestroy() {
|
|
|
|
|
this.closeSSE();
|
|
|
|
|
@@ -65,23 +77,28 @@ export default {
|
|
|
|
|
},
|
|
|
|
|
computed: {
|
|
|
|
|
sidebarOpened() {
|
|
|
|
|
return this.$store.state.app.sidebar.opened
|
|
|
|
|
}
|
|
|
|
|
return this.$store.state.app.sidebar.opened;
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
watch: {
|
|
|
|
|
sidebarOpened(newVal, oldVal) {
|
|
|
|
|
this.boxReset()
|
|
|
|
|
}
|
|
|
|
|
this.boxReset();
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
async getData() {
|
|
|
|
|
let _this = this;
|
|
|
|
|
if (_this.isDestroyed) return;
|
|
|
|
|
const url = process.env.VUE_APP_BASE_API+'/admin-api/monitoring/message/subscribe/'+store.getters.userId+'-'+Date.now();
|
|
|
|
|
const token = getAccessToken()
|
|
|
|
|
const url =
|
|
|
|
|
process.env.VUE_APP_BASE_API +
|
|
|
|
|
'/admin-api/monitoring/message/subscribe/' +
|
|
|
|
|
store.getters.userId +
|
|
|
|
|
'-' +
|
|
|
|
|
Date.now();
|
|
|
|
|
const token = getAccessToken();
|
|
|
|
|
const headers = new Headers({
|
|
|
|
|
'Authorization': `Bearer ${token}`,
|
|
|
|
|
'Content-Type': 'text/event-stream'
|
|
|
|
|
Authorization: `Bearer ${token}`,
|
|
|
|
|
'Content-Type': 'text/event-stream',
|
|
|
|
|
});
|
|
|
|
|
try {
|
|
|
|
|
// 创建中止控制器
|
|
|
|
|
@@ -90,12 +107,14 @@ export default {
|
|
|
|
|
const response = await fetch(url, {
|
|
|
|
|
method: 'GET',
|
|
|
|
|
headers: headers,
|
|
|
|
|
signal: _this.abortController.signal // 绑定中止信号
|
|
|
|
|
signal: _this.abortController.signal, // 绑定中止信号
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 获取流读取器
|
|
|
|
|
_this.sseReader = response.body.getReader();
|
|
|
|
|
const decoder = new TextDecoder();
|
|
|
|
|
let buffer = '';
|
|
|
|
|
let receivedBytes = 0;
|
|
|
|
|
|
|
|
|
|
// 持续读取流数据
|
|
|
|
|
while (true) {
|
|
|
|
|
@@ -105,13 +124,34 @@ export default {
|
|
|
|
|
_this.handleReconnect(); // 触发重连
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// 处理 SSE 事件数据
|
|
|
|
|
const data = decoder.decode(value);
|
|
|
|
|
console.log('收到消息:', data);
|
|
|
|
|
if (_this.isValidData(data)){
|
|
|
|
|
_this.upDateMsg(data);
|
|
|
|
|
// const data = decoder.decode(value);
|
|
|
|
|
// console.log('收到消息:', data);
|
|
|
|
|
|
|
|
|
|
receivedBytes += value.length;
|
|
|
|
|
console.log(
|
|
|
|
|
`收到数据块: ${value.length} 字节, 累计: ${receivedBytes} 字节`
|
|
|
|
|
);
|
|
|
|
|
const chunk = decoder.decode(value, { stream: true });
|
|
|
|
|
buffer += chunk;
|
|
|
|
|
|
|
|
|
|
// 处理完整的消息
|
|
|
|
|
const messages = buffer.split('\n\n'); // SSE 消息以双换行分隔
|
|
|
|
|
buffer = messages.pop() || ''; // 保留最后一个不完整的消息
|
|
|
|
|
|
|
|
|
|
for (const message of messages) {
|
|
|
|
|
if (_this.isValidData(message)) {
|
|
|
|
|
// console.log('完整 SSE 消息:', message);
|
|
|
|
|
_this.upDateMsg(message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理 SSE 事件数据
|
|
|
|
|
// const data = decoder.decode(value);
|
|
|
|
|
// console.log('收到消息:', data);
|
|
|
|
|
// if (_this.isValidData(data)){
|
|
|
|
|
// _this.upDateMsg(data);
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// 主动中止的请求不报错
|
|
|
|
|
if (error.name === 'AbortError') return;
|
|
|
|
|
@@ -153,66 +193,66 @@ export default {
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const dataObj = JSON.parse(jsonStr);
|
|
|
|
|
this.dataObj = dataObj
|
|
|
|
|
console.log('dataObj',this.dataObj)
|
|
|
|
|
this.dataObj = dataObj;
|
|
|
|
|
console.log('dataObj', this.dataObj);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('JSON 解析失败:', e);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
change() {
|
|
|
|
|
this.isFullScreen = screenfull.isFullscreen
|
|
|
|
|
this.isFullScreen = screenfull.isFullscreen;
|
|
|
|
|
},
|
|
|
|
|
init() {
|
|
|
|
|
if (!screenfull.isEnabled) {
|
|
|
|
|
this.$message({
|
|
|
|
|
message: 'you browser can not work',
|
|
|
|
|
type: 'warning'
|
|
|
|
|
})
|
|
|
|
|
return false
|
|
|
|
|
type: 'warning',
|
|
|
|
|
});
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
screenfull.on('change', this.change)
|
|
|
|
|
screenfull.on('change', this.change);
|
|
|
|
|
},
|
|
|
|
|
destroy() {
|
|
|
|
|
if (!screenfull.isEnabled) {
|
|
|
|
|
this.$message({
|
|
|
|
|
message: 'you browser can not work',
|
|
|
|
|
type: 'warning'
|
|
|
|
|
})
|
|
|
|
|
return false
|
|
|
|
|
type: 'warning',
|
|
|
|
|
});
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
screenfull.off('change', this.change)
|
|
|
|
|
screenfull.off('change', this.change);
|
|
|
|
|
},
|
|
|
|
|
// 全屏
|
|
|
|
|
screenfullChange() {
|
|
|
|
|
if (!screenfull.isEnabled) {
|
|
|
|
|
this.$message({
|
|
|
|
|
message: 'you browser can not work',
|
|
|
|
|
type: 'warning'
|
|
|
|
|
})
|
|
|
|
|
return false
|
|
|
|
|
type: 'warning',
|
|
|
|
|
});
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
screenfull.toggle(this.$refs.dataBoardBoxB)
|
|
|
|
|
screenfull.toggle(this.$refs.dataBoardBoxB);
|
|
|
|
|
},
|
|
|
|
|
resetSize() {
|
|
|
|
|
const dataBoardBox = document.getElementById('dataBoardBox')
|
|
|
|
|
const rw = parseFloat(window.innerWidth)
|
|
|
|
|
const rh = parseFloat(window.innerHeight)
|
|
|
|
|
const bw = parseFloat(dataBoardBox.style.width)
|
|
|
|
|
const bh = parseFloat(dataBoardBox.style.height)
|
|
|
|
|
let wx = 0
|
|
|
|
|
let hy = 0
|
|
|
|
|
const dataBoardBox = document.getElementById('dataBoardBox');
|
|
|
|
|
const rw = parseFloat(window.innerWidth);
|
|
|
|
|
const rh = parseFloat(window.innerHeight);
|
|
|
|
|
const bw = parseFloat(dataBoardBox.style.width);
|
|
|
|
|
const bh = parseFloat(dataBoardBox.style.height);
|
|
|
|
|
let wx = 0;
|
|
|
|
|
let hy = 0;
|
|
|
|
|
if (screenfull.isFullscreen) {
|
|
|
|
|
wx = rw / bw
|
|
|
|
|
hy = rh / bh
|
|
|
|
|
wx = rw / bw;
|
|
|
|
|
hy = rh / bh;
|
|
|
|
|
} else {
|
|
|
|
|
if (this.$store.state.app.sidebar.opened) {
|
|
|
|
|
wx = (rw - 283) / bw
|
|
|
|
|
wx = (rw - 283) / bw;
|
|
|
|
|
} else {
|
|
|
|
|
wx = (rw - 88) / bw
|
|
|
|
|
wx = (rw - 88) / bw;
|
|
|
|
|
}
|
|
|
|
|
hy = (rh - 150) / bh
|
|
|
|
|
hy = (rh - 150) / bh;
|
|
|
|
|
}
|
|
|
|
|
this.scaleNum = wx < hy ? wx : hy
|
|
|
|
|
this.scaleNum = wx < hy ? wx : hy;
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|