A:增加详细计算解释
This commit is contained in:
1 parent
e643d92fcb
commit
055a122bf8
1 file changed
+599
-222
+599
-222
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
body {
|
||||
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;
|
||||
padding: 20px;
|
||||
}
|
||||
@@ -21,14 +21,14 @@
|
||||
.container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
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;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: linear-gradient(135deg, #2c3e50, #3498db);
|
||||
background: linear-gradient(135deg, #5f9ea0, #4682b4);
|
||||
color: white;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
@@ -38,12 +38,15 @@
|
||||
.header h1 {
|
||||
font-size: 2.5em;
|
||||
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 {
|
||||
font-size: 1.2em;
|
||||
opacity: 0.9;
|
||||
opacity: 0.95;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
/* 语言和Github按钮样式 */
|
||||
@@ -57,14 +60,14 @@
|
||||
}
|
||||
|
||||
.lang-btn, .github-btn {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
color: white;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||
border-radius: 25px;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
@@ -74,23 +77,23 @@
|
||||
}
|
||||
|
||||
.lang-btn:hover, .github-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
background: rgba(255, 255, 255, 0.35);
|
||||
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 {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
background: rgba(255, 255, 255, 0.35);
|
||||
border-color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.github-btn {
|
||||
background: rgba(36, 41, 46, 0.8);
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
background: rgba(36, 41, 46, 0.7);
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.github-btn:hover {
|
||||
background: rgba(36, 41, 46, 0.9);
|
||||
background: rgba(36, 41, 46, 0.85);
|
||||
}
|
||||
|
||||
.main-content {
|
||||
@@ -101,22 +104,23 @@
|
||||
}
|
||||
|
||||
.input-section, .control-section {
|
||||
background: white;
|
||||
background: #f8fafb;
|
||||
border-radius: 15px;
|
||||
padding: 25px;
|
||||
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
|
||||
border: 1px solid #e0e6ed;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.06);
|
||||
border: 1px solid #e8f0f5;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.4em;
|
||||
color: #2c3e50;
|
||||
color: #4682b4;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #3498db;
|
||||
border-bottom: 2px solid #87ceeb;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
@@ -126,24 +130,25 @@
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
color: #34495e;
|
||||
font-weight: 500;
|
||||
color: #5a7c8a;
|
||||
}
|
||||
|
||||
input, select, textarea {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 2px solid #e1e8ed;
|
||||
border: 2px solid #e0ecf1;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s ease;
|
||||
font-family: 'Consolas', 'Monaco', monospace;
|
||||
background: white;
|
||||
}
|
||||
|
||||
input:focus, select:focus, textarea:focus {
|
||||
outline: none;
|
||||
border-color: #3498db;
|
||||
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
|
||||
border-color: #87ceeb;
|
||||
box-shadow: 0 0 0 3px rgba(135, 206, 235, 0.15);
|
||||
}
|
||||
|
||||
textarea {
|
||||
@@ -152,39 +157,39 @@
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: linear-gradient(135deg, #3498db, #2980b9);
|
||||
background: linear-gradient(135deg, #5f9ea0, #4682b4);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
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 {
|
||||
background: linear-gradient(135deg, #95a5a6, #7f8c8d);
|
||||
background: linear-gradient(135deg, #b0c4de, #9abdd6);
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: linear-gradient(135deg, #27ae60, #229954);
|
||||
background: linear-gradient(135deg, #66cdaa, #4db393);
|
||||
}
|
||||
|
||||
.btn-smart {
|
||||
background: linear-gradient(135deg, #e74c3c, #c0392b);
|
||||
background: linear-gradient(135deg, #ff7f50, #ff6347);
|
||||
}
|
||||
|
||||
/* 智能检测面板样式 - 紧凑版 */
|
||||
/* 智能检测面板样式 - 清新版 */
|
||||
.smart-detection {
|
||||
background: linear-gradient(135deg, #f8f9fa, #e9ecef);
|
||||
border: 2px solid #17a2b8;
|
||||
background: linear-gradient(135deg, #f0f9ff, #e6f7ff);
|
||||
border: 2px solid #87ceeb;
|
||||
border-radius: 12px;
|
||||
padding: 15px;
|
||||
margin: 15px 0;
|
||||
@@ -199,15 +204,15 @@
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, #17a2b8, #138496);
|
||||
background: linear-gradient(90deg, #87ceeb, #5f9ea0);
|
||||
}
|
||||
|
||||
.detection-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-weight: 700;
|
||||
color: #2c3e50;
|
||||
font-weight: 600;
|
||||
color: #4682b4;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
@@ -224,18 +229,18 @@
|
||||
}
|
||||
|
||||
.detection-results::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
background: #f0f9ff;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.detection-results::-webkit-scrollbar-thumb {
|
||||
background: #888;
|
||||
background: #b0c4de;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.detection-item {
|
||||
background: white;
|
||||
border: 1px solid #dee2e6;
|
||||
border: 1px solid #daeaf2;
|
||||
border-radius: 6px;
|
||||
padding: 8px 12px;
|
||||
display: flex;
|
||||
@@ -246,7 +251,7 @@
|
||||
|
||||
.detection-category {
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
color: #5a7c8a;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 4px;
|
||||
font-size: 12px;
|
||||
@@ -255,18 +260,18 @@
|
||||
}
|
||||
|
||||
.confidence-high {
|
||||
border-left: 3px solid #28a745;
|
||||
background: linear-gradient(90deg, rgba(40, 167, 69, 0.08), white);
|
||||
border-left: 3px solid #66cdaa;
|
||||
background: linear-gradient(90deg, rgba(102, 205, 170, 0.08), white);
|
||||
}
|
||||
|
||||
.confidence-medium {
|
||||
border-left: 3px solid #ffc107;
|
||||
background: linear-gradient(90deg, rgba(255, 193, 7, 0.08), white);
|
||||
border-left: 3px solid #ffd700;
|
||||
background: linear-gradient(90deg, rgba(255, 215, 0, 0.08), white);
|
||||
}
|
||||
|
||||
.confidence-low {
|
||||
border-left: 3px solid #dc3545;
|
||||
background: linear-gradient(90deg, rgba(220, 53, 69, 0.08), white);
|
||||
border-left: 3px solid #ff7f50;
|
||||
background: linear-gradient(90deg, rgba(255, 127, 80, 0.08), white);
|
||||
}
|
||||
|
||||
.confidence-badge {
|
||||
@@ -278,20 +283,20 @@
|
||||
}
|
||||
|
||||
.badge-high {
|
||||
background: #28a745;
|
||||
background: #66cdaa;
|
||||
}
|
||||
|
||||
.badge-medium {
|
||||
background: #ffc107;
|
||||
background: #ffd700;
|
||||
}
|
||||
|
||||
.badge-low {
|
||||
background: #dc3545;
|
||||
background: #ff7f50;
|
||||
}
|
||||
|
||||
.recommendation-panel {
|
||||
background: linear-gradient(135deg, #e8f5e8, #f0f8f0);
|
||||
border: 2px solid #28a745;
|
||||
background: linear-gradient(135deg, #f0fdf4, #dcfce7);
|
||||
border: 2px solid #66cdaa;
|
||||
border-radius: 12px;
|
||||
padding: 15px;
|
||||
margin: 15px 0;
|
||||
@@ -301,15 +306,15 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-weight: 700;
|
||||
color: #155724;
|
||||
font-weight: 600;
|
||||
color: #2e7d32;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.recommended-item {
|
||||
background: white;
|
||||
border: 1px solid #c3e6cb;
|
||||
border: 1px solid #a7f3d0;
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
margin: 8px 0;
|
||||
@@ -321,7 +326,7 @@
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
right: 10px;
|
||||
background: #28a745;
|
||||
background: #66cdaa;
|
||||
color: white;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
@@ -331,11 +336,11 @@
|
||||
|
||||
.results-section {
|
||||
grid-column: 1 / -1;
|
||||
background: white;
|
||||
background: #f8fafb;
|
||||
border-radius: 15px;
|
||||
padding: 25px;
|
||||
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
|
||||
border: 1px solid #e0e6ed;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.06);
|
||||
border: 1px solid #e8f0f5;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
@@ -343,7 +348,7 @@
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 2px solid #ecf0f1;
|
||||
border-bottom: 2px solid #e8f0f5;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
@@ -352,23 +357,23 @@
|
||||
border: none;
|
||||
border-bottom: 3px solid transparent;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
color: #7f8c8d;
|
||||
font-weight: 500;
|
||||
color: #8fa9b3;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tab-btn.active {
|
||||
color: #3498db;
|
||||
border-bottom-color: #3498db;
|
||||
color: #4682b4;
|
||||
border-bottom-color: #4682b4;
|
||||
}
|
||||
|
||||
.tab-btn.smart {
|
||||
color: #e74c3c;
|
||||
color: #ff7f50;
|
||||
}
|
||||
|
||||
.tab-btn.smart.active {
|
||||
border-bottom-color: #e74c3c;
|
||||
border-bottom-color: #ff7f50;
|
||||
}
|
||||
|
||||
/* Tab计数器样式 */
|
||||
@@ -376,7 +381,7 @@
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
right: -5px;
|
||||
background: #e74c3c;
|
||||
background: #ff7f50;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
min-width: 18px;
|
||||
@@ -405,8 +410,8 @@
|
||||
}
|
||||
|
||||
.result-item {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
background: white;
|
||||
border: 1px solid #e0ecf1;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
position: relative;
|
||||
@@ -414,25 +419,26 @@
|
||||
}
|
||||
|
||||
.result-item:hover {
|
||||
background: #e3f2fd;
|
||||
border-color: #3498db;
|
||||
background: #f0f9ff;
|
||||
border-color: #87ceeb;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(135, 206, 235, 0.2);
|
||||
}
|
||||
|
||||
.result-item.recommended {
|
||||
border: 2px solid #28a745;
|
||||
background: linear-gradient(135deg, rgba(40, 167, 69, 0.1), #f8f9fa);
|
||||
border: 2px solid #66cdaa;
|
||||
background: linear-gradient(135deg, rgba(102, 205, 170, 0.05), white);
|
||||
}
|
||||
|
||||
.result-item.failed {
|
||||
opacity: 0.5;
|
||||
border-color: #dc3545;
|
||||
background: linear-gradient(135deg, rgba(220, 53, 69, 0.1), #f8f9fa);
|
||||
opacity: 0.6;
|
||||
border-color: #ffb6c1;
|
||||
background: linear-gradient(135deg, rgba(255, 182, 193, 0.1), white);
|
||||
}
|
||||
|
||||
.result-header {
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
font-weight: 500;
|
||||
color: #4682b4;
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -440,46 +446,49 @@
|
||||
}
|
||||
|
||||
.result-header-sub {
|
||||
/*max-width: 210px;*/
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.result-value {
|
||||
font-family: 'Consolas', 'Monaco', monospace;
|
||||
background: white;
|
||||
background: #f0f9ff;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #dee2e6;
|
||||
border: 1px solid #daeaf2;
|
||||
word-break: break-all;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background: #17a2b8;
|
||||
background: #5f9ea0;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.copy-btn:hover {
|
||||
background: #138496;
|
||||
background: #4a8a8c;
|
||||
}
|
||||
|
||||
.details-btn {
|
||||
background: #28a745;
|
||||
background: #66cdaa;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.details-btn:hover {
|
||||
background: #1e7e34;
|
||||
background: #4db393;
|
||||
}
|
||||
|
||||
.success-indicator {
|
||||
@@ -492,18 +501,18 @@
|
||||
}
|
||||
|
||||
.success-indicator.success {
|
||||
background: #28a745;
|
||||
background: #66cdaa;
|
||||
}
|
||||
|
||||
.success-indicator.warning {
|
||||
background: #ffc107;
|
||||
background: #ffd700;
|
||||
}
|
||||
|
||||
.success-indicator.error {
|
||||
background: #dc3545;
|
||||
background: #ff7f50;
|
||||
}
|
||||
|
||||
/* 详情模态框样式 - 增大尺寸 */
|
||||
/* 详情模态框样式 - 清新版 */
|
||||
.details-modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
@@ -512,7 +521,7 @@
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
@@ -525,7 +534,7 @@
|
||||
max-width: 1200px;
|
||||
max-height: 95vh;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -541,7 +550,7 @@
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
background: linear-gradient(135deg, #2c3e50, #3498db);
|
||||
background: linear-gradient(135deg, #5f9ea0, #4682b4);
|
||||
color: white;
|
||||
padding: 20px 30px;
|
||||
border-radius: 15px 15px 0 0;
|
||||
@@ -552,7 +561,7 @@
|
||||
|
||||
.modal-title {
|
||||
font-size: 1.5em;
|
||||
font-weight: 600;
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@@ -567,7 +576,7 @@
|
||||
|
||||
.close-btn:hover {
|
||||
transform: rotate(90deg);
|
||||
opacity: 0.7;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
@@ -581,37 +590,106 @@
|
||||
}
|
||||
|
||||
.modal-body::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
background: #f0f9ff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.modal-body::-webkit-scrollbar-thumb {
|
||||
background: #888;
|
||||
background: #b0c4de;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* 详情内容样式 */
|
||||
/* 详情内容样式 - 清新版 */
|
||||
.detail-section {
|
||||
margin-bottom: 25px;
|
||||
background: #f8f9fa;
|
||||
background: #f8fafb;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
border-left: 4px solid #3498db;
|
||||
border-left: 4px solid #87ceeb;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-size: 1.3em;
|
||||
font-weight: 700;
|
||||
color: #2c3e50;
|
||||
font-weight: 600;
|
||||
color: #4682b4;
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
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 {
|
||||
background: white;
|
||||
border: 1px solid #dee2e6;
|
||||
border: 1px solid #daeaf2;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
margin: 10px 0;
|
||||
@@ -622,7 +700,7 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
border-bottom: 1px solid #f0f9ff;
|
||||
}
|
||||
|
||||
.byteorder-item:last-child {
|
||||
@@ -630,13 +708,13 @@
|
||||
}
|
||||
|
||||
.byteorder-label {
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
font-weight: 500;
|
||||
color: #5a7c8a;
|
||||
}
|
||||
|
||||
.byteorder-value {
|
||||
font-family: 'Consolas', 'Monaco', monospace;
|
||||
background: #f1f3f4;
|
||||
background: #f0f9ff;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
@@ -647,8 +725,8 @@
|
||||
}
|
||||
|
||||
.principle-box {
|
||||
background: #e8f5e8;
|
||||
border: 1px solid #c3e6cb;
|
||||
background: #f0fdf4;
|
||||
border: 1px solid #d1fae5;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
margin: 10px 0;
|
||||
@@ -663,12 +741,12 @@
|
||||
|
||||
.principle-item strong {
|
||||
min-width: 100px;
|
||||
color: #155724;
|
||||
color: #2e7d32;
|
||||
}
|
||||
|
||||
.memory-layout {
|
||||
background: #fff3cd;
|
||||
border: 1px solid #ffeaa7;
|
||||
background: #fffbeb;
|
||||
border: 1px solid #fde68a;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
margin: 10px 0;
|
||||
@@ -684,7 +762,7 @@
|
||||
background: #fff;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ddd;
|
||||
border: 1px solid #fbbf24;
|
||||
display: inline-block;
|
||||
margin: 0 5px;
|
||||
font-weight: 600;
|
||||
@@ -692,8 +770,8 @@
|
||||
|
||||
/* 原始数据展示样式 */
|
||||
.process-step {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
background: white;
|
||||
border: 1px solid #e0ecf1;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
@@ -701,43 +779,34 @@
|
||||
}
|
||||
|
||||
.process-step:hover {
|
||||
background: #e3f2fd;
|
||||
border-color: #3498db;
|
||||
background: #f0f9ff;
|
||||
border-color: #87ceeb;
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
.step-title {
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
font-family: 'Consolas', 'Monaco', monospace;
|
||||
background: white;
|
||||
background: #f8fafb;
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #dee2e6;
|
||||
border: 1px solid #e0ecf1;
|
||||
word-break: break-all;
|
||||
color: #495057;
|
||||
color: #4b5563;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #e74c3c;
|
||||
background: #fadbd8;
|
||||
color: #dc2626;
|
||||
background: #fee2e2;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.statistics-panel {
|
||||
background: #fff3cd;
|
||||
border: 1px solid #ffc107;
|
||||
background: #fffbeb;
|
||||
border: 1px solid #fbbf24;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
margin: 15px 0;
|
||||
@@ -755,30 +824,30 @@
|
||||
background: white;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #ffecb5;
|
||||
border: 1px solid #fde68a;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #856404;
|
||||
font-weight: 600;
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
color: #6b7280;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* 自动选择提示 */
|
||||
.auto-select-notice {
|
||||
background: #d1ecf1;
|
||||
border: 1px solid #bee5eb;
|
||||
background: #dbeafe;
|
||||
border: 1px solid #93c5fd;
|
||||
border-radius: 6px;
|
||||
padding: 8px 12px;
|
||||
margin: 10px 0;
|
||||
font-size: 12px;
|
||||
color: #0c5460;
|
||||
color: #1e40af;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
@@ -881,7 +950,8 @@
|
||||
</div>
|
||||
|
||||
<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 btn-secondary" onclick="clearInput()">🗑️ <span data-i18n="clear">清空</span></button>
|
||||
</div>
|
||||
@@ -958,7 +1028,8 @@
|
||||
⭐ <span data-i18n="smart_recommendation">智能推荐配置</span>
|
||||
</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>
|
||||
@@ -976,7 +1047,8 @@
|
||||
</div>
|
||||
|
||||
<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')">
|
||||
<span data-i18n="conversion_results_tab">转换结果</span>
|
||||
<span class="tab-counter" id="allResultsCounter" style="display: none;">0</span>
|
||||
@@ -1141,7 +1213,21 @@
|
||||
you_input: '你输入了',
|
||||
values: '值',
|
||||
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: {
|
||||
title: 'Smart PLC Data Format Converter & Debugger',
|
||||
@@ -1270,7 +1356,21 @@
|
||||
you_input: 'You input ',
|
||||
values: ' values: [',
|
||||
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 = `
|
||||
${smartInfo}
|
||||
<div class="result-header">
|
||||
<span class="result-header-sub">${statusIcon} ${result.inputFormat.toUpperCase()} → ${result.dataType.toUpperCase()} - ${result.byteOrder} - ${result.encoding}</span>
|
||||
<div>
|
||||
<span>${statusIcon} ${result.inputFormat.toUpperCase()} → ${result.dataType.toUpperCase()} - ${result.byteOrder} - ${result.encoding}</span>
|
||||
<div class="result-header-sub">
|
||||
<button class="copy-btn" data-copy-value="${result.displayValue.replace(/"/g, '"')}" onclick="copyResultFromButton(this)" 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 class="result-value">${result.displayValue}</div>
|
||||
@@ -2256,7 +2356,7 @@
|
||||
modal.style.display = 'block';
|
||||
}
|
||||
|
||||
// 生成转换详情内容
|
||||
// 生成转换详情内容 - 增强版,更详细的步骤展示
|
||||
function generateConversionDetails(result) {
|
||||
const bytes = result.originalBytes;
|
||||
const byteOrders = ['ABCD', 'DCBA', 'CDAB', 'BADC'];
|
||||
@@ -2290,6 +2390,114 @@
|
||||
</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') {
|
||||
html += `
|
||||
@@ -2330,31 +2538,6 @@
|
||||
<div class="detail-section">
|
||||
<div class="detail-title">📚 ${t('conversion_principle')}</div>
|
||||
<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">
|
||||
<strong>${t('byte_order_explanation')}</strong>
|
||||
<div style="margin-top: 8px;">
|
||||
@@ -2368,51 +2551,234 @@
|
||||
</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;
|
||||
}
|
||||
|
||||
// 从字节数组获取浮点数(用于演示)
|
||||
function getFloatFromBytes(bytes, byteOrder) {
|
||||
try {
|
||||
const orderedBytes = applyByteOrder([...bytes], byteOrder, 4);
|
||||
// 辅助函数:获取字节转换描述
|
||||
function getByteConversionDescription(inputFormat) {
|
||||
const descriptions = {
|
||||
'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 view = new DataView(buffer);
|
||||
for (let i = 0; i < 4; i++) {
|
||||
view.setUint8(i, orderedBytes[i]);
|
||||
for (let j = 0; j < 4; j++) {
|
||||
view.setUint8(j, ordered[j]);
|
||||
}
|
||||
const value = view.getFloat32(0, true);
|
||||
return isFinite(value) ? value.toFixed(6) : value.toString();
|
||||
} catch (error) {
|
||||
return t('conversion_failed');
|
||||
value = view.getFloat32(0, true);
|
||||
calc = `[${ordered.map(b => '0x' + b.toString(16).padStart(2, '0')).join(', ')}] = ${value.toFixed(6)}`;
|
||||
break;
|
||||
}
|
||||
|
||||
calculations.push(calc);
|
||||
}
|
||||
|
||||
return calculations.join('\n') + (actualBytes.length > maxCalc * byteSize ? '\n...' : '');
|
||||
}
|
||||
|
||||
// 关闭详情模态框(增强版,恢复body滚动)
|
||||
@@ -2438,7 +2804,23 @@
|
||||
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) {
|
||||
@@ -2742,12 +3124,6 @@
|
||||
`;
|
||||
}
|
||||
|
||||
function copyResult(text) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
console.log('已复制到剪贴板');
|
||||
});
|
||||
}
|
||||
|
||||
function switchTab(tabName) {
|
||||
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
|
||||
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
|
||||
@@ -2777,6 +3153,7 @@
|
||||
document.getElementById(tabName).classList.add('active');
|
||||
}
|
||||
|
||||
|
||||
// 清空之前的结果
|
||||
function clearPreviousResults() {
|
||||
allResults = [];
|
||||
|
||||
Reference in new issue
Block a user