A:增加详细计算解释

This commit is contained in:
jimmy committed 2025-07-23 10:12:06 +08:00
1 parent e643d92fcb
commit 055a122bf8
1 file changed
+599 -222
+599 -222
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
@@ -13,7 +13,7 @@
body { body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
min-height: 100vh; min-height: 100vh;
padding: 20px; padding: 20px;
} }
@@ -21,14 +21,14 @@
.container { .container {
max-width: 1400px; max-width: 1400px;
margin: 0 auto; margin: 0 auto;
background: rgba(255, 255, 255, 0.95); background: rgba(255, 255, 255, 0.98);
border-radius: 20px; border-radius: 20px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1); box-shadow: 0 15px 35px rgba(0, 0, 0, 0.08);
overflow: hidden; overflow: hidden;
} }
.header { .header {
background: linear-gradient(135deg, #2c3e50, #3498db); background: linear-gradient(135deg, #5f9ea0, #4682b4);
color: white; color: white;
padding: 30px; padding: 30px;
text-align: center; text-align: center;
@@ -38,12 +38,15 @@
.header h1 { .header h1 {
font-size: 2.5em; font-size: 2.5em;
margin-bottom: 10px; margin-bottom: 10px;
text-shadow: 0 2px 4px rgba(0,0,0,0.3); text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
font-weight: 300;
letter-spacing: 1px;
} }
.header p { .header p {
font-size: 1.2em; font-size: 1.2em;
opacity: 0.9; opacity: 0.95;
font-weight: 300;
} }
/* 语言和Github按钮样式 */ /* 语言和Github按钮样式 */
@@ -57,14 +60,14 @@
} }
.lang-btn, .github-btn { .lang-btn, .github-btn {
background: rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.25);
color: white; color: white;
border: 1px solid rgba(255, 255, 255, 0.3); border: 1px solid rgba(255, 255, 255, 0.4);
border-radius: 25px; border-radius: 25px;
padding: 8px 16px; padding: 8px 16px;
cursor: pointer; cursor: pointer;
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 500;
transition: all 0.3s ease; transition: all 0.3s ease;
text-decoration: none; text-decoration: none;
display: flex; display: flex;
@@ -74,23 +77,23 @@
} }
.lang-btn:hover, .github-btn:hover { .lang-btn:hover, .github-btn:hover {
background: rgba(255, 255, 255, 0.3); background: rgba(255, 255, 255, 0.35);
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
} }
.lang-btn.active { .lang-btn.active {
background: rgba(255, 255, 255, 0.3); background: rgba(255, 255, 255, 0.35);
border-color: rgba(255, 255, 255, 0.5); border-color: rgba(255, 255, 255, 0.6);
} }
.github-btn { .github-btn {
background: rgba(36, 41, 46, 0.8); background: rgba(36, 41, 46, 0.7);
border-color: rgba(255, 255, 255, 0.2); border-color: rgba(255, 255, 255, 0.3);
} }
.github-btn:hover { .github-btn:hover {
background: rgba(36, 41, 46, 0.9); background: rgba(36, 41, 46, 0.85);
} }
.main-content { .main-content {
@@ -101,22 +104,23 @@
} }
.input-section, .control-section { .input-section, .control-section {
background: white; background: #f8fafb;
border-radius: 15px; border-radius: 15px;
padding: 25px; padding: 25px;
box-shadow: 0 8px 25px rgba(0,0,0,0.1); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.06);
border: 1px solid #e0e6ed; border: 1px solid #e8f0f5;
} }
.section-title { .section-title {
font-size: 1.4em; font-size: 1.4em;
color: #2c3e50; color: #4682b4;
margin-bottom: 20px; margin-bottom: 20px;
padding-bottom: 10px; padding-bottom: 10px;
border-bottom: 2px solid #3498db; border-bottom: 2px solid #87ceeb;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
font-weight: 400;
} }
.form-group { .form-group {
@@ -126,24 +130,25 @@
label { label {
display: block; display: block;
margin-bottom: 8px; margin-bottom: 8px;
font-weight: 600; font-weight: 500;
color: #34495e; color: #5a7c8a;
} }
input, select, textarea { input, select, textarea {
width: 100%; width: 100%;
padding: 12px; padding: 12px;
border: 2px solid #e1e8ed; border: 2px solid #e0ecf1;
border-radius: 8px; border-radius: 8px;
font-size: 14px; font-size: 14px;
transition: all 0.3s ease; transition: all 0.3s ease;
font-family: 'Consolas', 'Monaco', monospace; font-family: 'Consolas', 'Monaco', monospace;
background: white;
} }
input:focus, select:focus, textarea:focus { input:focus, select:focus, textarea:focus {
outline: none; outline: none;
border-color: #3498db; border-color: #87ceeb;
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1); box-shadow: 0 0 0 3px rgba(135, 206, 235, 0.15);
} }
textarea { textarea {
@@ -152,39 +157,39 @@
} }
.btn { .btn {
background: linear-gradient(135deg, #3498db, #2980b9); background: linear-gradient(135deg, #5f9ea0, #4682b4);
color: white; color: white;
border: none; border: none;
padding: 12px 24px; padding: 12px 24px;
border-radius: 8px; border-radius: 8px;
cursor: pointer; cursor: pointer;
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 500;
transition: all 0.3s ease; transition: all 0.3s ease;
margin: 5px; margin: 5px;
} }
.btn:hover { .btn:hover {
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(52, 152, 219, 0.3); box-shadow: 0 8px 20px rgba(70, 130, 180, 0.3);
} }
.btn-secondary { .btn-secondary {
background: linear-gradient(135deg, #95a5a6, #7f8c8d); background: linear-gradient(135deg, #b0c4de, #9abdd6);
} }
.btn-success { .btn-success {
background: linear-gradient(135deg, #27ae60, #229954); background: linear-gradient(135deg, #66cdaa, #4db393);
} }
.btn-smart { .btn-smart {
background: linear-gradient(135deg, #e74c3c, #c0392b); background: linear-gradient(135deg, #ff7f50, #ff6347);
} }
/* 智能检测面板样式 - 紧凑版 */ /* 智能检测面板样式 - 清新版 */
.smart-detection { .smart-detection {
background: linear-gradient(135deg, #f8f9fa, #e9ecef); background: linear-gradient(135deg, #f0f9ff, #e6f7ff);
border: 2px solid #17a2b8; border: 2px solid #87ceeb;
border-radius: 12px; border-radius: 12px;
padding: 15px; padding: 15px;
margin: 15px 0; margin: 15px 0;
@@ -199,15 +204,15 @@
left: 0; left: 0;
width: 100%; width: 100%;
height: 3px; height: 3px;
background: linear-gradient(90deg, #17a2b8, #138496); background: linear-gradient(90deg, #87ceeb, #5f9ea0);
} }
.detection-header { .detection-header {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
font-weight: 700; font-weight: 600;
color: #2c3e50; color: #4682b4;
margin-bottom: 10px; margin-bottom: 10px;
font-size: 14px; font-size: 14px;
} }
@@ -224,18 +229,18 @@
} }
.detection-results::-webkit-scrollbar-track { .detection-results::-webkit-scrollbar-track {
background: #f1f1f1; background: #f0f9ff;
border-radius: 3px; border-radius: 3px;
} }
.detection-results::-webkit-scrollbar-thumb { .detection-results::-webkit-scrollbar-thumb {
background: #888; background: #b0c4de;
border-radius: 3px; border-radius: 3px;
} }
.detection-item { .detection-item {
background: white; background: white;
border: 1px solid #dee2e6; border: 1px solid #daeaf2;
border-radius: 6px; border-radius: 6px;
padding: 8px 12px; padding: 8px 12px;
display: flex; display: flex;
@@ -246,7 +251,7 @@
.detection-category { .detection-category {
font-weight: 600; font-weight: 600;
color: #495057; color: #5a7c8a;
margin-top: 8px; margin-top: 8px;
margin-bottom: 4px; margin-bottom: 4px;
font-size: 12px; font-size: 12px;
@@ -255,18 +260,18 @@
} }
.confidence-high { .confidence-high {
border-left: 3px solid #28a745; border-left: 3px solid #66cdaa;
background: linear-gradient(90deg, rgba(40, 167, 69, 0.08), white); background: linear-gradient(90deg, rgba(102, 205, 170, 0.08), white);
} }
.confidence-medium { .confidence-medium {
border-left: 3px solid #ffc107; border-left: 3px solid #ffd700;
background: linear-gradient(90deg, rgba(255, 193, 7, 0.08), white); background: linear-gradient(90deg, rgba(255, 215, 0, 0.08), white);
} }
.confidence-low { .confidence-low {
border-left: 3px solid #dc3545; border-left: 3px solid #ff7f50;
background: linear-gradient(90deg, rgba(220, 53, 69, 0.08), white); background: linear-gradient(90deg, rgba(255, 127, 80, 0.08), white);
} }
.confidence-badge { .confidence-badge {
@@ -278,20 +283,20 @@
} }
.badge-high { .badge-high {
background: #28a745; background: #66cdaa;
} }
.badge-medium { .badge-medium {
background: #ffc107; background: #ffd700;
} }
.badge-low { .badge-low {
background: #dc3545; background: #ff7f50;
} }
.recommendation-panel { .recommendation-panel {
background: linear-gradient(135deg, #e8f5e8, #f0f8f0); background: linear-gradient(135deg, #f0fdf4, #dcfce7);
border: 2px solid #28a745; border: 2px solid #66cdaa;
border-radius: 12px; border-radius: 12px;
padding: 15px; padding: 15px;
margin: 15px 0; margin: 15px 0;
@@ -301,15 +306,15 @@
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
font-weight: 700; font-weight: 600;
color: #155724; color: #2e7d32;
margin-bottom: 10px; margin-bottom: 10px;
font-size: 14px; font-size: 14px;
} }
.recommended-item { .recommended-item {
background: white; background: white;
border: 1px solid #c3e6cb; border: 1px solid #a7f3d0;
border-radius: 6px; border-radius: 6px;
padding: 10px; padding: 10px;
margin: 8px 0; margin: 8px 0;
@@ -321,7 +326,7 @@
position: absolute; position: absolute;
top: -6px; top: -6px;
right: 10px; right: 10px;
background: #28a745; background: #66cdaa;
color: white; color: white;
padding: 2px 8px; padding: 2px 8px;
border-radius: 10px; border-radius: 10px;
@@ -331,11 +336,11 @@
.results-section { .results-section {
grid-column: 1 / -1; grid-column: 1 / -1;
background: white; background: #f8fafb;
border-radius: 15px; border-radius: 15px;
padding: 25px; padding: 25px;
box-shadow: 0 8px 25px rgba(0,0,0,0.1); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.06);
border: 1px solid #e0e6ed; border: 1px solid #e8f0f5;
margin-top: 20px; margin-top: 20px;
} }
@@ -343,7 +348,7 @@
display: flex; display: flex;
gap: 10px; gap: 10px;
margin-bottom: 20px; margin-bottom: 20px;
border-bottom: 2px solid #ecf0f1; border-bottom: 2px solid #e8f0f5;
} }
.tab-btn { .tab-btn {
@@ -352,23 +357,23 @@
border: none; border: none;
border-bottom: 3px solid transparent; border-bottom: 3px solid transparent;
cursor: pointer; cursor: pointer;
font-weight: 600; font-weight: 500;
color: #7f8c8d; color: #8fa9b3;
transition: all 0.3s ease; transition: all 0.3s ease;
position: relative; position: relative;
} }
.tab-btn.active { .tab-btn.active {
color: #3498db; color: #4682b4;
border-bottom-color: #3498db; border-bottom-color: #4682b4;
} }
.tab-btn.smart { .tab-btn.smart {
color: #e74c3c; color: #ff7f50;
} }
.tab-btn.smart.active { .tab-btn.smart.active {
border-bottom-color: #e74c3c; border-bottom-color: #ff7f50;
} }
/* Tab计数器样式 */ /* Tab计数器样式 */
@@ -376,7 +381,7 @@
position: absolute; position: absolute;
top: -5px; top: -5px;
right: -5px; right: -5px;
background: #e74c3c; background: #ff7f50;
color: white; color: white;
border-radius: 50%; border-radius: 50%;
min-width: 18px; min-width: 18px;
@@ -405,8 +410,8 @@
} }
.result-item { .result-item {
background: #f8f9fa; background: white;
border: 1px solid #e9ecef; border: 1px solid #e0ecf1;
border-radius: 8px; border-radius: 8px;
padding: 15px; padding: 15px;
position: relative; position: relative;
@@ -414,25 +419,26 @@
} }
.result-item:hover { .result-item:hover {
background: #e3f2fd; background: #f0f9ff;
border-color: #3498db; border-color: #87ceeb;
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(135, 206, 235, 0.2);
} }
.result-item.recommended { .result-item.recommended {
border: 2px solid #28a745; border: 2px solid #66cdaa;
background: linear-gradient(135deg, rgba(40, 167, 69, 0.1), #f8f9fa); background: linear-gradient(135deg, rgba(102, 205, 170, 0.05), white);
} }
.result-item.failed { .result-item.failed {
opacity: 0.5; opacity: 0.6;
border-color: #dc3545; border-color: #ffb6c1;
background: linear-gradient(135deg, rgba(220, 53, 69, 0.1), #f8f9fa); background: linear-gradient(135deg, rgba(255, 182, 193, 0.1), white);
} }
.result-header { .result-header {
font-weight: 600; font-weight: 500;
color: #2c3e50; color: #4682b4;
margin-bottom: 8px; margin-bottom: 8px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@@ -440,46 +446,49 @@
} }
.result-header-sub { .result-header-sub {
/*max-width: 210px;*/ white-space: nowrap;
display: flex;
align-items: center;
gap: 4px;
} }
.result-value { .result-value {
font-family: 'Consolas', 'Monaco', monospace; font-family: 'Consolas', 'Monaco', monospace;
background: white; background: #f0f9ff;
padding: 8px; padding: 8px;
border-radius: 4px; border-radius: 4px;
border: 1px solid #dee2e6; border: 1px solid #daeaf2;
word-break: break-all; word-break: break-all;
font-size: 13px; font-size: 13px;
} }
.copy-btn { .copy-btn {
background: #17a2b8; background: #5f9ea0;
color: white; color: white;
border: none; border: none;
padding: 4px 8px; padding: 4px 8px;
border-radius: 4px; border-radius: 4px;
cursor: pointer; cursor: pointer;
font-size: 12px; font-size: 13px;
} }
.copy-btn:hover { .copy-btn:hover {
background: #138496; background: #4a8a8c;
} }
.details-btn { .details-btn {
background: #28a745; background: #66cdaa;
color: white; color: white;
border: none; border: none;
padding: 4px 8px; padding: 4px 8px;
border-radius: 4px; border-radius: 4px;
cursor: pointer; cursor: pointer;
font-size: 12px; font-size: 13px;
margin-left: 4px; margin-left: 4px;
} }
.details-btn:hover { .details-btn:hover {
background: #1e7e34; background: #4db393;
} }
.success-indicator { .success-indicator {
@@ -492,18 +501,18 @@
} }
.success-indicator.success { .success-indicator.success {
background: #28a745; background: #66cdaa;
} }
.success-indicator.warning { .success-indicator.warning {
background: #ffc107; background: #ffd700;
} }
.success-indicator.error { .success-indicator.error {
background: #dc3545; background: #ff7f50;
} }
/* 详情模态框样式 - 增大尺寸 */ /* 详情模态框样式 - 清新版 */
.details-modal { .details-modal {
display: none; display: none;
position: fixed; position: fixed;
@@ -512,7 +521,7 @@
top: 0; top: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: rgba(0, 0, 0, 0.5); background-color: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(5px); backdrop-filter: blur(5px);
} }
@@ -525,7 +534,7 @@
max-width: 1200px; max-width: 1200px;
max-height: 95vh; max-height: 95vh;
overflow: hidden; overflow: hidden;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
animation: modalSlideIn 0.3s ease-out; animation: modalSlideIn 0.3s ease-out;
} }
@@ -541,7 +550,7 @@
} }
.modal-header { .modal-header {
background: linear-gradient(135deg, #2c3e50, #3498db); background: linear-gradient(135deg, #5f9ea0, #4682b4);
color: white; color: white;
padding: 20px 30px; padding: 20px 30px;
border-radius: 15px 15px 0 0; border-radius: 15px 15px 0 0;
@@ -552,7 +561,7 @@
.modal-title { .modal-title {
font-size: 1.5em; font-size: 1.5em;
font-weight: 600; font-weight: 400;
margin: 0; margin: 0;
} }
@@ -567,7 +576,7 @@
.close-btn:hover { .close-btn:hover {
transform: rotate(90deg); transform: rotate(90deg);
opacity: 0.7; opacity: 0.8;
} }
.modal-body { .modal-body {
@@ -581,37 +590,106 @@
} }
.modal-body::-webkit-scrollbar-track { .modal-body::-webkit-scrollbar-track {
background: #f1f1f1; background: #f0f9ff;
border-radius: 4px; border-radius: 4px;
} }
.modal-body::-webkit-scrollbar-thumb { .modal-body::-webkit-scrollbar-thumb {
background: #888; background: #b0c4de;
border-radius: 4px; border-radius: 4px;
} }
/* 详情内容样式 */ /* 详情内容样式 - 清新版 */
.detail-section { .detail-section {
margin-bottom: 25px; margin-bottom: 25px;
background: #f8f9fa; background: #f8fafb;
border-radius: 10px; border-radius: 10px;
padding: 20px; padding: 20px;
border-left: 4px solid #3498db; border-left: 4px solid #87ceeb;
} }
.detail-title { .detail-title {
font-size: 1.3em; font-size: 1.3em;
font-weight: 700; font-weight: 600;
color: #2c3e50; color: #4682b4;
margin-bottom: 15px; margin-bottom: 15px;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
} }
/* 新增:步骤展示样式 */
.conversion-steps {
background: white;
border: 1px solid #e0ecf1;
border-radius: 8px;
padding: 20px;
margin: 15px 0;
}
.step-item {
position: relative;
padding: 15px 0;
padding-left: 40px;
border-bottom: 1px solid #f0f9ff;
}
.step-item:last-child {
border-bottom: none;
}
.step-number {
position: absolute;
left: 0;
top: 15px;
width: 28px;
height: 28px;
background: linear-gradient(135deg, #87ceeb, #5f9ea0);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 14px;
}
.step-title {
font-weight: 600;
color: #4682b4;
margin-bottom: 8px;
font-size: 15px;
}
.step-description {
color: #6b7280;
font-size: 13px;
margin-bottom: 8px;
line-height: 1.6;
}
.step-data {
font-family: 'Consolas', 'Monaco', monospace;
background: #f0f9ff;
padding: 10px 15px;
border-radius: 6px;
border: 1px solid #daeaf2;
color: #1e40af;
font-size: 14px;
margin-top: 8px;
word-break: break-all;
}
.step-arrow {
text-align: center;
color: #87ceeb;
font-size: 20px;
margin: 10px 0;
}
.byteorder-test { .byteorder-test {
background: white; background: white;
border: 1px solid #dee2e6; border: 1px solid #daeaf2;
border-radius: 8px; border-radius: 8px;
padding: 15px; padding: 15px;
margin: 10px 0; margin: 10px 0;
@@ -622,7 +700,7 @@
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 8px 0; padding: 8px 0;
border-bottom: 1px solid #eee; border-bottom: 1px solid #f0f9ff;
} }
.byteorder-item:last-child { .byteorder-item:last-child {
@@ -630,13 +708,13 @@
} }
.byteorder-label { .byteorder-label {
font-weight: 600; font-weight: 500;
color: #495057; color: #5a7c8a;
} }
.byteorder-value { .byteorder-value {
font-family: 'Consolas', 'Monaco', monospace; font-family: 'Consolas', 'Monaco', monospace;
background: #f1f3f4; background: #f0f9ff;
padding: 4px 8px; padding: 4px 8px;
border-radius: 4px; border-radius: 4px;
font-size: 13px; font-size: 13px;
@@ -647,8 +725,8 @@
} }
.principle-box { .principle-box {
background: #e8f5e8; background: #f0fdf4;
border: 1px solid #c3e6cb; border: 1px solid #d1fae5;
border-radius: 8px; border-radius: 8px;
padding: 15px; padding: 15px;
margin: 10px 0; margin: 10px 0;
@@ -663,12 +741,12 @@
.principle-item strong { .principle-item strong {
min-width: 100px; min-width: 100px;
color: #155724; color: #2e7d32;
} }
.memory-layout { .memory-layout {
background: #fff3cd; background: #fffbeb;
border: 1px solid #ffeaa7; border: 1px solid #fde68a;
border-radius: 8px; border-radius: 8px;
padding: 15px; padding: 15px;
margin: 10px 0; margin: 10px 0;
@@ -684,7 +762,7 @@
background: #fff; background: #fff;
padding: 8px; padding: 8px;
border-radius: 4px; border-radius: 4px;
border: 1px solid #ddd; border: 1px solid #fbbf24;
display: inline-block; display: inline-block;
margin: 0 5px; margin: 0 5px;
font-weight: 600; font-weight: 600;
@@ -692,8 +770,8 @@
/* 原始数据展示样式 */ /* 原始数据展示样式 */
.process-step { .process-step {
background: #f8f9fa; background: white;
border: 1px solid #e9ecef; border: 1px solid #e0ecf1;
border-radius: 8px; border-radius: 8px;
padding: 15px; padding: 15px;
margin-bottom: 15px; margin-bottom: 15px;
@@ -701,43 +779,34 @@
} }
.process-step:hover { .process-step:hover {
background: #e3f2fd; background: #f0f9ff;
border-color: #3498db; border-color: #87ceeb;
transform: translateX(5px); transform: translateX(5px);
} }
.step-title {
font-weight: 600;
color: #2c3e50;
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 8px;
}
.step-content { .step-content {
font-family: 'Consolas', 'Monaco', monospace; font-family: 'Consolas', 'Monaco', monospace;
background: white; background: #f8fafb;
padding: 12px; padding: 12px;
border-radius: 6px; border-radius: 6px;
border: 1px solid #dee2e6; border: 1px solid #e0ecf1;
word-break: break-all; word-break: break-all;
color: #495057; color: #4b5563;
font-size: 13px; font-size: 13px;
line-height: 1.6; line-height: 1.6;
} }
.error { .error {
color: #e74c3c; color: #dc2626;
background: #fadbd8; background: #fee2e2;
padding: 10px; padding: 10px;
border-radius: 5px; border-radius: 5px;
margin: 10px 0; margin: 10px 0;
} }
.statistics-panel { .statistics-panel {
background: #fff3cd; background: #fffbeb;
border: 1px solid #ffc107; border: 1px solid #fbbf24;
border-radius: 8px; border-radius: 8px;
padding: 15px; padding: 15px;
margin: 15px 0; margin: 15px 0;
@@ -755,30 +824,30 @@
background: white; background: white;
padding: 10px; padding: 10px;
border-radius: 6px; border-radius: 6px;
border: 1px solid #ffecb5; border: 1px solid #fde68a;
} }
.stat-value { .stat-value {
font-size: 20px; font-size: 20px;
font-weight: 700; font-weight: 600;
color: #856404; color: #d97706;
} }
.stat-label { .stat-label {
font-size: 12px; font-size: 12px;
color: #6c757d; color: #6b7280;
margin-top: 4px; margin-top: 4px;
} }
/* 自动选择提示 */ /* 自动选择提示 */
.auto-select-notice { .auto-select-notice {
background: #d1ecf1; background: #dbeafe;
border: 1px solid #bee5eb; border: 1px solid #93c5fd;
border-radius: 6px; border-radius: 6px;
padding: 8px 12px; padding: 8px 12px;
margin: 10px 0; margin: 10px 0;
font-size: 12px; font-size: 12px;
color: #0c5460; color: #1e40af;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
@@ -881,7 +950,8 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<button class="btn btn-smart" onclick="intelligentAnalysis()">🧠 <span data-i18n="intelligent_analysis">智能分析</span></button> <button class="btn btn-smart" onclick="intelligentAnalysis()">🧠 <span data-i18n="intelligent_analysis">智能分析</span>
</button>
<button class="btn" onclick="parseInput()">📊 <span data-i18n="parse_data">解析数据</span></button> <button class="btn" onclick="parseInput()">📊 <span data-i18n="parse_data">解析数据</span></button>
<button class="btn btn-secondary" onclick="clearInput()">🗑️ <span data-i18n="clear">清空</span></button> <button class="btn btn-secondary" onclick="clearInput()">🗑️ <span data-i18n="clear">清空</span></button>
</div> </div>
@@ -958,7 +1028,8 @@
⭐ <span data-i18n="smart_recommendation">智能推荐配置</span> ⭐ <span data-i18n="smart_recommendation">智能推荐配置</span>
</div> </div>
<div id="recommendedConfigs"></div> <div id="recommendedConfigs"></div>
<button class="btn btn-success" onclick="applyRecommendation()">✅ <span data-i18n="apply_recommendation">应用推荐配置</span></button> <button class="btn btn-success" onclick="applyRecommendation()">✅ <span
data-i18n="apply_recommendation">应用推荐配置</span></button>
</div> </div>
</div> </div>
</div> </div>
@@ -976,7 +1047,8 @@
</div> </div>
<div class="results-tabs"> <div class="results-tabs">
<button class="tab-btn smart active" onclick="switchTab('smart-results')">🧠 <span data-i18n="intelligent_recommendation">智能推荐</span></button> <button class="tab-btn smart active" onclick="switchTab('smart-results')">🧠 <span
data-i18n="intelligent_recommendation">智能推荐</span></button>
<button class="tab-btn" onclick="switchTab('all-results')"> <button class="tab-btn" onclick="switchTab('all-results')">
<span data-i18n="conversion_results_tab">转换结果</span> <span data-i18n="conversion_results_tab">转换结果</span>
<span class="tab-counter" id="allResultsCounter" style="display: none;">0</span> <span class="tab-counter" id="allResultsCounter" style="display: none;">0</span>
@@ -1141,7 +1213,21 @@
you_input: '你输入了', you_input: '你输入了',
values: '值', values: '值',
convert_to: ',转换为', convert_to: ',转换为',
type: '类型。' type: '类型。',
// 步骤相关
step_convert_process: '详细转换步骤',
step_parse_input: '解析输入数据',
step_convert_bytes: '转换为字节数组',
step_apply_encoding: '应用编码格式',
step_apply_byteorder: '应用字节序',
step_parse_datatype: '按数据类型解析',
step_final_result: '最终结果',
encoding_dec_desc: '十进制编码,直接使用原始字节值',
encoding_bcd_desc: 'BCD编码,每个字节表示两位十进制数',
encoding_hex_desc: '十六进制编码,字节值按16进制解释',
parse_as: '解析为',
bytes_combined: '个字节组合',
value_calculation: '数值计算'
}, },
en: { en: {
title: 'Smart PLC Data Format Converter & Debugger', title: 'Smart PLC Data Format Converter & Debugger',
@@ -1270,7 +1356,21 @@
you_input: 'You input ', you_input: 'You input ',
values: ' values: [', values: ' values: [',
convert_to: '], converting to ', convert_to: '], converting to ',
type: ' type.' type: ' type.',
// 步骤相关
step_convert_process: 'Detailed Conversion Steps',
step_parse_input: 'Parse Input Data',
step_convert_bytes: 'Convert to Byte Array',
step_apply_encoding: 'Apply Encoding Format',
step_apply_byteorder: 'Apply Byte Order',
step_parse_datatype: 'Parse by Data Type',
step_final_result: 'Final Result',
encoding_dec_desc: 'Decimal encoding, using raw byte values directly',
encoding_bcd_desc: 'BCD encoding, each byte represents two decimal digits',
encoding_hex_desc: 'Hexadecimal encoding, bytes interpreted as hex',
parse_as: 'Parse as',
bytes_combined: 'bytes combined',
value_calculation: 'Value calculation'
} }
}; };
@@ -2216,11 +2316,11 @@
div.innerHTML = ` div.innerHTML = `
${smartInfo} ${smartInfo}
<div class="result-header"> <div class="result-header">
<span class="result-header-sub">${statusIcon} ${result.inputFormat.toUpperCase()} → ${result.dataType.toUpperCase()} - ${result.byteOrder} - ${result.encoding}</span> <span>${statusIcon} ${result.inputFormat.toUpperCase()} → ${result.dataType.toUpperCase()} - ${result.byteOrder} - ${result.encoding}</span>
<div> <div class="result-header-sub">
<button class="copy-btn" data-copy-value="${result.displayValue.replace(/"/g, '&quot;')}" onclick="copyResultFromButton(this)" title="复制结果">📋</button> <button class="copy-btn" data-copy-value="${result.displayValue.replace(/"/g, '&quot;')}" onclick="copyResultFromButton(this)" title="复制结果">📋</button>
<button class="details-btn" onclick="showConversionDetails(${resultIndex})" title="查看详情">🔍</button> <button class="details-btn" onclick="showConversionDetails(${resultIndex})" title="查看详情">🔍</button>
${showSmartInfo ? `<span style="font-size: 12px; color: #6c757d;">${result.smartScore}分</span>` : ''} ${showSmartInfo ? `<span style="font-size: 16px; color: #6c757d;">${result.smartScore}分</span>` : ''}
</div> </div>
</div> </div>
<div class="result-value">${result.displayValue}</div> <div class="result-value">${result.displayValue}</div>
@@ -2256,7 +2356,7 @@
modal.style.display = 'block'; modal.style.display = 'block';
} }
// 生成转换详情内容 // 生成转换详情内容 - 增强版,更详细的步骤展示
function generateConversionDetails(result) { function generateConversionDetails(result) {
const bytes = result.originalBytes; const bytes = result.originalBytes;
const byteOrders = ['ABCD', 'DCBA', 'CDAB', 'BADC']; const byteOrders = ['ABCD', 'DCBA', 'CDAB', 'BADC'];
@@ -2290,6 +2390,114 @@
</div> </div>
`; `;
// 添加详细的转换步骤
html += `
<div class="detail-section">
<div class="detail-title">🔄 ${t('step_convert_process')}</div>
<div class="conversion-steps">
`;
// 步骤1:解析输入数据
html += `
<div class="step-item">
<div class="step-number">1</div>
<div>
<div class="step-title">${t('step_parse_input')}</div>
<div class="step-description">
${t('input_format')}: ${currentInputFormat.toUpperCase()}
</div>
<div class="step-data">
[${currentData.slice(0, 10).join(', ')}${currentData.length > 10 ? '...' : ''}]
</div>
</div>
</div>
`;
// 步骤2:转换为字节数组
html += `
<div class="step-item">
<div class="step-number">2</div>
<div>
<div class="step-title">${t('step_convert_bytes')}</div>
<div class="step-description">
${getByteConversionDescription(currentInputFormat)}
</div>
<div class="step-data">
${formatBytesForDisplay(bytes)}
</div>
</div>
</div>
`;
// 步骤3:应用编码格式
if (result.encoding !== 'DEC') {
html += `
<div class="step-item">
<div class="step-number">3</div>
<div>
<div class="step-title">${t('step_apply_encoding')} (${result.encoding})</div>
<div class="step-description">
${getEncodingDescription(result.encoding)}
</div>
<div class="step-data">
${getEncodedBytesDisplay(bytes, result.encoding)}
</div>
</div>
</div>
`;
}
// 步骤4:应用字节序
if (needsByteOrder(result.dataType)) {
const stepNumber = result.encoding !== 'DEC' ? 4 : 3;
html += `
<div class="step-item">
<div class="step-number">${stepNumber}</div>
<div>
<div class="step-title">${t('step_apply_byteorder')} (${result.byteOrder})</div>
<div class="step-description">
${getByteOrderDescription(result.byteOrder, result.dataType)}
</div>
<div class="step-data">
${getByteOrderDisplay(bytes, result.byteOrder, result.dataType)}
</div>
</div>
</div>
`;
}
// 步骤5:按数据类型解析
const finalStepNumber = result.encoding !== 'DEC' ? (needsByteOrder(result.dataType) ? 5 : 4) : (needsByteOrder(result.dataType) ? 4 : 3);
html += `
<div class="step-item">
<div class="step-number">${finalStepNumber}</div>
<div>
<div class="step-title">${t('step_parse_datatype')} (${result.dataType.toUpperCase()})</div>
<div class="step-description">
${getDataTypeParseDescription(result.dataType)}
</div>
<div class="step-data">
${getDataTypeCalculation(bytes, result.dataType, result.byteOrder, result.encoding)}
</div>
</div>
</div>
`;
// 最终结果
html += `
<div class="step-item">
<div class="step-number">${finalStepNumber + 1}</div>
<div>
<div class="step-title">${t('step_final_result')}</div>
<div class="step-data">
${result.displayValue}
</div>
</div>
</div>
</div>
</div>
`;
// 显示所有字节序测试结果 // 显示所有字节序测试结果
if (result.dataType !== 'ascii' && result.dataType !== 'bool' && result.dataType !== 'byte') { if (result.dataType !== 'ascii' && result.dataType !== 'bool' && result.dataType !== 'byte') {
html += ` html += `
@@ -2330,31 +2538,6 @@
<div class="detail-section"> <div class="detail-section">
<div class="detail-title">📚 ${t('conversion_principle')}</div> <div class="detail-title">📚 ${t('conversion_principle')}</div>
<div class="principle-box"> <div class="principle-box">
<div class="principle-item">
<strong>${t('problem_analysis')}</strong>
<span>${t('you_input')}${currentData.length}${t('values')}${currentData.slice(0, 10).join(', ')}${currentData.length > 10 ? '...' : ''}]${t('convert_to')}${result.dataType}${t('type')}</span>
</div>
`;
// 具体的转换分析
if (currentInputFormat === 'ushort' && result.dataType === 'int') {
html += `
<div class="principle-item">
<strong>${t('data_analysis')}</strong>
<span>${t('ushort_to_int_analysis')}</span>
</div>
`;
} else if (currentInputFormat === 'int' && result.dataType === 'ushort') {
html += `
<div class="principle-item">
<strong>${t('data_analysis')}</strong>
<span>${t('int_to_ushort_analysis')}</span>
</div>
`;
}
// 字节序说明
html += `
<div class="principle-item"> <div class="principle-item">
<strong>${t('byte_order_explanation')}</strong> <strong>${t('byte_order_explanation')}</strong>
<div style="margin-top: 8px;"> <div style="margin-top: 8px;">
@@ -2368,51 +2551,234 @@
</div> </div>
`; `;
// 内存布局展示
if (bytes.length > 0) {
html += `
<div class="detail-section">
<div class="detail-title">💾 ${t('memory_layout_analysis')}</div>
<div class="memory-layout">
<div class="memory-step">${t('original_data')}: [${currentData.slice(0, 5).join(', ')}${currentData.length > 5 ? '...' : ''}]</div>
<div class="memory-step">${t('byte_representation')}: [${bytes.slice(0, 20).map(b => '0x' + b.toString(16).padStart(2, '0')).join(', ')}${bytes.length > 20 ? '...' : ''}]</div>
`;
if (result.dataType === 'float' && bytes.length >= 4) {
const firstFourBytes = bytes.slice(0, 4);
html += `
<div class="memory-step">
${t('float_conversion_example')} (前4字节):
<div style="margin-top: 5px;">
DCBA${t('order')}: <span class="memory-bytes">[${firstFourBytes.map(b => '0x' + b.toString(16).padStart(2, '0')).join('][')}]}</span> → ${getFloatFromBytes(firstFourBytes, 'DCBA')}
</div>
<div>
ABCD${t('order')}: <span class="memory-bytes">[${firstFourBytes.slice().reverse().map(b => '0x' + b.toString(16).padStart(2, '0')).join('][')}]}</span> → ${getFloatFromBytes(firstFourBytes, 'ABCD')}
</div>
</div>
`;
}
html += '</div></div>';
}
return html; return html;
} }
// 从字节数组获取浮点数(用于演示) // 辅助函数:获取字节转换描述
function getFloatFromBytes(bytes, byteOrder) { function getByteConversionDescription(inputFormat) {
try { const descriptions = {
const orderedBytes = applyByteOrder([...bytes], byteOrder, 4); 'byte': '每个byte值直接作为一个字节',
'ushort': '每个ushort值(16位)拆分为2个字节:低字节在前,高字节在后',
'short': '每个short值(16位)拆分为2个字节:低字节在前,高字节在后',
'int': '每个int值(32位)拆分为4个字节:从低到高依次排列',
'uint': '每个uint值(32位)拆分为4个字节:从低到高依次排列',
'hex': '十六进制字符串转换为对应的字节值'
};
return descriptions[inputFormat] || '';
}
// 辅助函数:格式化字节显示
function formatBytesForDisplay(bytes) {
const maxDisplay = 20;
const displayBytes = bytes.slice(0, maxDisplay);
let formatted = displayBytes.map((b, i) => {
const hex = b.toString(16).padStart(2, '0').toUpperCase();
return `[${i}]: 0x${hex} (${b})`;
}).join(', ');
if (bytes.length > maxDisplay) {
formatted += ` ... (共 ${bytes.length} 字节)`;
}
return formatted;
}
// 辅助函数:获取编码描述
function getEncodingDescription(encoding) {
const descriptions = {
'DEC': t('encoding_dec_desc'),
'BCD': t('encoding_bcd_desc'),
'HEX': t('encoding_hex_desc')
};
return descriptions[encoding] || '';
}
// 辅助函数:获取编码后的字节显示
function getEncodedBytesDisplay(bytes, encoding) {
if (encoding === 'BCD') {
const maxDisplay = 10;
const displayBytes = bytes.slice(0, maxDisplay);
let formatted = displayBytes.map((b, i) => {
const high = (b >> 4) & 0xF;
const low = b & 0xF;
const bcdValue = high * 10 + low;
return `0x${b.toString(16).padStart(2, '0').toUpperCase()} → ${bcdValue}`;
}).join(', ');
if (bytes.length > maxDisplay) {
formatted += ` ... (共 ${bytes.length} 字节)`;
}
return formatted;
}
return formatBytesForDisplay(bytes);
}
// 辅助函数:检查是否需要字节序
function needsByteOrder(dataType) {
return !['bool', 'byte', 'sbyte', 'ascii'].includes(dataType);
}
// 辅助函数:获取字节序描述
function getByteOrderDescription(byteOrder, dataType) {
const bytesNeeded = getDataTypeByteSize(dataType);
const examples = {
'ABCD': {
2: '例: [0x12, 0x34] → [0x34, 0x12]',
4: '例: [0x12, 0x34, 0x56, 0x78] → [0x78, 0x56, 0x34, 0x12]'
},
'DCBA': {
2: '例: [0x12, 0x34] → [0x12, 0x34] (不变)',
4: '例: [0x12, 0x34, 0x56, 0x78] → [0x12, 0x34, 0x56, 0x78] (不变)'
},
'CDAB': {
2: '例: [0x12, 0x34] → [0x34, 0x12]',
4: '例: [0x12, 0x34, 0x56, 0x78] → [0x56, 0x78, 0x12, 0x34]'
},
'BADC': {
2: '例: [0x12, 0x34] → [0x34, 0x12]',
4: '例: [0x12, 0x34, 0x56, 0x78] → [0x34, 0x12, 0x78, 0x56]'
}
};
return examples[byteOrder] && examples[byteOrder][bytesNeeded] || '';
}
// 辅助函数:获取数据类型字节大小
function getDataTypeByteSize(dataType) {
const sizes = {
'short': 2, 'ushort': 2,
'int': 4, 'uint': 4, 'float': 4,
'long': 8, 'ulong': 8, 'double': 8
};
return sizes[dataType] || 1;
}
// 辅助函数:获取字节序显示
function getByteOrderDisplay(bytes, byteOrder, dataType) {
const byteSize = getDataTypeByteSize(dataType);
const maxGroups = 3;
let display = '';
for (let i = 0; i < Math.min(bytes.length, maxGroups * byteSize); i += byteSize) {
const group = bytes.slice(i, i + byteSize);
const ordered = applyByteOrder([...group], byteOrder, byteSize);
display += `[${group.map(b => '0x' + b.toString(16).padStart(2, '0').toUpperCase()).join(', ')}] → `;
display += `[${ordered.map(b => '0x' + b.toString(16).padStart(2, '0').toUpperCase()).join(', ')}]`;
if (i + byteSize < bytes.length) {
display += ', ';
}
}
if (bytes.length > maxGroups * byteSize) {
display += ' ...';
}
return display;
}
// 辅助函数:获取数据类型解析描述
function getDataTypeParseDescription(dataType) {
const descriptions = {
'bool': '每个字节的8个位分别解析为布尔值',
'byte': '每个字节直接作为无符号8位整数',
'sbyte': '每个字节作为有符号8位整数',
'short': '每2个字节组合为有符号16位整数',
'ushort': '每2个字节组合为无符号16位整数',
'int': '每4个字节组合为有符号32位整数',
'uint': '每4个字节组合为无符号32位整数',
'long': '每8个字节组合为有符号64位整数',
'ulong': '每8个字节组合为无符号64位整数',
'float': '每4个字节按IEEE 754标准解析为32位浮点数',
'double': '每8个字节按IEEE 754标准解析为64位浮点数',
'ascii': '每个字节解析为ASCII字符'
};
return descriptions[dataType] || '';
}
// 辅助函数:获取数据类型计算过程
function getDataTypeCalculation(bytes, dataType, byteOrder, encoding) {
const processedBytes = applyEncoding(bytes, encoding);
if (!processedBytes.success) {
return '编码转换失败';
}
const actualBytes = processedBytes.data;
const byteSize = getDataTypeByteSize(dataType);
if (dataType === 'ascii') {
const chars = actualBytes.slice(0, 20).map(b => {
const char = b >= 32 && b <= 126 ? String.fromCharCode(b) : '·';
return `0x${b.toString(16).padStart(2, '0')} → '${char}'`;
}).join(', ');
return chars + (actualBytes.length > 20 ? ' ...' : '');
}
if (dataType === 'bool') {
const firstByte = actualBytes[0] || 0;
let bits = '';
for (let i = 7; i >= 0; i--) {
bits += ((firstByte >> i) & 1) ? '1' : '0';
if (i === 4) bits += ' ';
}
return `字节 0x${firstByte.toString(16).padStart(2, '0')} = ${bits} → [${bits.replace(' ', '').split('').map(b => b === '1').join(', ')}]`;
}
// 显示前几个转换
let calculations = [];
const maxCalc = 3;
for (let i = 0; i < Math.min(actualBytes.length, maxCalc * byteSize); i += byteSize) {
const group = actualBytes.slice(i, i + byteSize);
if (group.length < byteSize) break;
const ordered = applyByteOrder([...group], byteOrder, byteSize);
let value = 0;
let calc = '';
switch (dataType) {
case 'byte':
value = group[0];
calc = `0x${group[0].toString(16).padStart(2, '0')} = ${value}`;
break;
case 'sbyte':
value = group[0] > 127 ? group[0] - 256 : group[0];
calc = `0x${group[0].toString(16).padStart(2, '0')} = ${value}`;
break;
case 'short':
case 'ushort':
value = ordered[0] | (ordered[1] << 8);
if (dataType === 'short' && value > 32767) {
value -= 65536;
}
calc = `[${ordered.map(b => '0x' + b.toString(16).padStart(2, '0')).join(', ')}] = 0x${value.toString(16).padStart(4, '0')} = ${value}`;
break;
case 'int':
case 'uint':
value = ordered[0] | (ordered[1] << 8) | (ordered[2] << 16) | (ordered[3] << 24);
if (dataType === 'int' && value > 2147483647) {
value -= 4294967296;
} else if (dataType === 'uint') {
value = value >>> 0;
}
calc = `[${ordered.map(b => '0x' + b.toString(16).padStart(2, '0')).join(', ')}] = 0x${(value >>> 0).toString(16).padStart(8, '0')} = ${value}`;
break;
case 'float':
const buffer = new ArrayBuffer(4); const buffer = new ArrayBuffer(4);
const view = new DataView(buffer); const view = new DataView(buffer);
for (let i = 0; i < 4; i++) { for (let j = 0; j < 4; j++) {
view.setUint8(i, orderedBytes[i]); view.setUint8(j, ordered[j]);
} }
const value = view.getFloat32(0, true); value = view.getFloat32(0, true);
return isFinite(value) ? value.toFixed(6) : value.toString(); calc = `[${ordered.map(b => '0x' + b.toString(16).padStart(2, '0')).join(', ')}] = ${value.toFixed(6)}`;
} catch (error) { break;
return t('conversion_failed');
} }
calculations.push(calc);
}
return calculations.join('\n') + (actualBytes.length > maxCalc * byteSize ? '\n...' : '');
} }
// 关闭详情模态框(增强版,恢复body滚动) // 关闭详情模态框(增强版,恢复body滚动)
@@ -2438,7 +2804,23 @@
closeDetailsModal(); closeDetailsModal();
} }
} }
}); })
// 从字节数组获取浮点数(用于演示)
function getFloatFromBytes(bytes, byteOrder) {
try {
const orderedBytes = applyByteOrder([...bytes], byteOrder, 4);
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
for (let i = 0; i < 4; i++) {
view.setUint8(i, orderedBytes[i]);
}
const value = view.getFloat32(0, true);
return isFinite(value) ? value.toFixed(6) : value.toString();
} catch (error) {
return t('conversion_failed');
}
}
// 显示结果 // 显示结果
function displayResults(results, targetTypeFilter, byteOrderFilter, encodingFilter) { function displayResults(results, targetTypeFilter, byteOrderFilter, encodingFilter) {
@@ -2742,12 +3124,6 @@
`; `;
} }
function copyResult(text) {
navigator.clipboard.writeText(text).then(() => {
console.log('已复制到剪贴板');
});
}
function switchTab(tabName) { function switchTab(tabName) {
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
@@ -2777,6 +3153,7 @@
document.getElementById(tabName).classList.add('active'); document.getElementById(tabName).classList.add('active');
} }
// 清空之前的结果 // 清空之前的结果
function clearPreviousResults() { function clearPreviousResults() {
allResults = []; allResults = [];