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