初始化项目

This commit is contained in:
2025-07-31 18:05:10 +08:00
commit 6fecd70ca5
7 changed files with 3496 additions and 0 deletions

399
templates/db_compare.html Normal file
View File

@@ -0,0 +1,399 @@
<!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>