775 lines
39 KiB
HTML
775 lines
39 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Cassandra数据比对工具 - DataTools Pro</title>
|
||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||
<style>
|
||
.config-section {
|
||
background-color: #f8f9fa;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
margin-bottom: 20px;
|
||
}
|
||
.result-section {
|
||
margin-top: 30px;
|
||
}
|
||
.difference-item {
|
||
border-left: 4px solid #dc3545;
|
||
padding-left: 15px;
|
||
margin-bottom: 15px;
|
||
background-color: #fff5f5;
|
||
padding: 15px;
|
||
border-radius: 5px;
|
||
}
|
||
.stat-card {
|
||
text-align: center;
|
||
padding: 20px;
|
||
border-radius: 10px;
|
||
margin-bottom: 20px;
|
||
}
|
||
.loading {
|
||
display: none;
|
||
}
|
||
.query-keys {
|
||
min-height: 120px;
|
||
}
|
||
.compare-fields {
|
||
min-height: 80px;
|
||
}
|
||
.exclude-fields {
|
||
min-height: 80px;
|
||
}
|
||
.json-field {
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 0.9em;
|
||
background-color: #f8f9fa !important;
|
||
}
|
||
.badge {
|
||
font-size: 0.7em;
|
||
}
|
||
.field-container {
|
||
border: 1px solid #e9ecef;
|
||
border-radius: 5px;
|
||
padding: 10px;
|
||
margin-bottom: 10px;
|
||
}
|
||
.field-value {
|
||
font-size: 0.85em;
|
||
max-height: 200px;
|
||
overflow-y: auto;
|
||
margin: 0;
|
||
}
|
||
.field-header {
|
||
font-weight: 600;
|
||
color: #495057;
|
||
}
|
||
.pagination {
|
||
--bs-pagination-padding-x: 0.5rem;
|
||
--bs-pagination-padding-y: 0.25rem;
|
||
--bs-pagination-font-size: 0.875rem;
|
||
}
|
||
.copy-btn {
|
||
position: absolute;
|
||
top: 5px;
|
||
right: 5px;
|
||
padding: 2px 6px;
|
||
font-size: 0.75rem;
|
||
border-radius: 3px;
|
||
}
|
||
.field-container {
|
||
position: relative;
|
||
}
|
||
|
||
/* 确保提示消息在最顶层 */
|
||
.alert {
|
||
position: fixed !important;
|
||
top: 20px !important;
|
||
left: 50% !important;
|
||
transform: translateX(-50%) !important;
|
||
z-index: 9999 !important;
|
||
min-width: 300px !important;
|
||
max-width: 600px !important;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;
|
||
}
|
||
|
||
/* 树形视图样式 */
|
||
.tree-view {
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 0.9em;
|
||
max-height: 500px;
|
||
overflow-y: auto;
|
||
border: 1px solid #e9ecef;
|
||
border-radius: 5px;
|
||
padding: 10px;
|
||
background-color: #f8f9fa;
|
||
}
|
||
|
||
.tree-node {
|
||
margin: 2px 0;
|
||
}
|
||
|
||
.tree-toggle {
|
||
cursor: pointer;
|
||
user-select: none;
|
||
margin-right: 5px;
|
||
color: #6c757d;
|
||
}
|
||
|
||
.tree-toggle:hover {
|
||
color: #495057;
|
||
}
|
||
|
||
.tree-children {
|
||
margin-left: 15px;
|
||
}
|
||
|
||
.tree-item {
|
||
margin: 1px 0;
|
||
padding: 1px 0;
|
||
}
|
||
|
||
/* 原生数据搜索高亮 */
|
||
.raw-data-container mark {
|
||
background-color: #fff3cd !important;
|
||
padding: 1px 2px;
|
||
border-radius: 2px;
|
||
}
|
||
|
||
/* 自定义分页输入框 */
|
||
.form-control:focus {
|
||
border-color: #86b7fe;
|
||
outline: 0;
|
||
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
|
||
}
|
||
|
||
/* 树形视图样式 */
|
||
.tree-view {
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
padding: 15px;
|
||
background-color: #f8f9fa;
|
||
border-radius: 5px;
|
||
overflow-x: auto;
|
||
max-height: 500px;
|
||
overflow-y: auto;
|
||
}
|
||
.tree-key {
|
||
color: #0066cc;
|
||
font-weight: bold;
|
||
}
|
||
.tree-value {
|
||
color: #008000;
|
||
}
|
||
.tree-index {
|
||
color: #666;
|
||
font-style: italic;
|
||
}
|
||
.tree-object, .tree-array {
|
||
margin-left: 20px;
|
||
}
|
||
|
||
/* 原生数据模态框样式 */
|
||
#rawDataModal .modal-dialog {
|
||
max-width: 90%;
|
||
}
|
||
#rawDataModal pre {
|
||
white-space: pre-wrap;
|
||
word-wrap: break-word;
|
||
}
|
||
#rawDataModal .nav-tabs .nav-link {
|
||
color: #495057;
|
||
}
|
||
#rawDataModal .nav-tabs .nav-link.active {
|
||
font-weight: bold;
|
||
}
|
||
|
||
/* 对比视图增强样式 */
|
||
#diffView .table-warning td {
|
||
background-color: #fff3cd !important;
|
||
border-color: #ffc107;
|
||
}
|
||
#diffView .table-danger td {
|
||
background-color: #f8d7da !important;
|
||
font-weight: bold;
|
||
color: #721c24;
|
||
}
|
||
#diffView .table-success td {
|
||
background-color: #d4edda !important;
|
||
color: #155724;
|
||
}
|
||
#diffView .badge {
|
||
font-size: 0.7rem;
|
||
}
|
||
#diffView .text-truncate {
|
||
cursor: pointer;
|
||
}
|
||
#diffView .text-truncate:hover {
|
||
background-color: rgba(0,0,0,0.05);
|
||
padding: 2px 4px;
|
||
border-radius: 3px;
|
||
}
|
||
#diffView pre {
|
||
font-size: 12px;
|
||
line-height: 1.4;
|
||
margin: 0;
|
||
background-color: #f8f9fa;
|
||
border: 1px solid #dee2e6;
|
||
}
|
||
#diffView .table-warning pre {
|
||
background-color: #fff;
|
||
border-color: #ffc107;
|
||
}
|
||
#diffView td {
|
||
padding: 0.5rem;
|
||
}
|
||
|
||
/* 分表配置样式 */
|
||
.sharding-config-section {
|
||
background-color: #e8f4fd;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
margin-bottom: 20px;
|
||
border: 2px solid #0d6efd;
|
||
}
|
||
.sharding-info {
|
||
background-color: #f8f9fa;
|
||
border-radius: 8px;
|
||
padding: 15px;
|
||
margin-bottom: 20px;
|
||
}
|
||
.shard-table-info {
|
||
display: inline-block;
|
||
margin: 5px;
|
||
padding: 5px 10px;
|
||
background-color: #e7f3ff;
|
||
border-radius: 15px;
|
||
font-size: 0.9em;
|
||
border: 1px solid #b3d9ff;
|
||
}
|
||
.shard-error-info {
|
||
background-color: #ffe6e6;
|
||
border-color: #ffb3b3;
|
||
color: #d63384;
|
||
}
|
||
|
||
/* 面包屑导航样式 */
|
||
.breadcrumb {
|
||
background: none;
|
||
padding: 0;
|
||
}
|
||
.breadcrumb-item a {
|
||
color: #007bff;
|
||
text-decoration: none;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<!-- 导航栏 -->
|
||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||
<div class="container">
|
||
<a class="navbar-brand" href="/">
|
||
<i class="fas fa-database me-2"></i> DataTools Pro
|
||
</a>
|
||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||
<span class="navbar-toggler-icon"></span>
|
||
</button>
|
||
<div class="collapse navbar-collapse" id="navbarNav">
|
||
<ul class="navbar-nav ms-auto">
|
||
<li class="nav-item">
|
||
<a class="nav-link" href="/">
|
||
<i class="fas fa-home"></i> 首页
|
||
</a>
|
||
</li>
|
||
<li class="nav-item">
|
||
<a class="nav-link active" href="/cassandra-compare">
|
||
<i class="fas fa-database"></i> Cassandra比对
|
||
</a>
|
||
</li>
|
||
<li class="nav-item">
|
||
<a class="nav-link" href="/redis-compare">
|
||
<i class="fab fa-redis"></i> Redis比对
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<div class="container-fluid py-4">
|
||
<!-- 面包屑导航 -->
|
||
<nav aria-label="breadcrumb">
|
||
<ol class="breadcrumb">
|
||
<li class="breadcrumb-item"><a href="/">首页</a></li>
|
||
<li class="breadcrumb-item active" aria-current="page">Cassandra数据比对工具</li>
|
||
</ol>
|
||
</nav>
|
||
|
||
<div class="row">
|
||
<div class="col-12">
|
||
<h1 class="text-center mb-4">
|
||
<i class="fas fa-database"></i> Cassandra数据比对工具
|
||
<small class="text-muted d-block fs-6 mt-2">支持单表查询和分表查询两种模式</small>
|
||
</h1>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<!-- 配置面板 -->
|
||
<div class="col-lg-4">
|
||
<div class="config-section">
|
||
<h4><i class="fas fa-cogs"></i> 配置管理</h4>
|
||
|
||
<!-- 配置组管理 -->
|
||
<div class="card mb-3">
|
||
<div class="card-header">
|
||
<h6><i class="fas fa-layer-group"></i> 配置组管理</h6>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="row mb-3">
|
||
<div class="col-8">
|
||
<select class="form-select form-select-sm" id="configGroupSelect">
|
||
<option value="">选择配置组...</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-4">
|
||
<button class="btn btn-primary btn-sm w-100" onclick="loadSelectedConfigGroup()">
|
||
<i class="fas fa-download"></i> 加载
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="row">
|
||
<div class="col-6">
|
||
<button class="btn btn-success btn-sm w-100" onclick="showSaveConfigDialog()">
|
||
<i class="fas fa-save"></i> 保存配置组
|
||
</button>
|
||
</div>
|
||
<div class="col-6">
|
||
<button class="btn btn-info btn-sm w-100" onclick="showManageConfigDialog()">
|
||
<i class="fas fa-cog"></i> 管理配置组
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="row mt-2">
|
||
<div class="col-4">
|
||
<button class="btn btn-warning btn-sm w-100" onclick="showQueryHistoryDialog()">
|
||
<i class="fas fa-history"></i> 查询历史
|
||
</button>
|
||
</div>
|
||
<div class="col-4">
|
||
<button class="btn btn-info btn-sm w-100" onclick="showQueryLogsDialog()">
|
||
<i class="fas fa-file-alt"></i> 查询日志
|
||
</button>
|
||
</div>
|
||
<div class="col-4">
|
||
<button class="btn btn-secondary btn-sm w-100" onclick="showSaveHistoryDialog()">
|
||
<i class="fas fa-bookmark"></i> 保存历史
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<button class="btn btn-secondary btn-sm" onclick="loadDefaultConfig()">
|
||
<i class="fas fa-refresh"></i> 重置为空配置
|
||
</button>
|
||
<button class="btn btn-success btn-sm" onclick="exportConfig()">
|
||
<i class="fas fa-file-export"></i> 导出配置
|
||
</button>
|
||
</div>
|
||
|
||
<!-- 查询模式切换 -->
|
||
<div class="card mb-3">
|
||
<div class="card-header">
|
||
<h6><i class="fas fa-toggle-on"></i> 查询模式</h6>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-check form-switch">
|
||
<input class="form-check-input" type="checkbox" id="enableSharding" onchange="toggleShardingMode()">
|
||
<label class="form-check-label" for="enableSharding">
|
||
<strong>启用分表查询模式</strong>
|
||
<small class="text-muted d-block">支持TWCS时间分表的智能索引计算</small>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 分表参数配置 (默认隐藏) -->
|
||
<div class="sharding-config-section" id="shardingConfig" style="display: none;">
|
||
<div class="card mb-3">
|
||
<div class="card-header">
|
||
<h6><i class="fas fa-layer-group"></i> 分表参数配置</h6>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="row">
|
||
<div class="col-md-6">
|
||
<h6 class="text-primary">生产环境分表配置</h6>
|
||
<div class="form-check mb-2">
|
||
<input class="form-check-input" type="checkbox" id="use_sharding_for_pro" checked>
|
||
<label class="form-check-label" for="use_sharding_for_pro">
|
||
启用分表查询
|
||
</label>
|
||
</div>
|
||
<div class="mb-2">
|
||
<label for="pro_interval_seconds" class="form-label">时间间隔(秒)</label>
|
||
<input type="number" class="form-control form-control-sm" id="pro_interval_seconds" value="604800" min="1">
|
||
<small class="form-text text-muted">默认604800秒(7天)</small>
|
||
</div>
|
||
<div class="mb-2">
|
||
<label for="pro_table_count" class="form-label">分表数量</label>
|
||
<input type="number" class="form-control form-control-sm" id="pro_table_count" value="14" min="1" max="100">
|
||
<small class="form-text text-muted">默认14张分表</small>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<h6 class="text-success">测试环境分表配置</h6>
|
||
<div class="form-check mb-2">
|
||
<input class="form-check-input" type="checkbox" id="use_sharding_for_test">
|
||
<label class="form-check-label" for="use_sharding_for_test">
|
||
启用分表查询
|
||
</label>
|
||
</div>
|
||
<div class="mb-2">
|
||
<label for="test_interval_seconds" class="form-label">时间间隔(秒)</label>
|
||
<input type="number" class="form-control form-control-sm" id="test_interval_seconds" value="604800" min="1">
|
||
<small class="form-text text-muted">默认604800秒(7天)</small>
|
||
</div>
|
||
<div class="mb-2">
|
||
<label for="test_table_count" class="form-label">分表数量</label>
|
||
<input type="number" class="form-control form-control-sm" id="test_table_count" value="14" min="1" max="100">
|
||
<small class="form-text text-muted">默认14张分表</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="alert alert-info mt-3">
|
||
<strong>分表计算说明:</strong>系统会自动从Key值中提取时间戳并计算分表索引。
|
||
计算公式:<code>时间戳 // 间隔秒数 % 分表数量</code>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 生产环境配置 -->
|
||
<div class="card mb-3">
|
||
<div class="card-header d-flex justify-content-between align-items-center">
|
||
<h6><i class="fas fa-server"></i> 生产环境配置</h6>
|
||
<button class="btn btn-sm btn-outline-primary" onclick="showImportDialog('pro')">
|
||
<i class="fas fa-download"></i> 一键导入
|
||
</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="row">
|
||
<div class="col-6">
|
||
<label class="form-label">集群名称</label>
|
||
<input type="text" class="form-control form-control-sm" id="pro_cluster_name" placeholder="Production Cluster">
|
||
</div>
|
||
<div class="col-6">
|
||
<label class="form-label">数据中心</label>
|
||
<input type="text" class="form-control form-control-sm" id="pro_datacenter" placeholder="dc1">
|
||
</div>
|
||
</div>
|
||
<div class="row mt-2">
|
||
<div class="col-8">
|
||
<label class="form-label">集群节点 (逗号分隔)</label>
|
||
<input type="text" class="form-control form-control-sm" id="pro_hosts" placeholder="127.0.0.1,127.0.0.2">
|
||
</div>
|
||
<div class="col-4">
|
||
<label class="form-label">端口</label>
|
||
<input type="number" class="form-control form-control-sm" id="pro_port" placeholder="9042">
|
||
</div>
|
||
</div>
|
||
<div class="row mt-2">
|
||
<div class="col-6">
|
||
<label class="form-label">用户名</label>
|
||
<input type="text" class="form-control form-control-sm" id="pro_username" placeholder="username">
|
||
</div>
|
||
<div class="col-6">
|
||
<label class="form-label">密码</label>
|
||
<input type="password" class="form-control form-control-sm" id="pro_password">
|
||
</div>
|
||
</div>
|
||
<div class="row mt-2">
|
||
<div class="col-6">
|
||
<label class="form-label">Keyspace</label>
|
||
<input type="text" class="form-control form-control-sm" id="pro_keyspace" placeholder="keyspace">
|
||
</div>
|
||
<div class="col-6">
|
||
<label class="form-label">表名</label>
|
||
<input type="text" class="form-control form-control-sm" id="pro_table" placeholder="tablename">
|
||
<small class="form-text text-muted" id="pro_table_hint">完整表名或基础表名(分表时)</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 测试环境配置 -->
|
||
<div class="card mb-3">
|
||
<div class="card-header d-flex justify-content-between align-items-center">
|
||
<h6><i class="fas fa-flask"></i> 测试环境配置</h6>
|
||
<button class="btn btn-sm btn-outline-primary" onclick="showImportDialog('test')">
|
||
<i class="fas fa-download"></i> 一键导入
|
||
</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="row">
|
||
<div class="col-6">
|
||
<label class="form-label">集群名称</label>
|
||
<input type="text" class="form-control form-control-sm" id="test_cluster_name" placeholder="Test Cluster">
|
||
</div>
|
||
<div class="col-6">
|
||
<label class="form-label">数据中心</label>
|
||
<input type="text" class="form-control form-control-sm" id="test_datacenter" placeholder="dc1">
|
||
</div>
|
||
</div>
|
||
<div class="row mt-2">
|
||
<div class="col-8">
|
||
<label class="form-label">集群节点 (逗号分隔)</label>
|
||
<input type="text" class="form-control form-control-sm" id="test_hosts" placeholder="127.0.0.1,127.0.0.2">
|
||
</div>
|
||
<div class="col-4">
|
||
<label class="form-label">端口</label>
|
||
<input type="number" class="form-control form-control-sm" id="test_port" placeholder="9042">
|
||
</div>
|
||
</div>
|
||
<div class="row mt-2">
|
||
<div class="col-6">
|
||
<label class="form-label">用户名</label>
|
||
<input type="text" class="form-control form-control-sm" id="test_username" placeholder="username">
|
||
</div>
|
||
<div class="col-6">
|
||
<label class="form-label">密码</label>
|
||
<input type="password" class="form-control form-control-sm" id="test_password">
|
||
</div>
|
||
</div>
|
||
<div class="row mt-2">
|
||
<div class="col-6">
|
||
<label class="form-label">Keyspace</label>
|
||
<input type="text" class="form-control form-control-sm" id="test_keyspace" placeholder="keyspace">
|
||
</div>
|
||
<div class="col-6">
|
||
<label class="form-label">表名</label>
|
||
<input type="text" class="form-control form-control-sm" id="test_table" placeholder="tablename">
|
||
<small class="form-text text-muted" id="test_table_hint">完整表名或基础表名(分表时)</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 查询配置 -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h6><i class="fas fa-search"></i> 查询配置</h6>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="mb-3">
|
||
<label class="form-label">主键字段 (逗号分隔,支持复合主键)</label>
|
||
<input type="text" class="form-control form-control-sm" id="keys" placeholder="单主键:docid 或 复合主键:docid,id" value="">
|
||
<small class="form-text text-muted">支持单主键或复合主键,复合主键用逗号分隔,如:docid,id</small>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label">比较字段 (空则比较全部,逗号分隔)</label>
|
||
<textarea class="form-control form-control-sm compare-fields" id="fields_to_compare" placeholder="留空表示比较所有字段 或输入: field1,field2,field3"></textarea>
|
||
</div>
|
||
<div>
|
||
<label class="form-label">排除字段 (逗号分隔)</label>
|
||
<textarea class="form-control form-control-sm exclude-fields" id="exclude_fields" placeholder="排除不需要比较的字段 如: field1,field2"></textarea>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 查询面板 -->
|
||
<div class="col-lg-8">
|
||
<div class="config-section">
|
||
<h4><i class="fas fa-key"></i> 查询Key管理</h4>
|
||
<div class="mb-3">
|
||
<label class="form-label">批量Key输入 (一行一个)</label>
|
||
<textarea class="form-control query-keys" id="query_values" placeholder="请输入查询的Key值,一行一个 单主键示例: key1 key2 key3 复合主键示例(逗号分隔): docid1,id1 docid2,id2 docid3,id3 分表查询示例(包含时间戳): wmid_1609459200 wmid_1610064000 wmid_1610668800"></textarea>
|
||
<small class="form-text text-muted" id="key_input_hint">单表模式:输入普通Key值 | 分表模式:Key值应包含时间戳用于计算分表索引</small>
|
||
</div>
|
||
<div class="mb-3">
|
||
<button class="btn btn-primary" onclick="executeQuery()">
|
||
<i class="fas fa-play"></i> <span id="executeButtonText">执行查询比对</span>
|
||
</button>
|
||
<button class="btn btn-secondary" onclick="clearResults()">
|
||
<i class="fas fa-trash"></i> 清空结果
|
||
</button>
|
||
</div>
|
||
|
||
<!-- 加载动画 -->
|
||
<div class="loading text-center" id="loading">
|
||
<div class="spinner-border text-primary" role="status">
|
||
<span class="visually-hidden">查询中...</span>
|
||
</div>
|
||
<p class="mt-2" id="loadingText">正在执行查询比对...</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 结果面板 -->
|
||
<div class="result-section" id="results" style="display: none;">
|
||
<!-- 分表查询信息 (分表模式时显示) -->
|
||
<div class="sharding-info" id="shardingInfoContainer" style="display: none;">
|
||
<h6><i class="fas fa-info-circle"></i> 分表查询信息</h6>
|
||
<div id="shardingInfo"></div>
|
||
</div>
|
||
|
||
<!-- 统计信息 -->
|
||
<div class="row" id="stats">
|
||
<!-- 统计卡片将在这里动态生成 -->
|
||
</div>
|
||
|
||
<!-- 结果选项卡导航 -->
|
||
<div class="card mt-4">
|
||
<div class="card-header">
|
||
<ul class="nav nav-tabs card-header-tabs" id="resultTabs" role="tablist">
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link active" id="differences-tab" data-bs-toggle="tab" data-bs-target="#differences-panel" type="button" role="tab">
|
||
<i class="fas fa-exclamation-triangle"></i> 差异详情 <span class="badge bg-danger ms-1" id="diff-count">0</span>
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="identical-tab" data-bs-toggle="tab" data-bs-target="#identical-panel" type="button" role="tab">
|
||
<i class="fas fa-check-circle"></i> 相同结果 <span class="badge bg-success ms-1" id="identical-count">0</span>
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="rawdata-tab" data-bs-toggle="tab" data-bs-target="#rawdata-panel" type="button" role="tab">
|
||
<i class="fas fa-database"></i> 原始数据 <span class="badge bg-info ms-1" id="rawdata-count">0</span>
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="summary-tab" data-bs-toggle="tab" data-bs-target="#summary-panel" type="button" role="tab">
|
||
<i class="fas fa-chart-pie"></i> 比较总结
|
||
</button>
|
||
</li>
|
||
</ul>
|
||
<div class="mt-2">
|
||
<button class="btn btn-sm btn-outline-primary" onclick="exportResults()">
|
||
<i class="fas fa-download"></i> 导出结果
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card-body">
|
||
<div class="tab-content" id="resultTabContent">
|
||
<!-- 差异详情面板 -->
|
||
<div class="tab-pane fade show active" id="differences-panel" role="tabpanel">
|
||
<div id="differences">
|
||
<!-- 差异内容将在这里动态生成 -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 相同结果面板 -->
|
||
<div class="tab-pane fade" id="identical-panel" role="tabpanel">
|
||
<div id="identical-results">
|
||
<!-- 相同结果将在这里动态生成 -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 原始数据面板 -->
|
||
<div class="tab-pane fade" id="rawdata-panel" role="tabpanel">
|
||
<div class="container-fluid">
|
||
<!-- 筛选控制区 -->
|
||
<div class="row mb-3">
|
||
<div class="col-md-6">
|
||
<div class="d-flex align-items-center">
|
||
<label class="form-label me-2 mb-0">显示环境:</label>
|
||
<div class="form-check form-check-inline">
|
||
<input class="form-check-input" type="checkbox" id="showProData" checked onchange="filterRawData()">
|
||
<label class="form-check-label" for="showProData">生产环境</label>
|
||
</div>
|
||
<div class="form-check form-check-inline">
|
||
<input class="form-check-input" type="checkbox" id="showTestData" checked onchange="filterRawData()">
|
||
<label class="form-check-label" for="showTestData">测试环境</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<div class="input-group">
|
||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||
<input type="text" class="form-control" placeholder="搜索Key或字段值..."
|
||
onkeyup="searchRawData(this.value)" id="rawDataSearch">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 原始数据内容 -->
|
||
<div id="raw-data-content">
|
||
<!-- 原始数据将在这里动态生成 -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 比较总结面板 -->
|
||
<div class="tab-pane fade" id="summary-panel" role="tabpanel">
|
||
<div id="comparison-summary">
|
||
<!-- 总结报告将在这里动态生成 -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 查询日志模态框 -->
|
||
<div class="modal fade" id="queryLogsModal" tabindex="-1" aria-labelledby="queryLogsModalLabel" aria-hidden="true">
|
||
<div class="modal-dialog modal-xl">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title" id="queryLogsModalLabel">
|
||
<i class="fas fa-file-alt"></i> 查询日志管理
|
||
</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||
<div class="d-flex align-items-center">
|
||
<h6 class="mb-0 me-3">查询执行日志</h6>
|
||
<div class="form-check form-check-inline">
|
||
<input class="form-check-input" type="checkbox" id="modal-log-level-info" checked onchange="filterModalLogsByLevel()">
|
||
<label class="form-check-label text-primary" for="modal-log-level-info">INFO</label>
|
||
</div>
|
||
<div class="form-check form-check-inline">
|
||
<input class="form-check-input" type="checkbox" id="modal-log-level-warning" checked onchange="filterModalLogsByLevel()">
|
||
<label class="form-check-label text-warning" for="modal-log-level-warning">WARNING</label>
|
||
</div>
|
||
<div class="form-check form-check-inline">
|
||
<input class="form-check-input" type="checkbox" id="modal-log-level-error" checked onchange="filterModalLogsByLevel()">
|
||
<label class="form-check-label text-danger" for="modal-log-level-error">ERROR</label>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<button class="btn btn-sm btn-outline-primary me-2" onclick="refreshModalQueryLogs()">
|
||
<i class="fas fa-sync-alt"></i> 刷新
|
||
</button>
|
||
<button class="btn btn-sm btn-outline-warning me-2" onclick="cleanupOldLogs()">
|
||
<i class="fas fa-broom"></i> 清理旧日志
|
||
</button>
|
||
<button class="btn btn-sm btn-outline-danger" onclick="clearQueryLogs()">
|
||
<i class="fas fa-trash"></i> 清空
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div id="modal-query-logs" style="max-height: 600px; overflow-y: auto;">
|
||
<!-- 查询日志将在这里动态生成 -->
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||
<script src="{{ url_for('static', filename='js/app.js') }}"></script>
|
||
</body>
|
||
</html> |