ubuntu@VM-0-6-ubuntu:~$ curl -v http://127.0.0.1:8080/AI-rotator.html
* Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080
> GET /AI-rotator.html HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.29.2
< Date: Thu, 27 Nov 2025 07:58:37 GMT
< Content-Type: text/html
< Content-Length: 18876
< Last-Modified: Fri, 07 Nov 2025 00:58:21 GMT
< Connection: keep-alive
< Vary: Accept-Encoding
< ETag: "690d442d-49bc"
< Content-Security-Policy: frame-ancestors *; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: http: https:; font-src 'self'; object-src 'none';
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI分播机轮播系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;
}
body {
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
color: #e2e8f0;
min-height: 100vh;
overflow-x: hidden;
}
.container {
max-width: 3000px;
margin: 0 auto;
padding: 0.1rem;
}
header {
text-align: center;
margin-bottom: 0rem;
padding: 0rem;
}
h1 {
font-size: 0rem;
font-weight: 300;
margin-bottom: 0rem;
background: linear-gradient(90deg, #818cf8 0%, #60a5fa 100%);
-webkit-text-fill-color: transparent;
letter-spacing: 1px;
}
.subtitle {
color: #94a3b8;
font-size: 0rem;
max-width: 600px;
margin: 0 auto;
font-weight: 300;
}
.dashboard-container {
position: relative;
width: 100%;
height: 94vh;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
margin: 0 auto;
}
.dashboard-slide {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
transition: opacity 1.2s ease;
background: #1e293b;
border-radius: 16px;
display: flex;
flex-direction: column;
}
.dashboard-slide.active {
opacity: 1;
z-index: 10;
}
.dashboard-header {
padding: 1rem 1.5rem;
background: rgba(30, 41, 59, 0.9);
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
display: flex;
justify-content: space-between;
align-items: center;
}
.dashboard-title {
font-size: 1.4rem;
font-weight: 400;
color: #e2e8f0;
letter-spacing: 0.5px;
}
.datetime-display {
font-size: 1.1rem;
color: #cbd5e1;
text-align: right;
min-width: 280px;
}
.date-part {
font-weight: 500;
margin-right: 10px;
}
.time-part {
font-weight: 600;
color: #60a5fa;
font-family: 'Courier New', monospace;
}
.dashboard-content {
flex: 1;
overflow: hidden;
background: #0f172a;
position: relative;
}
.dashboard-iframe {
width: 100%;
height: 100%;
border: none;
}
.indicators {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 0.5rem;
}
.indicator {
width: 10px;
height: 10px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.15);
cursor: default;
transition: all 0.4s ease;
}
.indicator.active {
background: #60a5fa;
transform: scale(1.4);
box-shadow: 0 0 12px rgba(96, 165, 250, 0.6);
}
footer {
text-align: center;
margin-top: 3rem;
padding: 2rem;
color: #64748b;
font-size: 0.9rem;
border-top: 1px solid rgba(255, 255, 255, 0.05);
}
@media (max-width: 1024px) {
.dashboard-container {
height: 95vh;
}
h1 {
font-size: 0rem;
}
.dashboard-header {
padding: 0.8rem 1.2rem;
}
.dashboard-title {
font-size: 1.2rem;
}
.datetime-display {
font-size: 1rem;
min-width: 240px;
}
}
@media (max-width: 768px) {
.container {
padding: 1.5rem;
}
h1 {
font-size: 2rem;
}
.dashboard-container {
height: 55vh;
border-radius: 12px;
}
.dashboard-header {
padding: 0.6rem 1rem;
flex-direction: column;
gap: 0.5rem;
}
.dashboard-title {
font-size: 1.1rem;
text-align: center;
}
.datetime-display {
font-size: 0.9rem;
text-align: center;
min-width: auto;
}
}
/* 加载动画 */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.dashboard-slide.active {
animation: fadeIn 1.2s ease;
}
/* 状态指示器样式 */
.indicator[data-status="preloading"] {
background: #fbbf24 !important; /* 黄色-加载中 */
animation: pulse 1.5s infinite;
}
.indicator[data-status="preloaded"] {
background: #10b981 !important; /* 绿色-预加载完成 */
}
.indicator[data-status="error"] {
background: #ef4444 !important; /* 红色-加载失败 */
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>数据可视化平台</h1>
<p class="subtitle">实时业务监控与数据分析</p>
</header>
<div class="dashboard-container" id="dashboard-container">
<!-- 看板将通过JS动态添加 -->
</div>
<div class="indicators" id="indicators">
<!-- 指示器将通过JS动态添加 -->
</div>
</div>
<script>
// 看板数据
const dashboardUrls = [
{
url: "/superset/dashboard/2/?standalone=3&show_filters=0",
title: "AI分播机作业实况"
},
{
url: "/superset/dashboard/2/?standalone=3&show_filters=0",
title: "AI分播机作业实况"
}
];
// 配置参数
const rotationInterval = 15000; // 60秒切换
const preloadTime = 15000; // 在切换前50秒开始预加载
const loadTimeout = 15000; // 30秒加载超时
let currentIndex = 0;
let timerId = null;
let preloadTimerId = null;
let datetimeTimerId = null;
// 初始化看板
function initDashboards() {
const dashboardContainer = document.getElementById('dashboard-container');
const indicatorsContainer = document.getElementById('indicators');
dashboardUrls.forEach((dashboard, index) => {
// 创建看板幻灯片
const slide = document.createElement('div');
slide.className = `dashboard-slide ${index === 0 ? 'active' : ''}`;
slide.innerHTML = `
<div class="dashboard-header">
<div class="dashboard-title">${dashboard.title}</div>
<div class="datetime-display">
<span class="date-part" id="date-display-${index}">${getFormattedDate()}</span>
<span class="time-part" id="time-display-${index}">${getFormattedTime()}</span>
</div>
</div>
<div class="dashboard-content">
${index === 0 ?
`<iframe class="dashboard-iframe" src="${dashboard.url}" frameborder="0" data-loaded="true"></iframe>` :
`<div class="preload-placeholder" style="display: none;"></div>`
}
</div>
`;
dashboardContainer.appendChild(slide);
// 创建指示器
const indicator = document.createElement('div');
indicator.className = `indicator ${index === 0 ? 'active' : ''}`;
indicator.setAttribute('data-status', index === 0 ? 'loaded' : 'unloaded');
indicatorsContainer.appendChild(indicator);
});
}
// 获取格式化日期
function getFormattedDate() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const weekdays = ['日', '一', '二', '三', '四', '五', '六'];
const weekday = weekdays[now.getDay()];
return `${year}-${month}-${day} 周${weekday}`;
}
// 获取格式化时间
function getFormattedTime() {
const now = new Date();
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
return `${hours}:${minutes}:${seconds}`;
}
// 更新所有看板的日期时间显示
function updateAllDateTimeDisplays() {
const date = getFormattedDate();
const time = getFormattedTime();
// 更新所有看板的日期时间显示
dashboardUrls.forEach((_, index) => {
const dateElement = document.getElementById(`date-display-${index}`);
const timeElement = document.getElementById(`time-display-${index}`);
if (dateElement) dateElement.textContent = date;
if (timeElement) timeElement.textContent = time;
});
}
// 启动日期时间更新
function startDateTimeUpdate() {
// 立即更新一次
updateAllDateTimeDisplays();
// 每秒更新一次
datetimeTimerId = setInterval(updateAllDateTimeDisplays, 1000);
}
// 预加载指定看板
function preloadDashboard(index) {
const slide = document.querySelectorAll('.dashboard-slide')[index];
const placeholder = slide.querySelector('.preload-placeholder');
const indicator = document.querySelectorAll('.indicator')[index];
const currentStatus = indicator.getAttribute('data-status');
// 如果已经在加载或已加载,则跳过
if (currentStatus === 'preloaded' || currentStatus === 'loaded' || currentStatus === 'preloading') {
return;
}
// 更新状态为预加载中
indicator.setAttribute('data-status', 'preloading');
indicator.style.background = '#fbbf24';
// 创建iframe
const iframe = document.createElement('iframe');
iframe.className = 'dashboard-iframe';
iframe.style.display = 'none';
iframe.src = dashboardUrls[index].url;
iframe.setAttribute('data-index', index);
// 超时处理
const timeoutId = setTimeout(() => {
onIframeLoadComplete(index, 'timeout');
}, loadTimeout);
// 尝试使用 onload 事件
iframe.onload = function() {
clearTimeout(timeoutId);
onIframeLoadComplete(index, 'success');
};
iframe.onerror = function() {
clearTimeout(timeoutId);
onIframeLoadComplete(index, 'error');
};
// 移除placeholder并添加iframe
if (placeholder) {
placeholder.remove();
}
slide.querySelector('.dashboard-content').appendChild(iframe);
// 备用检测方案
startIframeContentCheck(index, iframe, timeoutId);
}
// iframe 加载完成处理
function onIframeLoadComplete(index, status) {
const indicator = document.querySelectorAll('.indicator')[index];
if (status === 'success') {
const iframe = document.querySelectorAll('.dashboard-slide')[index].querySelector('.dashboard-iframe');
if (iframe) {
iframe.style.display = 'block';
iframe.setAttribute('data-loaded', 'true');
}
indicator.setAttribute('data-status', 'preloaded');
indicator.style.background = '#10b981'; // 绿色-预加载完成
} else {
indicator.setAttribute('data-status', 'error');
indicator.style.background = '#ef4444'; // 红色-加载失败
}
}
// 切换到指定看板
function goToDashboard(index) {
const slides = document.querySelectorAll('.dashboard-slide');
const indicators = document.querySelectorAll('.indicator');
// 隐藏所有看板
slides.forEach(slide => slide.classList.remove('active'));
// 重置上一个看板的状态(如果不是预加载的下一个)
const prevIndex = currentIndex;
if (prevIndex !== index) {
const prevIndicator = indicators[prevIndex];
if (prevIndicator.getAttribute('data-status') === 'loaded') {
prevIndicator.setAttribute('data-status', 'unloaded');
prevIndicator.style.background = 'rgba(255, 255, 255, 0.15)';
// 卸载iframe以释放资源
const prevIframe = slides[prevIndex].querySelector('.dashboard-iframe');
if (prevIframe) {
prevIframe.src = 'about:blank';
prevIframe.remove();
// 重新添加placeholder
const contentDiv = slides[prevIndex].querySelector('.dashboard-content');
contentDiv.innerHTML = '<div class="preload-placeholder" style="display: none;"></div>';
}
}
}
// 显示新的看板
slides[index].classList.add('active');
// 更新当前看板状态
const currentIndicator = indicators[index];
if (currentIndicator.getAttribute('data-status') === 'preloaded') {
currentIndicator.setAttribute('data-status', 'loaded');
// 保持绿色,表示已加载并正在显示
}
// 更新当前索引
currentIndex = index;
// 更新指示器激活状态
indicators.forEach((indicator, i) => {
indicator.classList.toggle('active', i === index);
});
// 设置下一次预加载定时器(预加载下下一个看板)
setupPreloadTimer();
}
// 设置预加载定时器
function setupPreloadTimer() {
if (preloadTimerId) clearTimeout(preloadTimerId);
// 计算下一个要预加载的看板索引(当前的下一个)
const nextIndex = (currentIndex + 1) % dashboardUrls.length;
preloadTimerId = setTimeout(() => {
// 确保不是当前显示的看板
if (nextIndex !== currentIndex) {
preloadDashboard(nextIndex);
}
}, rotationInterval - preloadTime);
}
// 切换到下一个看板
function nextDashboard() {
const nextIndex = (currentIndex + 1) % dashboardUrls.length;
goToDashboard(nextIndex);
}
// 启动轮播
function startRotation() {
if (timerId) clearInterval(timerId);
timerId = setInterval(nextDashboard, rotationInterval);
// 初始预加载设置(预加载第二个看板)
if (dashboardUrls.length > 1) {
preloadTimerId = setTimeout(() => {
preloadDashboard(1); // 预加载第二个看板
}, rotationInterval - preloadTime);
}
}
// 初始化
function init() {
initDashboards();
startRotation();
startDateTimeUpdate(); // 启动日期时间更新
}
// 页面加载完成后初始化
window.addEventListener('load', init);
// 清理定时器
window.addEventListener('beforeunload', function() {
if (timerId) clearInterval(timerId);
if (preloadTimerId) clearTimeout(preloadTimerId);
if (datetimeTimerId) clearInterval(datetimeTimerId);
});
// 备用检测方案函数(需要实现)
function startIframeContentCheck(index, iframe, timeoutId) {
// 这里可以添加iframe内容检测的逻辑
// 例如定期检查iframe的尺寸或内容加载状态
}
</script>
</body>
* Connection #0 to host 127.0.0.1 left intact
</html>ubuntu@VM-0-6-ubucurl -v http://127.0.0.1:8079/1:8079/
* Trying 127.0.0.1:8079...
* connect to 127.0.0.1 port 8079 from 127.0.0.1 port 34808 failed: Connection refused
* Failed to connect to 127.0.0.1 port 8079 after 0 ms: Couldn't connect to server
* Closing connection
curl: (7) Failed to connect to 127.0.0.1 port 8079 after 0 ms: Couldn't connect to server
ubuntu@VM-0-6-ubuntu:~$ curl -v http://127.0.0.1:8088/superset
* Trying 127.0.0.1:8088...
* Connected to 127.0.0.1 (127.0.0.1) port 8088
> GET /superset HTTP/1.1
> Host: 127.0.0.1:8088
> User-Agent: curl/8.5.0
> Accept: */*
>
* Empty reply from server
* Closing connection
curl: (52) Empty reply from server
ubuntu@VM-0-6-ubuntu:~$
最新发布