JiangNan-DT/main.html
赵豪 040ad8958f V1.4版本
1.完善了画布自适应能力,避免画布过小时显示异常问题
2.增加了main.html测试嵌入孪生index.html效果
2025-10-13 11:21:30 +08:00

881 lines
25 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>江南造船厂数字孪生平台</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
height: 100vh;
overflow: hidden;
background: #0a0e27;
color: #fff;
}
/* 整体布局 */
.container {
display: grid;
grid-template-columns: 1fr 30%;
grid-template-rows: 60px 1fr 200px;
height: 100vh;
gap: 2px;
background: #000;
}
/* 顶部标题栏 */
.header {
grid-column: 1 / 3;
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}
.header-title {
font-size: 24px;
font-weight: bold;
letter-spacing: 2px;
}
.header-info {
display: flex;
gap: 30px;
font-size: 14px;
color: #a8d5ff;
}
.header-info span {
display: flex;
align-items: center;
gap: 5px;
}
/* 控制按钮 */
.header-controls {
display: flex;
gap: 10px;
align-items: center;
}
.control-btn {
background: rgba(79, 195, 247, 0.2);
border: 1px solid #4fc3f7;
color: #4fc3f7;
padding: 8px 16px;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
display: flex;
align-items: center;
gap: 6px;
transition: all 0.3s ease;
}
.control-btn:hover {
background: rgba(79, 195, 247, 0.3);
border-color: #64b5f6;
color: #64b5f6;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(79, 195, 247, 0.3);
}
.control-btn:active {
transform: translateY(0);
}
.control-btn svg {
width: 16px;
height: 16px;
}
/* 左侧数字孪生区域 */
.main-view {
grid-column: 1;
grid-row: 2;
background: #0a0e27;
position: relative;
overflow: hidden;
}
.main-view iframe {
width: 100%;
height: 100%;
border: none;
display: block;
}
/* 右侧属性面板 */
.property-panel {
grid-column: 2;
grid-row: 2 / 4;
background: linear-gradient(180deg, #1a1f3a 0%, #0f1329 100%);
padding: 20px;
overflow-y: auto;
border-left: 1px solid #2a3f5f;
}
.panel-title {
font-size: 18px;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid #2a5298;
color: #4fc3f7;
}
.property-item {
margin-bottom: 15px;
padding: 15px;
background: rgba(42, 63, 95, 0.3);
border-radius: 5px;
border-left: 3px solid #4fc3f7;
}
.property-label {
font-size: 12px;
color: #8ba9c7;
margin-bottom: 5px;
}
.property-value {
font-size: 16px;
color: #fff;
font-weight: bold;
}
.status-indicator {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 8px;
animation: pulse 2s infinite;
}
.status-normal {
background: #4caf50;
}
.status-warning {
background: #ff9800;
}
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
/* 底部数据面板 */
.data-panel {
grid-column: 1;
grid-row: 3;
background: linear-gradient(180deg, #1a1f3a 0%, #0f1329 100%);
padding: 15px 20px;
border-top: 1px solid #2a3f5f;
overflow-y: auto;
}
.data-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
.data-card {
background: rgba(42, 63, 95, 0.3);
padding: 15px;
border-radius: 5px;
border: 1px solid #2a5298;
transition: transform 0.2s;
}
.data-card:hover {
transform: translateY(-2px);
border-color: #4fc3f7;
}
.data-card-title {
font-size: 12px;
color: #8ba9c7;
margin-bottom: 8px;
}
.data-card-value {
font-size: 20px;
font-weight: bold;
color: #4fc3f7;
}
.data-card-unit {
font-size: 12px;
color: #8ba9c7;
margin-left: 5px;
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: #0a0e27;
}
::-webkit-scrollbar-thumb {
background: #2a5298;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #4fc3f7;
}
/* 图表容器 */
.chart-container {
margin: 20px 0;
padding: 15px;
background: rgba(42, 63, 95, 0.2);
border-radius: 8px;
border: 1px solid #2a5298;
}
.chart-title {
font-size: 14px;
color: #4fc3f7;
margin-bottom: 15px;
font-weight: bold;
}
/* 进度条图表 */
.progress-bar-chart {
margin: 10px 0;
}
.progress-label {
display: flex;
justify-content: space-between;
font-size: 12px;
color: #8ba9c7;
margin-bottom: 5px;
}
.progress-bar-bg {
width: 100%;
height: 20px;
background: rgba(0, 0, 0, 0.3);
border-radius: 10px;
overflow: hidden;
position: relative;
}
.progress-bar-fill {
height: 100%;
background: linear-gradient(90deg, #4fc3f7, #2196f3);
border-radius: 10px;
transition: width 0.3s ease;
position: relative;
overflow: hidden;
}
.progress-bar-fill::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
animation: shimmer 2s infinite;
}
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
/* 环形图表 */
.ring-chart {
display: flex;
align-items: center;
gap: 20px;
margin: 15px 0;
}
.ring-svg {
width: 80px;
height: 80px;
transform: rotate(-90deg);
}
.ring-background {
fill: none;
stroke: rgba(0, 0, 0, 0.3);
stroke-width: 8;
}
.ring-progress {
fill: none;
stroke: url(#gradient);
stroke-width: 8;
stroke-linecap: round;
transition: stroke-dashoffset 0.5s ease;
}
.ring-info {
flex: 1;
}
.ring-value {
font-size: 24px;
font-weight: bold;
color: #4fc3f7;
}
.ring-label {
font-size: 12px;
color: #8ba9c7;
margin-top: 5px;
}
/* 折线图样式 */
.line-chart {
height: 120px;
display: flex;
align-items: flex-end;
gap: 4px;
padding: 10px 0;
}
.line-bar {
flex: 1;
background: linear-gradient(to top, #2196f3, #4fc3f7);
border-radius: 3px 3px 0 0;
transition: height 0.3s ease;
position: relative;
min-height: 10px;
}
.line-bar:hover {
background: linear-gradient(to top, #4fc3f7, #64b5f6);
cursor: pointer;
}
/* 温度计样式 */
.thermometer {
display: flex;
gap: 15px;
align-items: center;
padding: 10px 0;
}
.thermometer-bar {
width: 30px;
height: 150px;
background: rgba(0, 0, 0, 0.3);
border-radius: 15px;
position: relative;
overflow: hidden;
}
.thermometer-fill {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(to top, #f44336, #ff9800, #4caf50);
border-radius: 15px;
transition: height 0.5s ease;
}
.thermometer-info {
flex: 1;
}
.thermometer-value {
font-size: 32px;
font-weight: bold;
color: #4fc3f7;
}
.thermometer-desc {
font-size: 12px;
color: #8ba9c7;
margin-top: 5px;
}
</style>
</head>
<body>
<div class="container">
<!-- 顶部标题栏 -->
<div class="header">
<div class="header-title">江南造船厂数字孪生平台</div>
<div class="header-info">
<span>
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" />
</svg>
系统状态: 正常运行
</span>
<span>
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z" />
</svg>
当前时间: <span id="current-time"></span>
</span>
<span>
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path
d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z" />
</svg>
上海市 江南造船厂
</span>
</div>
<div class="header-controls">
<button class="control-btn" id="refresh-btn" title="刷新数字孪生">
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/>
</svg>
刷新
</button>
<button class="control-btn" id="fullscreen-btn" title="全屏数字孪生">
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>
</svg>
全屏
</button>
</div>
</div>
<!-- 左侧数字孪生主视图 -->
<div class="main-view">
<iframe src="index.html" title="江南造船厂数字孪生"></iframe>
</div>
<!-- 右侧属性面板 -->
<div class="property-panel">
<h3 class="panel-title">实时监控面板</h3>
<!-- 设备基础信息 -->
<div class="property-item">
<div class="property-label">设备名称</div>
<div class="property-value">龙门吊 #3</div>
</div>
<div class="property-item">
<div class="property-label">设备状态</div>
<div class="property-value">
<span class="status-indicator status-normal"></span>
运行中
</div>
</div>
<!-- 设备利用率环形图 -->
<div class="chart-container">
<div class="chart-title">设备利用率</div>
<div class="ring-chart">
<svg class="ring-svg" viewBox="0 0 100 100">
<defs>
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#4fc3f7;stop-opacity:1" />
<stop offset="100%" style="stop-color:#2196f3;stop-opacity:1" />
</linearGradient>
</defs>
<circle class="ring-background" cx="50" cy="50" r="40"></circle>
<circle class="ring-progress" cx="50" cy="50" r="40" stroke-dasharray="251.2" stroke-dashoffset="62.8"
id="utilization-ring"></circle>
</svg>
<div class="ring-info">
<div class="ring-value" id="utilization-value">75%</div>
<div class="ring-label">当前利用率</div>
</div>
</div>
</div>
<!-- 负载进度条 -->
<div class="chart-container">
<div class="chart-title">负载状态</div>
<div class="progress-bar-chart">
<div class="progress-label">
<span>当前负载</span>
<span id="load-value">45.8 吨</span>
</div>
<div class="progress-bar-bg">
<div class="progress-bar-fill" style="width: 38%;" id="load-bar"></div>
</div>
<div class="progress-label" style="margin-top: 5px;">
<span>最大: 120.0 吨</span>
<span id="load-percent">38%</span>
</div>
</div>
</div>
<!-- 能耗温度计 -->
<div class="chart-container">
<div class="chart-title">实时能耗</div>
<div class="thermometer">
<div class="thermometer-bar">
<div class="thermometer-fill" style="height: 62%;" id="power-fill"></div>
</div>
<div class="thermometer-info">
<div class="thermometer-value" id="power-value">185</div>
<div class="thermometer-desc">kW / 300 kW</div>
<div class="thermometer-desc" style="margin-top: 10px;">
<span class="status-indicator status-warning"></span>
中等负荷
</div>
</div>
</div>
</div>
<!-- 24小时运行趋势 -->
<div class="chart-container">
<div class="chart-title">24小时运行趋势</div>
<div class="line-chart" id="trend-chart">
<div class="line-bar" style="height: 60%;" data-value="60"></div>
<div class="line-bar" style="height: 75%;" data-value="75"></div>
<div class="line-bar" style="height: 55%;" data-value="55"></div>
<div class="line-bar" style="height: 80%;" data-value="80"></div>
<div class="line-bar" style="height: 70%;" data-value="70"></div>
<div class="line-bar" style="height: 85%;" data-value="85"></div>
<div class="line-bar" style="height: 65%;" data-value="65"></div>
<div class="line-bar" style="height: 90%;" data-value="90"></div>
<div class="line-bar" style="height: 75%;" data-value="75"></div>
<div class="line-bar" style="height: 68%;" data-value="68"></div>
<div class="line-bar" style="height: 78%;" data-value="78"></div>
<div class="line-bar" style="height: 82%;" data-value="82"></div>
</div>
</div>
<!-- 运行时长进度 -->
<div class="chart-container">
<div class="chart-title">今日运行时长</div>
<div class="progress-bar-chart">
<div class="progress-label">
<span>已运行</span>
<span id="runtime-value">6.5 小时</span>
</div>
<div class="progress-bar-bg">
<div class="progress-bar-fill" style="width: 65%;" id="runtime-bar"></div>
</div>
<div class="progress-label" style="margin-top: 5px;">
<span>计划: 10.0 小时</span>
<span>65%</span>
</div>
</div>
</div>
<!-- 其他信息 -->
<div class="property-item">
<div class="property-label">维护状态</div>
<div class="property-value">良好</div>
</div>
<div class="property-item">
<div class="property-label">上次维护</div>
<div class="property-value">2025-10-01</div>
</div>
<div class="property-item">
<div class="property-label">操作人员</div>
<div class="property-value">张三 (工号: 2024001)</div>
</div>
</div>
<!-- 底部数据面板 -->
<div class="data-panel">
<div class="data-grid">
<div class="data-card">
<div class="data-card-title">实时产能</div>
<div>
<span class="data-card-value">1,245</span>
<span class="data-card-unit">件/日</span>
</div>
</div>
<div class="data-card">
<div class="data-card-title">设备利用率</div>
<div>
<span class="data-card-value">87.5</span>
<span class="data-card-unit">%</span>
</div>
</div>
<div class="data-card">
<div class="data-card-title">在线设备</div>
<div>
<span class="data-card-value">24/28</span>
<span class="data-card-unit"></span>
</div>
</div>
<div class="data-card">
<div class="data-card-title">当前作业人员</div>
<div>
<span class="data-card-value">156</span>
<span class="data-card-unit"></span>
</div>
</div>
<div class="data-card">
<div class="data-card-title">今日能耗</div>
<div>
<span class="data-card-value">3,842</span>
<span class="data-card-unit">kWh</span>
</div>
</div>
<div class="data-card">
<div class="data-card-title">安全预警</div>
<div>
<span class="data-card-value" style="color: #4caf50;">0</span>
<span class="data-card-unit"></span>
</div>
</div>
<div class="data-card">
<div class="data-card-title">完工进度</div>
<div>
<span class="data-card-value">68.3</span>
<span class="data-card-unit">%</span>
</div>
</div>
<div class="data-card">
<div class="data-card-title">质量合格率</div>
<div>
<span class="data-card-value">99.2</span>
<span class="data-card-unit">%</span>
</div>
</div>
</div>
</div>
</div>
<script>
// 更新当前时间
function updateTime() {
const now = new Date();
const timeString = now.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
});
document.getElementById('current-time').textContent = timeString;
}
updateTime();
setInterval(updateTime, 1000);
// 更新环形图
function updateRingChart(percent) {
const ring = document.getElementById('utilization-ring');
const valueText = document.getElementById('utilization-value');
const circumference = 251.2;
const offset = circumference - (percent / 100) * circumference;
ring.style.strokeDashoffset = offset;
valueText.textContent = percent + '%';
}
// 更新负载进度条
function updateLoadBar(current, max) {
const percent = ((current / max) * 100).toFixed(1);
document.getElementById('load-value').textContent = current.toFixed(1) + ' 吨';
document.getElementById('load-percent').textContent = percent + '%';
document.getElementById('load-bar').style.width = percent + '%';
}
// 更新能耗
function updatePower(power, max) {
const percent = ((power / max) * 100).toFixed(0);
document.getElementById('power-value').textContent = power;
document.getElementById('power-fill').style.height = percent + '%';
}
// 更新运行时长
function updateRuntime(hours, max) {
const percent = ((hours / max) * 100).toFixed(0);
document.getElementById('runtime-value').textContent = hours.toFixed(1) + ' 小时';
document.getElementById('runtime-bar').style.width = percent + '%';
}
// 更新趋势图
function updateTrendChart() {
const bars = document.querySelectorAll('.line-bar');
bars.forEach(bar => {
const currentHeight = parseInt(bar.style.height);
const variation = (Math.random() - 0.5) * 10;
let newHeight = currentHeight + variation;
newHeight = Math.max(20, Math.min(95, newHeight));
bar.style.height = newHeight + '%';
});
}
// 模拟数据更新
function updateChartData() {
// 更新环形图(设备利用率)
const currentUtil = parseInt(document.getElementById('utilization-value').textContent);
const newUtil = Math.max(60, Math.min(95, currentUtil + (Math.random() - 0.5) * 5));
updateRingChart(Math.round(newUtil));
// 更新负载
const currentLoad = parseFloat(document.getElementById('load-value').textContent);
const newLoad = Math.max(30, Math.min(100, currentLoad + (Math.random() - 0.5) * 3));
updateLoadBar(newLoad, 120);
// 更新能耗
const currentPower = parseInt(document.getElementById('power-value').textContent);
const newPower = Math.max(150, Math.min(250, currentPower + (Math.random() - 0.5) * 10));
updatePower(Math.round(newPower), 300);
// 更新运行时长(缓慢增加)
const currentRuntime = parseFloat(document.getElementById('runtime-value').textContent);
const newRuntime = Math.min(10, currentRuntime + 0.01);
updateRuntime(newRuntime, 10);
// 更新趋势图
updateTrendChart();
}
// 模拟底部数据面板更新
function updateDataPanel() {
const cards = document.querySelectorAll('.data-card-value');
cards.forEach(card => {
const currentValue = parseFloat(card.textContent);
if (!isNaN(currentValue)) {
// 随机小幅波动
const variation = (Math.random() - 0.5) * 2;
const newValue = (currentValue + variation).toFixed(1);
card.textContent = newValue;
}
});
}
// 每3秒更新一次图表数据
setInterval(updateChartData, 3000);
// 每5秒更新一次数据面板
setInterval(updateDataPanel, 5000);
// 监听来自Unity的消息
window.addEventListener('message', function (event) {
console.log('收到iframe消息:', event.data);
// 这里可以根据Unity发送的消息更新界面数据
// 例如根据Unity发送的设备数据更新图表
if (event.data && event.data.type === 'equipment') {
if (event.data.utilization) updateRingChart(event.data.utilization);
if (event.data.load) updateLoadBar(event.data.load, 120);
if (event.data.power) updatePower(event.data.power, 300);
}
});
// 刷新按钮功能
document.getElementById('refresh-btn').addEventListener('click', function() {
const iframe = document.querySelector('.main-view iframe');
const btn = this;
// 添加旋转动画
btn.style.transition = 'transform 0.6s ease';
btn.style.transform = 'rotate(360deg)';
// 刷新iframe
iframe.src = iframe.src;
// 动画结束后重置
setTimeout(() => {
btn.style.transform = 'rotate(0deg)';
}, 600);
});
// 全屏按钮功能
document.getElementById('fullscreen-btn').addEventListener('click', function() {
const mainView = document.querySelector('.main-view');
const btn = this;
if (!document.fullscreenElement) {
// 进入全屏
if (mainView.requestFullscreen) {
mainView.requestFullscreen();
} else if (mainView.webkitRequestFullscreen) {
mainView.webkitRequestFullscreen();
} else if (mainView.msRequestFullscreen) {
mainView.msRequestFullscreen();
}
} else {
// 退出全屏
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
});
// 监听全屏状态变化,更新按钮
document.addEventListener('fullscreenchange', function() {
const btn = document.getElementById('fullscreen-btn');
if (!document.fullscreenElement) {
// 退出全屏状态
btn.innerHTML = `
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>
</svg>
全屏
`;
btn.title = '全屏数字孪生';
} else {
// 全屏状态
btn.innerHTML = `
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/>
</svg>
退出全屏
`;
btn.title = '退出全屏';
}
});
// 兼容不同浏览器的全屏事件
document.addEventListener('webkitfullscreenchange', function() {
document.dispatchEvent(new Event('fullscreenchange'));
});
document.addEventListener('mozfullscreenchange', function() {
document.dispatchEvent(new Event('fullscreenchange'));
});
document.addEventListener('MSFullscreenChange', function() {
document.dispatchEvent(new Event('fullscreenchange'));
});
</script>
</body>
</html>