1.在前端文件夹中RuoYi-Vue/ruoyi-ui/public创建相应的包
例如:我在这就创建了一个monitor包,并放入了相应的index.html模板
如图所示:
2.在首页创建了一个按钮,点击按钮
触发goTarget方法,跳转到相应页面
3.index.vue页面,主要用于在页面中嵌入一个 iframe(内嵌框架),并通过 postMessage 实现父子页面间的安全通信(传递身份认证 Token)
1. 组件功能概述
核心作用:嵌入一个监控页面(
/monitor/liujiaxia/index.html
),并将当前用户的认证 Token 从父页面传递到 iframe 子页面。安全机制:通过
localStorage
+postMessage
双保险传递 Token,确保 iframe 内页面能获取到授权信息。
<template>
<div class="app-container">
<div class="monitor-container">
<iframe
ref="monitorIframe"
src="/monitor/liujiaxia/index.html"
frameborder="0"
width="100%"
height="100%"
style="border: none;"
@load="onIframeLoad"
></iframe>
</div>
</div>
</template>
<script>
import { getToken } from '@/utils/auth'
export default {
name: "LiuJiaXiaMonitor",
data() {
return {
token: null
};
},
mounted() {
this.token = getToken();
// 進入iframe時將token寫入localStorage
localStorage.setItem('iframe_token', this.token);
// 監聽iframe的請求
window.addEventListener('message', this.onMessageFromIframe);
},
beforeDestroy() {
window.removeEventListener('message', this.onMessageFromIframe);
},
methods: {
onIframeLoad() {
this.sendTokenToIframe();
},
onMessageFromIframe(event) {
if (event.data && event.data.type === 'REQUEST_AUTH_TOKEN') {
this.sendTokenToIframe();
}
},
sendTokenToIframe() {
const iframe = this.$refs.monitorIframe;
if (iframe && iframe.contentWindow) {
try {
console.log('[iframe] 發送REQUEST_AUTH_TOKEN給父頁面');
iframe.contentWindow.postMessage({
type: 'AUTH_TOKEN',
token: this.token
}, '*');
} catch (error) {
console.error('向iframe傳遞token失敗:', error);
}
}
}
}
};
</script>
<style scoped>
.monitor-container {
width: 100%;
height: calc(100vh - 120px);
overflow: hidden;
}
iframe {
width: 100%;
height: 100%;
border: none;
}
</style>
4.在HTML页面中,插入一个折线图
<div style="width:100%;height:100%;margin-top:-40px;" id="dataChart" dg-chart-theme="{'color':'#faf7fa','actualBackgroundColor':'#030303'}">
<!--最新200条数据折现图-->
</div>
5.由于这是一个静态页面需要加入axios来请求后端的API
<script src="/monitor/liujiaxia/js/jquery.min.js"></script>
<script src="/monitor/liujiaxia/js/echarts.min.js"></script>
<script src="/monitor/liujiaxia/js/fq.js"></script>
<script src="/monitor/liujiaxia/js/axios.min.js"></script>
<script>
var lazyFun;
var authToken = localStorage.getItem('iframe_token');
if (!authToken) {
window.parent.postMessage({ type: 'REQUEST_AUTH_TOKEN' }, '*');
}
function init(el, width, height) {
var _el = document.getElementById(el);
var hScale = window.innerHeight / height;
var wScale = window.innerWidth / width;
_el.style.transform = 'scale(' + wScale + ',' + hScale + ')'
}
init('mainbody', 1920, 1080);
window.onresize = function() {
clearTimeout(lazyFun);
lazyFun = setTimeout(function() {
init('mainbody', 1920, 1080)
}, 600);
};
// 監聽來自父頁面的消息
window.addEventListener('message', function(event) {
if (event.data && event.data.type === 'AUTH_TOKEN') {
var wasTokenMissing = !authToken;
authToken = event.data.token;
localStorage.setItem('iframe_token', authToken); // 写入localStorage,保存刷新後還在
if (wasTokenMissing) {
fetchLatestData();
}
}
});
// 初始化数据图表
var dataChart = echarts.init(document.getElementById('dataChart'));
//
function fetchLatestData() {
if (!authToken) {
console.error('[iframe] 未獲取到認證token,無法請求數據');
return;
}
// 使用axios調用後端接口
axios.get('/dev-api/screen/list2', {
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + authToken
}
})
.then(function(response) {
console.log('获取数据成功:', response.data.data);
// 假設返回的數據格式為數組,取最新200條
var data = response.data.data ? response.data.data.slice(-200) : response.data.slice(-200);
updateChart(data);
})
.catch(function(error) {
console.error('获取数据失敗:', error);
// 接口失敗時直接報錯,不顯示模擬數據
throw error;
});
}
// 更新圖表
function updateChart(data) {
// 数据格式:使用dateTime作为时间,data1作为数值
var times = data.map(function(item) {
// 將ISO时间格式转换为更友好的显示格式
var date = new Date(item.dateTime);
return date.toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
});
var values = data.map(function(item) { return item.data1; });
var option = {
backgroundColor: 'transparent',
title: {
text: '最新数据监测',
textStyle: {
color: '#faf7fa',
fontSize: 14
},
left: 'center'
},
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(0,0,0,0.8)',
borderColor: '#faf7fa',
textStyle: {
color: '#faf7fa'
},
formatter: function(params) {
var dataIndex = params[0].dataIndex;
var originalData = data[dataIndex];
return '时间: ' + originalData.dateTime + '<br/>' +
'数值: ' + params[0].value;
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: times,
axisLine: {
lineStyle: {
color: '#faf7fa'
}
},
axisLabel: {
color: '#faf7fa',
fontSize: 10,
rotate: 45
}
},
yAxis: {
type: 'value',
axisLine: {
lineStyle: {
color: '#faf7fa'
}
},
axisLabel: {
color: '#faf7fa'
},
splitLine: {
lineStyle: {
color: 'rgba(250,247,250,0.2)'
}
}
},
series: [{
name: '检測值',
type: 'line',
data: values,
smooth: true,
lineStyle: {
color: '#00ff00',
width: 2
},
itemStyle: {
color: '#00ff00'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0, color: 'rgba(0,255,0,0.3)'
}, {
offset: 1, color: 'rgba(0,255,0,0.1)'
}]
}
}
}]
};
dataChart.setOption(option);
}
// 頁面加載完成後初始化圖表
$(document).ready(function() {
// 如果localStorage中已有token,則立即獲取數據
if (authToken) {
fetchLatestData();
}
// 每30秒更新一次數據(但需要檢查token)
setInterval(function() {
if (authToken) {
fetchLatestData();
}
}, 30000);
});
</script>