Files
BigDataTool/templates/db_compare.html
2025-07-31 18:05:10 +08:00

399 lines
20 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数据库查询比对工具</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;
}
</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-tools"></i> 大数据工具集合
</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="/">首页</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/db-compare">数据库比对</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container-fluid py-4">
<div class="row">
<div class="col-12">
<h1 class="text-center mb-4">
<i class="fas fa-database"></i> 数据库查询比对工具
</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>
</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 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="10.20.2.22,10.20.2.23">
</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="cbase">
</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="yuqing_skinny">
</div>
<div class="col-6">
<label class="form-label">表名</label>
<input type="text" class="form-control form-control-sm" id="pro_table" placeholder="document">
</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="10.20.2.22,10.20.2.23">
</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="cbase">
</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="yuqing_skinny">
</div>
<div class="col-6">
<label class="form-label">表名</label>
<input type="text" class="form-control form-control-sm" id="test_table" placeholder="document_test">
</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" value="docid">
</div>
<div class="mb-3">
<label class="form-label">比较字段 (空则比较全部,逗号分隔)</label>
<textarea class="form-control form-control-sm compare-fields" id="fields_to_compare" placeholder="留空表示比较所有字段&#10;或输入: field1,field2,field3"></textarea>
</div>
<div>
<label class="form-label">排除字段 (逗号分隔)</label>
<textarea class="form-control form-control-sm exclude-fields" id="exclude_fields" placeholder="排除不需要比较的字段&#10;如: 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值一行一个&#10;例如:&#10;key1&#10;key2&#10;key3"></textarea>
</div>
<div class="mb-3">
<button class="btn btn-primary" onclick="executeQuery()">
<i class="fas fa-play"></i> 执行查询比对
</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">正在执行查询比对...</p>
</div>
</div>
<!-- 结果面板 -->
<div class="result-section" id="results" style="display: none;">
<!-- 统计信息 -->
<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="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="summary-panel" role="tabpanel">
<div id="comparison-summary">
<!-- 总结报告将在这里动态生成 -->
</div>
</div>
</div>
</div>
</div>
</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>