修复展示错误
This commit is contained in:
@@ -279,6 +279,11 @@ def compare_json_arrays(array1, array2):
|
||||
|
||||
def format_json_for_display(value):
|
||||
"""格式化JSON用于显示"""
|
||||
# 处理None值
|
||||
if value is None:
|
||||
return "null"
|
||||
|
||||
# 处理非字符串类型
|
||||
if not isinstance(value, str):
|
||||
return str(value)
|
||||
|
||||
|
183
static/js/app.js
183
static/js/app.js
@@ -911,11 +911,14 @@ function displayDifferences() {
|
||||
// 保存当前搜索框的值
|
||||
const currentSearchValue = document.getElementById('differenceSearch')?.value || '';
|
||||
|
||||
if (!filteredDifferenceResults.length) {
|
||||
if (!filteredDifferenceResults || !filteredDifferenceResults.length) {
|
||||
differencesContainer.innerHTML = '<p class="text-success"><i class="fas fa-check"></i> 未发现差异</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
// 调试日志:检查差异数据
|
||||
console.log('差异数据:', filteredDifferenceResults);
|
||||
|
||||
// 按主键分组差异
|
||||
const groupedDifferences = groupDifferencesByKey(filteredDifferenceResults);
|
||||
const totalGroups = Object.keys(groupedDifferences).length;
|
||||
@@ -995,10 +998,19 @@ function displayDifferences() {
|
||||
`;
|
||||
|
||||
// 显示当前页的主键组
|
||||
currentPageKeys.forEach((key, groupIndex) => {
|
||||
const diffs = groupedDifferences[key];
|
||||
currentPageKeys.forEach((keyStr, groupIndex) => {
|
||||
const diffs = groupedDifferences[keyStr];
|
||||
const globalIndex = startIndex + groupIndex + 1;
|
||||
|
||||
// 将字符串化的key解析回对象
|
||||
let keyObj;
|
||||
try {
|
||||
keyObj = JSON.parse(keyStr);
|
||||
} catch (e) {
|
||||
// 如果解析失败,假设它本身就是一个简单值
|
||||
keyObj = keyStr;
|
||||
}
|
||||
|
||||
html += `
|
||||
<div class="card mb-3 border-primary">
|
||||
<div class="card-header bg-primary bg-opacity-10">
|
||||
@@ -1012,14 +1024,14 @@ function displayDifferences() {
|
||||
data-bs-toggle="collapse" data-bs-target="#keyGroup${globalIndex}">
|
||||
<i class="fas fa-chevron-down"></i> 展开/收起
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-info ms-2" onclick='showDifferenceRawData(${JSON.stringify(JSON.stringify(key))})'>
|
||||
<button class="btn btn-sm btn-outline-info ms-2" onclick='showDifferenceRawData(${JSON.stringify(JSON.stringify(keyObj))})'>
|
||||
<i class="fas fa-code"></i> 原生数据
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mb-0 mt-2"><strong>主键:</strong> ${formatCompositeKey(key)}</p>
|
||||
<p class="mb-0 mt-2"><strong>主键:</strong> ${formatCompositeKey(keyObj)}</p>
|
||||
</div>
|
||||
<div class="collapse show" id="keyGroup${globalIndex}">
|
||||
<div class="collapse" id="keyGroup${globalIndex}">
|
||||
<div class="card-body">
|
||||
`;
|
||||
|
||||
@@ -1039,6 +1051,18 @@ function displayDifferences() {
|
||||
const jsonClass = isJson ? 'json-field' : '';
|
||||
const fieldId = `field_${globalIndex}_${diffIndex}`;
|
||||
|
||||
// 调试:打印差异数据
|
||||
console.log(`差异 ${diff.field}:`, {
|
||||
pro_value: diff.pro_value,
|
||||
test_value: diff.test_value,
|
||||
is_json: diff.is_json,
|
||||
is_array: diff.is_array
|
||||
});
|
||||
|
||||
// 确保值存在
|
||||
const proValue = diff.pro_value !== undefined && diff.pro_value !== null ? diff.pro_value : '无数据';
|
||||
const testValue = diff.test_value !== undefined && diff.test_value !== null ? diff.test_value : '无数据';
|
||||
|
||||
html += `
|
||||
<div class="mb-3 border-start border-3 border-danger ps-3">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
@@ -1060,7 +1084,7 @@ function displayDifferences() {
|
||||
<small class="text-success fw-bold">生产环境</small>
|
||||
</div>
|
||||
<div class="card-body p-2">
|
||||
<pre class="field-value mb-0 ${jsonClass}" style="max-height: 200px; overflow-y: auto;">${escapeHtml(diff.pro_value)}</pre>
|
||||
<pre class="field-value mb-0 ${jsonClass}" style="max-height: 200px; overflow-y: auto;">${escapeHtml(proValue)}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1070,7 +1094,7 @@ function displayDifferences() {
|
||||
<small class="text-info fw-bold">测试环境</small>
|
||||
</div>
|
||||
<div class="card-body p-2">
|
||||
<pre class="field-value mb-0 ${jsonClass}" style="max-height: 200px; overflow-y: auto;">${escapeHtml(diff.test_value)}</pre>
|
||||
<pre class="field-value mb-0 ${jsonClass}" style="max-height: 200px; overflow-y: auto;">${escapeHtml(testValue)}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1115,8 +1139,21 @@ function displayDifferences() {
|
||||
|
||||
// HTML转义函数,防止XSS
|
||||
function escapeHtml(text) {
|
||||
// 处理undefined和null值
|
||||
if (text === undefined || text === null) {
|
||||
return '<span class="text-muted">无数据</span>';
|
||||
}
|
||||
|
||||
// 确保是字符串
|
||||
const str = String(text);
|
||||
|
||||
// 如果是空字符串
|
||||
if (str === '') {
|
||||
return '<span class="text-muted">空值</span>';
|
||||
}
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
div.textContent = str;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
@@ -1671,6 +1708,7 @@ function showRawData(keyStr) {
|
||||
try {
|
||||
// 解析key
|
||||
const key = JSON.parse(keyStr);
|
||||
console.log('查找原始数据,key:', key);
|
||||
|
||||
// 在原生数据中查找对应的记录
|
||||
let proData = null;
|
||||
@@ -1682,15 +1720,40 @@ function showRawData(keyStr) {
|
||||
// 支持复合主键比较
|
||||
if (typeof key === 'object' && !Array.isArray(key)) {
|
||||
// 复合主键情况:比较所有主键字段
|
||||
return Object.keys(key).every(keyField =>
|
||||
JSON.stringify(item[keyField]) === JSON.stringify(key[keyField])
|
||||
);
|
||||
const matches = Object.keys(key).every(keyField => {
|
||||
const itemValue = item[keyField];
|
||||
const keyValue = key[keyField];
|
||||
|
||||
// 如果都是undefined或null,认为匹配
|
||||
if (itemValue == null && keyValue == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 转换为字符串进行比较
|
||||
const itemStr = String(itemValue);
|
||||
const keyStr = String(keyValue);
|
||||
|
||||
// 直接比较字符串
|
||||
return itemStr === keyStr;
|
||||
});
|
||||
|
||||
if (matches) {
|
||||
console.log('找到生产环境数据:', item);
|
||||
}
|
||||
return matches;
|
||||
} else {
|
||||
// 单主键情况:保持原有逻辑
|
||||
const keyField = Object.keys(key)[0];
|
||||
return JSON.stringify(item[keyField]) === JSON.stringify(key[keyField]);
|
||||
// 单主键情况(兼容旧代码)
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// 如果没找到,输出调试信息
|
||||
if (!proData) {
|
||||
console.log('未找到生产数据,查找的key:', key);
|
||||
console.log('可用的数据:', currentResults.raw_pro_data.map(item => ({
|
||||
statusid: item.statusid
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
// 查找测试环境数据
|
||||
@@ -1699,17 +1762,44 @@ function showRawData(keyStr) {
|
||||
// 支持复合主键比较
|
||||
if (typeof key === 'object' && !Array.isArray(key)) {
|
||||
// 复合主键情况:比较所有主键字段
|
||||
return Object.keys(key).every(keyField =>
|
||||
JSON.stringify(item[keyField]) === JSON.stringify(key[keyField])
|
||||
);
|
||||
const matches = Object.keys(key).every(keyField => {
|
||||
const itemValue = item[keyField];
|
||||
const keyValue = key[keyField];
|
||||
|
||||
// 如果都是undefined或null,认为匹配
|
||||
if (itemValue == null && keyValue == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 转换为字符串进行比较
|
||||
const itemStr = String(itemValue);
|
||||
const keyStr = String(keyValue);
|
||||
|
||||
// 直接比较字符串
|
||||
return itemStr === keyStr;
|
||||
});
|
||||
|
||||
if (matches) {
|
||||
console.log('找到测试环境数据:', item);
|
||||
}
|
||||
return matches;
|
||||
} else {
|
||||
// 单主键情况:保持原有逻辑
|
||||
const keyField = Object.keys(key)[0];
|
||||
return JSON.stringify(item[keyField]) === JSON.stringify(key[keyField]);
|
||||
// 单主键情况(兼容旧代码)
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// 如果没找到,输出调试信息
|
||||
if (!testData) {
|
||||
console.log('未找到测试数据,查找的key:', key);
|
||||
console.log('可用的数据:', currentResults.raw_test_data.map(item => ({
|
||||
statusid: item.statusid
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
console.log('查找结果 - 生产数据:', proData, '测试数据:', testData);
|
||||
|
||||
// 创建模态框内容
|
||||
const modalContent = `
|
||||
<div class="modal fade" id="rawDataModal" tabindex="-1">
|
||||
@@ -1833,14 +1923,47 @@ function showRawData(keyStr) {
|
||||
|
||||
// 延迟渲染对比视图和树形视图
|
||||
setTimeout(() => {
|
||||
renderDiffView(proData, testData);
|
||||
renderTreeView('proTreeView', proData);
|
||||
renderTreeView('testTreeView', testData);
|
||||
console.log('开始渲染视图,生产数据:', proData, '测试数据:', testData);
|
||||
|
||||
// 渲染对比视图
|
||||
try {
|
||||
renderDiffView(proData, testData);
|
||||
console.log('对比视图渲染完成');
|
||||
} catch (e) {
|
||||
console.error('对比视图渲染失败:', e);
|
||||
}
|
||||
|
||||
// 渲染树形视图
|
||||
try {
|
||||
renderTreeView('proTreeView', proData);
|
||||
renderTreeView('testTreeView', testData);
|
||||
console.log('树形视图渲染完成');
|
||||
} catch (e) {
|
||||
console.error('树形视图渲染失败:', e);
|
||||
}
|
||||
|
||||
// 为格式化视图添加同步滚动
|
||||
setupSyncScroll('formatted');
|
||||
// 为树形视图添加同步滚动
|
||||
setupSyncScroll('tree');
|
||||
|
||||
// 添加标签页切换事件监听器,确保切换时重新渲染
|
||||
const tabButtons = document.querySelectorAll('#rawDataTabs button[data-bs-toggle="tab"]');
|
||||
tabButtons.forEach(button => {
|
||||
button.addEventListener('shown.bs.tab', (event) => {
|
||||
const targetId = event.target.getAttribute('data-bs-target');
|
||||
console.log('切换到标签页:', targetId);
|
||||
|
||||
if (targetId === '#diff') {
|
||||
// 重新渲染对比视图
|
||||
renderDiffView(proData, testData);
|
||||
} else if (targetId === '#tree') {
|
||||
// 重新渲染树形视图
|
||||
renderTreeView('proTreeView', proData);
|
||||
renderTreeView('testTreeView', testData);
|
||||
}
|
||||
});
|
||||
});
|
||||
}, 100);
|
||||
|
||||
} catch (error) {
|
||||
@@ -1875,6 +1998,12 @@ function showDifferenceRawData(keyStr) {
|
||||
function renderDiffView(proData, testData) {
|
||||
const diffViewContainer = document.getElementById('diffView');
|
||||
|
||||
// 确保容器存在
|
||||
if (!diffViewContainer) {
|
||||
console.error('对比视图容器不存在');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!proData && !testData) {
|
||||
diffViewContainer.innerHTML = '<p class="text-muted text-center">无数据可对比</p>';
|
||||
return;
|
||||
@@ -2256,6 +2385,12 @@ function normalizeValue(value) {
|
||||
function renderTreeView(containerId, data) {
|
||||
const container = document.getElementById(containerId);
|
||||
|
||||
// 确保容器存在
|
||||
if (!container) {
|
||||
console.error('树形视图容器不存在:', containerId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
container.innerHTML = '<p class="text-muted">无数据</p>';
|
||||
return;
|
||||
|
Reference in New Issue
Block a user