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">
<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, '&quot;')}" 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 = [];