250 lines
6.9 KiB
Vue
250 lines
6.9 KiB
Vue
<template>
|
|
<div class='home-box'>
|
|
<div id="homeComtainerB" ref="homeComtainerB">
|
|
<div class='home-comtainer' id="homeComtainer" style="width: 1920px; height: 1080px"
|
|
:style="{ transform: 'scale(' + scaleNum + ')' }">
|
|
<HomeHeader :isFullScreen="isFullScreen" @screenfullChange="screenfullChange" />
|
|
<div class='line-one'>
|
|
<LossSum style='margin-right: 16px;' :dataObj="dataObj.workShopData" />
|
|
<EqAlarm style='margin-right: 16px;' :dataObj="dataObj.alarmData" />
|
|
<Count :dataObj="dataObj.downLoadNum" />
|
|
</div>
|
|
<div class='line-two'>
|
|
<div>
|
|
<SectionInputAndOutput style='margin-bottom: 16px;' :dataObj="dataObj.workShopData" />
|
|
<LineRate :dataObj="dataObj.lineHours"/>
|
|
</div>
|
|
<LineInpurAndOutput :dataObj="dataObj.productionLineData" style='margin-left: 16px;' />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</template>
|
|
<script>
|
|
import HomeHeader from './components/HomeHeader.vue';
|
|
import LossSum from './components/LossSum.vue';
|
|
import EqAlarm from './components/EqAlarm.vue';
|
|
import Count from './components/Count.vue';
|
|
import SectionInputAndOutput from './components/SectionInputAndOutput.vue';
|
|
import LineRate from './components/LineRate.vue';
|
|
import LineInpurAndOutput from './components/LineInpurAndOutput.vue';
|
|
import { debounce } from '@/utils/debounce';
|
|
import screenfull from 'screenfull';
|
|
import { getAccessToken } from '@/utils/auth';
|
|
import store from "@/store";
|
|
export default {
|
|
name: 'Home',
|
|
components: {
|
|
HomeHeader,
|
|
LossSum,
|
|
EqAlarm,
|
|
Count,
|
|
SectionInputAndOutput,
|
|
LineRate,
|
|
LineInpurAndOutput
|
|
},
|
|
data() {
|
|
return {
|
|
isFullScreen: false,
|
|
scaleNum: 1,
|
|
dataObj: {},
|
|
sseReader: null, // 保存流读取器
|
|
abortController: null, // 用于中止 fetch 请求
|
|
retryCount: 0, // 当前重试次数
|
|
isDestroyed: false // 标记组件是否已销毁
|
|
};
|
|
},
|
|
created() {
|
|
this.init();
|
|
this.getData()
|
|
},
|
|
mounted() {
|
|
this.boxReset();
|
|
window.addEventListener('resize', this.boxReset);
|
|
// this.boxReset = debounce(() => {
|
|
// this.resetSize()
|
|
// }, 300)
|
|
// this.boxReset()
|
|
// window.addEventListener('resize', () => {
|
|
// this.boxReset()
|
|
// })
|
|
this.getData()
|
|
},
|
|
destroyed() {
|
|
window.removeEventListener('resize', this.boxReset);
|
|
},
|
|
methods: {
|
|
boxReset() {
|
|
debounce(() => {
|
|
this.resetSize();
|
|
}, 300)();
|
|
},
|
|
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 headers = new Headers({
|
|
'Authorization': `Bearer ${token}`,
|
|
'Content-Type': 'text/event-stream'
|
|
});
|
|
try {
|
|
// 创建中止控制器
|
|
this.abortController = new AbortController();
|
|
// 发起 fetch 请求(替换为你的接口地址)
|
|
const response = await fetch(url, {
|
|
method: 'GET',
|
|
headers: headers,
|
|
signal: _this.abortController.signal // 绑定中止信号
|
|
});
|
|
|
|
// 获取流读取器
|
|
_this.sseReader = response.body.getReader();
|
|
const decoder = new TextDecoder();
|
|
|
|
// 持续读取流数据
|
|
while (true) {
|
|
const { done, value } = await _this.sseReader.read();
|
|
if (done) {
|
|
console.log('SSE 连接正常关闭');
|
|
_this.handleReconnect(); // 触发重连
|
|
break;
|
|
}
|
|
// 处理 SSE 事件数据
|
|
const data = decoder.decode(value);
|
|
console.log('收到消息:', data);
|
|
if (_this.isValidData(data)) {
|
|
_this.upDateMsg(data);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
// 主动中止的请求不报错
|
|
if (error.name === 'AbortError') return;
|
|
console.error('SSE 连接异常:', error);
|
|
_this.handleReconnect(); // 触发重连
|
|
}
|
|
},
|
|
closeSSE() {
|
|
this.isDestroyed = true; // 标记销毁
|
|
if (this.abortController) {
|
|
this.abortController.abort(); // 中止 fetch 请求
|
|
}
|
|
if (this.sseReader) {
|
|
this.sseReader.cancel(); // 关闭流读取器
|
|
this.sseReader = null;
|
|
}
|
|
console.log('SSE 连接已强制关闭');
|
|
},
|
|
handleReconnect() {
|
|
if (this.isDestroyed) return;
|
|
// 指数退避策略(最大重试 5 次)
|
|
const maxRetries = 5;
|
|
if (this.retryCount < maxRetries) {
|
|
const delay = Math.pow(2, this.retryCount) * 1000;
|
|
setTimeout(() => {
|
|
this.retryCount++;
|
|
this.initSSE();
|
|
}, delay);
|
|
} else {
|
|
console.error('SSE 重连次数已达上限');
|
|
}
|
|
},
|
|
isValidData(data) {
|
|
return data.trim().startsWith('data:{') && !data.includes('heartbeat');
|
|
},
|
|
upDateMsg(data) {
|
|
const jsonStr = data.replace(/^data:/, '').trim();
|
|
console.log('jsonStr', jsonStr);
|
|
|
|
try {
|
|
const dataObj = JSON.parse(jsonStr);
|
|
this.dataObj = dataObj
|
|
console.log('dataObj', this.dataObj)
|
|
} catch (e) {
|
|
console.error('JSON 解析失败:', e);
|
|
}
|
|
},
|
|
change() {
|
|
this.isFullScreen = screenfull.isFullscreen;
|
|
},
|
|
init() {
|
|
if (screenfull.isEnabled) {
|
|
screenfull.on('change', this.change);
|
|
}
|
|
},
|
|
destroy() {
|
|
if (screenfull.isEnabled) {
|
|
screenfull.off('change', this.change);
|
|
}
|
|
},
|
|
// 全屏
|
|
screenfullChange() {
|
|
if (!screenfull.isEnabled) {
|
|
this.$message({
|
|
message: 'you browser can not work',
|
|
type: 'warning',
|
|
});
|
|
return false;
|
|
}
|
|
screenfull.toggle(this.$refs.homeComtainerB);
|
|
},
|
|
resetSize() {
|
|
let coldContainerBox = document.getElementById('homeComtainer');
|
|
let rw = parseFloat(window.innerWidth);
|
|
let rh = parseFloat(window.innerHeight);
|
|
let bw = parseFloat(coldContainerBox.style.width);
|
|
let bh = parseFloat(coldContainerBox.style.height);
|
|
let wx = 0;
|
|
let hx = 0;
|
|
if (screenfull.isFullscreen) {
|
|
wx = rw / bw;
|
|
hx = rh / bh;
|
|
} else {
|
|
if (this.$store.state.app.sidebar.opened) {
|
|
wx = (rw - 280) / bw;
|
|
hx = (rh - 116) / bh;
|
|
} else {
|
|
wx = (rw - 85) / bw;
|
|
hx = (rh - 116) / bh;
|
|
}
|
|
}
|
|
this.scaleNum = wx;
|
|
},
|
|
},
|
|
watch: {
|
|
'$store.state.app.sidebar.opened': {
|
|
handler(newVal, oldVal) {
|
|
this.boxReset();
|
|
},
|
|
immediate: true
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
<style lang='scss' scoped>
|
|
.home-box {
|
|
min-height: calc(100vh - 56px - 72px);
|
|
min-width: calc(100vh - 280px);
|
|
background-color: #F2F4F9;
|
|
}
|
|
.home-comtainer{
|
|
background-color: #F2F4F9;
|
|
position: absolute;
|
|
transform-origin: 16px 8px;
|
|
top: 0px;
|
|
left: 0px;
|
|
padding-left: 16px;
|
|
padding-top: 10px;
|
|
overflow: hidden;
|
|
.line-one {
|
|
display: flex;
|
|
margin-top: 16px;
|
|
}
|
|
.line-two {
|
|
display: flex;
|
|
margin-top: 16px;
|
|
}
|
|
}
|
|
</style>
|