Files
BigDataTool/templates/redis_compare.html
2025-08-12 16:27:00 +08:00

982 lines
52 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>Redis集群比对工具 - 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;
}
.identical-item {
border-left: 4px solid #28a745;
padding-left: 15px;
margin-bottom: 15px;
background-color: #f8fff8;
padding: 15px;
border-radius: 5px;
}
.missing-item {
border-left: 4px solid #ffc107;
padding-left: 15px;
margin-bottom: 15px;
background-color: #fffbf0;
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;
}
.redis-value {
font-family: 'Courier New', monospace;
font-size: 0.9em;
background-color: #f8f9fa !important;
white-space: pre-wrap;
word-break: break-all;
}
.node-input {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.node-input input {
margin-right: 10px;
}
.btn-redis {
background: linear-gradient(135deg, #dc143c 0%, #b91c1c 100%);
border: none;
color: white;
}
.btn-redis:hover {
background: linear-gradient(135deg, #b91c1c 0%, #991b1b 100%);
color: white;
transform: translateY(-1px);
}
.redis-logo {
color: #dc143c;
}
.cluster-config {
border: 2px solid #e9ecef;
border-radius: 10px;
padding: 15px;
margin-bottom: 15px;
}
.cluster-config.active {
border-color: #dc143c;
background-color: #fff8f8;
}
.log-viewer {
background-color: #1e1e1e;
color: #d4d4d4;
font-family: 'Courier New', monospace;
font-size: 0.9em;
padding: 15px;
border-radius: 5px;
max-height: 400px;
overflow-y: auto;
}
.navbar {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
}
.breadcrumb {
background: none;
padding: 0;
}
.breadcrumb-item a {
color: #007bff;
text-decoration: none;
}
.pagination {
--bs-pagination-padding-x: 0.5rem;
--bs-pagination-padding-y: 0.25rem;
--bs-pagination-font-size: 0.875rem;
}
/* 确保提示消息在最顶层 */
.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;
}
/* 拖拽区域样式 */
#dropZone {
transition: all 0.3s ease;
}
#dropZone:hover {
border-color: #dc143c !important;
background-color: #fff8f8;
}
#dropZone.dragover {
border-color: #dc143c !important;
background-color: #fff8f8;
transform: scale(1.02);
}
/* 配置预览样式 */
.config-preview-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 5px 0;
border-bottom: 1px solid #eee;
}
.config-preview-item:last-child {
border-bottom: none;
}
.config-preview-label {
font-weight: 600;
color: #495057;
}
.config-preview-value {
color: #6c757d;
font-family: 'Courier New', monospace;
font-size: 0.9em;
}
/* Redis日志分组样式 */
.redis-log-group {
border: 1px solid #dee2e6;
border-radius: 8px;
overflow: hidden;
}
.redis-log-group .card-header {
transition: background-color 0.2s ease;
}
.redis-log-group .card-header:hover {
background-color: rgba(0, 123, 255, 0.1) !important;
}
.redis-log-content {
max-height: 400px;
overflow-y: auto;
}
.log-item {
background-color: #f8f9fa;
border-radius: 4px;
transition: all 0.2s ease;
}
.log-item:hover {
background-color: #e9ecef;
transform: translateX(2px);
}
.log-item .badge {
min-width: 60px;
font-size: 0.75em;
}
/* 日志级别边框颜色 */
.border-info {
border-color: #0dcaf0 !important;
}
.border-warning {
border-color: #ffc107 !important;
}
.border-danger {
border-color: #dc3545 !important;
}
.border-secondary {
border-color: #6c757d !important;
}
</style>
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-dark">
<div class="container">
<a class="navbar-brand" href="/">
<i class="fab fa-redis me-2 redis-logo"></i>
DataTools Pro
</a>
<div class="navbar-nav ms-auto">
<a class="nav-link" href="/">
<i class="fas fa-home"></i> 首页
</a>
<a class="nav-link" href="/cassandra-compare">
<i class="fas fa-database"></i> Cassandra比对
</a>
<a class="nav-link active" href="/redis-compare">
<i class="fab fa-redis"></i> Redis比对
</a>
</div>
</div>
</nav>
<div class="container-fluid mt-4">
<!-- 面包屑导航 -->
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">首页</a></li>
<li class="breadcrumb-item active" aria-current="page">Redis集群比对工具</li>
</ol>
</nav>
<!-- 页面标题 -->
<div class="row">
<div class="col-12">
<h1 class="text-center mb-4">
<i class="fab fa-redis redis-logo"></i> Redis集群比对工具
<small class="text-muted d-block fs-6 mt-2">专业的Redis集群数据比对工具支持随机采样和指定Key查询</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="redisConfigGroupSelect">
<option value="">选择Redis配置组...</option>
</select>
</div>
<div class="col-4">
<button class="btn btn-primary btn-sm w-100" onclick="loadSelectedRedisConfigGroup()">
<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="showSaveRedisConfigDialog()">
<i class="fas fa-save"></i> 保存配置组
</button>
</div>
<div class="col-6">
<button class="btn btn-info btn-sm w-100" onclick="showManageRedisConfigDialog()">
<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="showRedisQueryHistoryDialog()">
<i class="fas fa-history"></i> 查询历史
</button>
</div>
<div class="col-4">
<button class="btn btn-info btn-sm w-100" onclick="showRedisQueryLogsDialog()">
<i class="fas fa-file-alt"></i> 查询日志
</button>
</div>
<div class="col-4">
<button class="btn btn-secondary btn-sm w-100" onclick="showSaveRedisHistoryDialog()">
<i class="fas fa-bookmark"></i> 保存历史
</button>
</div>
</div>
</div>
</div>
<!-- Redis集群配置 -->
<div class="card mb-3">
<div class="card-header">
<h6><i class="fas fa-server"></i> Redis集群配置</h6>
</div>
<div class="card-body">
<!-- 集群1配置 -->
<div class="cluster-config mb-3">
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="text-primary mb-0"><i class="fas fa-server"></i> 集群1 (生产)</h6>
<button type="button" class="btn btn-outline-primary btn-sm" onclick="showImportConfigDialog('cluster1')" title="导入YAML配置">
<i class="fas fa-file-import"></i> 导入配置
</button>
</div>
<div class="row mb-2">
<div class="col-12">
<label class="form-label">集群名称</label>
<input type="text" class="form-control form-control-sm" id="cluster1Name" placeholder="生产集群" value="生产集群">
</div>
</div>
<div class="mb-2">
<label class="form-label">Redis节点</label>
<div id="cluster1Nodes">
<div class="node-input">
<input type="text" class="form-control form-control-sm me-2" placeholder="127.0.0.1" value="127.0.0.1" style="flex: 2;">
<input type="number" class="form-control form-control-sm me-2" placeholder="6379" value="6379" style="flex: 1;">
<button type="button" class="btn btn-outline-danger btn-sm" onclick="removeRedisNode(this, 'cluster1')">
<i class="fas fa-minus"></i>
</button>
</div>
</div>
<button type="button" class="btn btn-outline-primary btn-sm mt-2" onclick="addRedisNode('cluster1')">
<i class="fas fa-plus"></i> 添加节点
</button>
</div>
<div class="row mb-2">
<div class="col-12">
<label class="form-label">密码</label>
<input type="password" class="form-control form-control-sm" id="cluster1Password" placeholder="可选">
</div>
</div>
<div class="row mb-2">
<div class="col-4">
<label class="form-label">连接超时(秒)</label>
<input type="number" class="form-control form-control-sm" id="cluster1SocketTimeout" value="3" min="1" max="60">
</div>
<div class="col-4">
<label class="form-label">建立超时(秒)</label>
<input type="number" class="form-control form-control-sm" id="cluster1SocketConnectTimeout" value="3" min="1" max="60">
</div>
<div class="col-4">
<label class="form-label">最大连接数</label>
<input type="number" class="form-control form-control-sm" id="cluster1MaxConnectionsPerNode" value="16" min="1" max="100">
</div>
</div>
<div class="row">
<div class="col-12">
<button class="btn btn-outline-primary btn-sm w-100" onclick="testConnection('cluster1')">
<i class="fas fa-plug"></i> 测试连接
</button>
</div>
</div>
</div>
<!-- 集群2配置 -->
<div class="cluster-config">
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="text-success mb-0"><i class="fas fa-server"></i> 集群2 (测试)</h6>
<button type="button" class="btn btn-outline-success btn-sm" onclick="showImportConfigDialog('cluster2')" title="导入YAML配置">
<i class="fas fa-file-import"></i> 导入配置
</button>
</div>
<div class="row mb-2">
<div class="col-12">
<label class="form-label">集群名称</label>
<input type="text" class="form-control form-control-sm" id="cluster2Name" placeholder="测试集群" value="测试集群">
</div>
</div>
<div class="mb-2">
<label class="form-label">Redis节点</label>
<div id="cluster2Nodes">
<div class="node-input">
<input type="text" class="form-control form-control-sm me-2" placeholder="127.0.0.1" value="127.0.0.1" style="flex: 2;">
<input type="number" class="form-control form-control-sm me-2" placeholder="6380" value="6380" style="flex: 1;">
<button type="button" class="btn btn-outline-danger btn-sm" onclick="removeRedisNode(this, 'cluster2')">
<i class="fas fa-minus"></i>
</button>
</div>
</div>
<button type="button" class="btn btn-outline-primary btn-sm mt-2" onclick="addRedisNode('cluster2')">
<i class="fas fa-plus"></i> 添加节点
</button>
</div>
<div class="row mb-2">
<div class="col-12">
<label class="form-label">密码</label>
<input type="password" class="form-control form-control-sm" id="cluster2Password" placeholder="可选">
</div>
</div>
<div class="row mb-2">
<div class="col-4">
<label class="form-label">连接超时(秒)</label>
<input type="number" class="form-control form-control-sm" id="cluster2SocketTimeout" value="3" min="1" max="60">
</div>
<div class="col-4">
<label class="form-label">建立超时(秒)</label>
<input type="number" class="form-control form-control-sm" id="cluster2SocketConnectTimeout" value="3" min="1" max="60">
</div>
<div class="col-4">
<label class="form-label">最大连接数</label>
<input type="number" class="form-control form-control-sm" id="cluster2MaxConnectionsPerNode" value="16" min="1" max="100">
</div>
</div>
<div class="row">
<div class="col-12">
<button class="btn btn-outline-primary btn-sm w-100" onclick="testConnection('cluster2')">
<i class="fas fa-plug"></i> 测试连接
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 查询面板和结果展示 -->
<div class="col-lg-8">
<div class="config-section">
<h4><i class="fas fa-search"></i> 查询配置</h4>
<!-- 查询模式选择 -->
<div class="card mb-3">
<div class="card-header">
<h6><i class="fas fa-sliders-h"></i> 查询模式</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="form-check">
<input class="form-check-input" type="radio" name="queryMode" id="randomMode" value="random" checked onchange="toggleQueryMode()">
<label class="form-check-label" for="randomMode">
<strong>随机采样模式</strong>
</label>
</div>
<div id="randomOptions" class="mt-2">
<div class="row">
<div class="col-6">
<label class="form-label">采样数量</label>
<input type="number" class="form-control form-control-sm" id="sampleCount" value="100" min="1" max="10000">
</div>
<div class="col-6">
<label class="form-label">Key模式</label>
<input type="text" class="form-control form-control-sm" id="keyPattern" value="*" placeholder="*">
</div>
</div>
<div class="mt-2">
<label class="form-label">源集群</label>
<select class="form-select form-select-sm" id="sourceCluster">
<option value="cluster1">从集群1获取Key</option>
<option value="cluster2" selected>从集群2获取Key</option>
</select>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-check">
<input class="form-check-input" type="radio" name="queryMode" id="specifiedMode" value="specified" onchange="toggleQueryMode()">
<label class="form-check-label" for="specifiedMode">
<strong>指定Key模式</strong>
</label>
</div>
<div id="specifiedOptions" class="mt-2" style="display: none;">
<label class="form-label">Key列表 (每行一个)</label>
<textarea class="form-control query-keys" id="specifiedKeys" rows="6" placeholder="输入要查询的Key每行一个&#10;例如:&#10;user:example1&#10;user:example2&#10;session:abc123"></textarea>
<small class="form-text text-muted">支持大批量Key查询建议单次不超过1000个</small>
</div>
</div>
</div>
</div>
</div>
<!-- 执行按钮 -->
<div class="text-center mb-4">
<button type="button" class="btn btn-redis btn-lg me-3" onclick="executeRedisComparison()">
<i class="fas fa-play me-2"></i>开始Redis数据比较
</button>
<button type="button" class="btn btn-outline-secondary btn-lg" onclick="clearResults()">
<i class="fas fa-eraser me-2"></i>清空结果
</button>
</div>
</div>
<!-- 结果展示区域 -->
<div class="result-section" id="resultSection" 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="missing-tab" data-bs-toggle="tab" data-bs-target="#missing-panel" type="button" role="tab">
<i class="fas fa-question-circle"></i> 缺失数据 <span class="badge bg-warning ms-1" id="missing-count">0</span>
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="raw-data-tab" data-bs-toggle="tab" data-bs-target="#raw-data-panel" type="button" role="tab">
<i class="fas fa-database"></i> 原生数据
</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>
<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-content">
<!-- 差异内容将在这里动态生成 -->
</div>
<div id="differences-pagination" class="d-flex justify-content-center mt-3">
<!-- 分页将在这里动态生成 -->
</div>
</div>
<!-- 相同结果面板 -->
<div class="tab-pane fade" id="identical-panel" role="tabpanel">
<div id="identical-content">
<!-- 相同结果内容将在这里动态生成 -->
</div>
<div id="identical-pagination" class="d-flex justify-content-center mt-3">
<!-- 分页将在这里动态生成 -->
</div>
</div>
<!-- 缺失数据面板 -->
<div class="tab-pane fade" id="missing-panel" role="tabpanel">
<div id="missing-content">
<!-- 缺失数据内容将在这里动态生成 -->
</div>
<div id="missing-pagination" class="d-flex justify-content-center mt-3">
<!-- 分页将在这里动态生成 -->
</div>
</div>
<!-- 原生数据面板 -->
<div class="tab-pane fade" id="raw-data-panel" role="tabpanel">
<div class="row">
<div class="col-md-6">
<h5><i class="fas fa-server text-primary"></i> 集群1 原生数据</h5>
<div class="mb-3">
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-primary active" onclick="switchRawDataView('cluster1', 'formatted')">格式化</button>
<button type="button" class="btn btn-outline-primary" onclick="switchRawDataView('cluster1', 'raw')">原始</button>
<button type="button" class="btn btn-outline-primary" onclick="exportRawData('cluster1')">导出</button>
</div>
</div>
<div id="cluster1-raw-data" class="redis-value" style="max-height: 500px; overflow-y: auto;">
<!-- 集群1原生数据将在这里显示 -->
</div>
</div>
<div class="col-md-6">
<h5><i class="fas fa-server text-success"></i> 集群2 原生数据</h5>
<div class="mb-3">
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-success active" onclick="switchRawDataView('cluster2', 'formatted')">格式化</button>
<button type="button" class="btn btn-outline-success" onclick="switchRawDataView('cluster2', 'raw')">原始</button>
<button type="button" class="btn btn-outline-success" onclick="exportRawData('cluster2')">导出</button>
</div>
</div>
<div id="cluster2-raw-data" class="redis-value" style="max-height: 500px; overflow-y: auto;">
<!-- 集群2原生数据将在这里显示 -->
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-12">
<div class="alert alert-info">
<i class="fas fa-info-circle"></i>
<strong>提示:</strong>原生数据显示实际从Redis查询到的数据。格式化视图便于阅读原始视图保持数据原始格式。
</div>
</div>
</div>
</div>
<!-- 比较总结面板 -->
<div class="tab-pane fade" id="summary-panel" role="tabpanel">
<div id="performanceReport">
<!-- 性能报告将在这里动态生成 -->
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 加载提示 -->
<div class="loading" id="loadingIndicator">
<div class="position-fixed top-50 start-50 translate-middle bg-dark text-white p-4 rounded shadow" style="z-index: 9999;">
<div class="d-flex align-items-center">
<div class="spinner-border spinner-border-sm me-3" role="status"></div>
<span id="loadingText">正在执行Redis数据比较请稍候...</span>
</div>
</div>
</div>
<!-- 配置组保存对话框 -->
<div class="modal fade" id="saveRedisConfigModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">保存Redis配置组</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="redisConfigName" class="form-label">配置组名称</label>
<input type="text" class="form-control" id="redisConfigName" placeholder="输入配置组名称">
</div>
<div class="mb-3">
<label for="redisConfigDescription" class="form-label">描述</label>
<textarea class="form-control" id="redisConfigDescription" rows="3" placeholder="配置组描述(可选)"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-success" onclick="saveRedisConfigGroup()">保存</button>
</div>
</div>
</div>
</div>
<!-- 配置组管理对话框 -->
<div class="modal fade" id="manageRedisConfigModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">管理Redis配置组</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div id="redisConfigGroupList">
<!-- 配置组列表将在这里动态生成 -->
</div>
</div>
</div>
</div>
</div>
<!-- 导入配置模态框 -->
<div class="modal fade" id="importConfigModal" tabindex="-1" aria-labelledby="importConfigModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="importConfigModalLabel">
<i class="fas fa-file-import redis-logo"></i> 导入Redis配置
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<h6 class="mb-2">导入方式</h6>
<div class="btn-group w-100" role="group">
<input type="radio" class="btn-check" name="importMethod" id="importMethodText" checked>
<label class="btn btn-outline-primary" for="importMethodText">
<i class="fas fa-keyboard"></i> 文本粘贴
</label>
<input type="radio" class="btn-check" name="importMethod" id="importMethodFile">
<label class="btn btn-outline-primary" for="importMethodFile">
<i class="fas fa-file-upload"></i> 文件上传
</label>
</div>
</div>
<!-- 文本粘贴方式 -->
<div id="textImportSection">
<div class="mb-3">
<label for="configYamlText" class="form-label">YAML配置内容</label>
<textarea class="form-control" id="configYamlText" rows="8" placeholder="请粘贴YAML格式的配置内容例如&#10;clusterName: &quot;redis-example&quot;&#10;clusterAddress: &quot;127.0.0.1:6379&quot;&#10;clusterPassword: &quot;&quot;"></textarea>
</div>
</div>
<!-- 文件上传方式 -->
<div id="fileImportSection" style="display: none;">
<div class="mb-3">
<label for="configYamlFile" class="form-label">选择YAML配置文件</label>
<input type="file" class="form-control" id="configYamlFile" accept=".yml,.yaml,.txt">
</div>
<div class="border rounded p-3 text-center" id="dropZone" style="border-style: dashed !important; min-height: 100px; cursor: pointer;">
<i class="fas fa-cloud-upload-alt fa-2x text-muted mb-2"></i>
<p class="text-muted mb-0">点击选择文件或拖拽文件到此处</p>
</div>
</div>
<!-- 配置预览 -->
<div id="configPreview" style="display: none;">
<hr>
<h6><i class="fas fa-eye"></i> 配置预览</h6>
<div class="alert alert-info">
<div id="previewContent"></div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary me-2" onclick="previewConfig()">
<i class="fas fa-eye"></i> 预览配置
</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-redis" onclick="importConfig()">
<i class="fas fa-file-import"></i> 导入
</button>
</div>
</div>
</div>
</div>
<!-- Redis查询历史模态框 -->
<div class="modal fade" id="redisQueryHistoryModal" tabindex="-1" aria-labelledby="redisQueryHistoryModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="redisQueryHistoryModalLabel">
<i class="fas fa-history redis-logo"></i> Redis查询历史管理
</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">
<h6 class="mb-0">历史查询记录</h6>
<div>
<button class="btn btn-sm btn-outline-primary me-2" onclick="refreshRedisQueryHistory()">
<i class="fas fa-sync-alt"></i> 刷新
</button>
<button class="btn btn-sm btn-outline-danger" onclick="clearAllRedisHistory()">
<i class="fas fa-trash"></i> 清空全部
</button>
</div>
</div>
<div id="redisHistoryList" style="max-height: 600px; overflow-y: auto;">
<!-- Redis历史记录列表将在这里动态生成 -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<!-- Redis查询日志模态框 -->
<div class="modal fade" id="redisQueryLogsModal" tabindex="-1" aria-labelledby="redisQueryLogsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="redisQueryLogsModalLabel">
<i class="fas fa-file-alt redis-logo"></i> Redis查询日志管理
</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">Redis查询执行日志</h6>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="redis-modal-log-level-info" checked onchange="filterRedisModalLogsByLevel()">
<label class="form-check-label text-primary" for="redis-modal-log-level-info">INFO</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="redis-modal-log-level-warning" checked onchange="filterRedisModalLogsByLevel()">
<label class="form-check-label text-warning" for="redis-modal-log-level-warning">WARNING</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="redis-modal-log-level-error" checked onchange="filterRedisModalLogsByLevel()">
<label class="form-check-label text-danger" for="redis-modal-log-level-error">ERROR</label>
</div>
</div>
<div>
<button class="btn btn-sm btn-outline-primary me-2" onclick="refreshRedisQueryLogs()">
<i class="fas fa-sync-alt"></i> 刷新
</button>
<button class="btn btn-sm btn-outline-danger" onclick="clearRedisQueryLogs()">
<i class="fas fa-trash"></i> 清空
</button>
</div>
</div>
<div id="redis-modal-query-logs" style="max-height: 600px; overflow-y: auto;">
<!-- Redis查询日志将在这里动态生成 -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<!-- 导入配置对话框 -->
<div class="modal fade" id="importRedisConfigModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">导入Redis配置</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="configFormat" class="form-label">配置格式</label>
<select class="form-select" id="configFormat" onchange="updateConfigTemplate()">
<option value="yaml">YAML格式</option>
<option value="json">JSON格式</option>
</select>
</div>
<div class="mb-3">
<label for="configContent" class="form-label">配置内容</label>
<textarea class="form-control" id="configContent" rows="15" placeholder="请粘贴配置内容..."></textarea>
<small class="form-text text-muted">支持YAML和JSON格式的Redis配置</small>
</div>
<div class="mb-3">
<div class="accordion" id="configTemplateAccordion">
<div class="accordion-item">
<h2 class="accordion-header" id="templateHeading">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#templateCollapse">
<i class="fas fa-code me-2"></i>配置示例
</button>
</h2>
<div id="templateCollapse" class="accordion-collapse collapse" data-bs-parent="#configTemplateAccordion">
<div class="accordion-body">
<pre id="configTemplate" class="bg-light p-3 rounded" style="font-size: 0.85em;"></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" onclick="importRedisConfig()">导入配置</button>
</div>
</div>
</div>
</div>
<!-- 保存Redis配置组模态框 -->
<div class="modal fade" id="saveRedisConfigModal" tabindex="-1" aria-labelledby="saveRedisConfigModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="saveRedisConfigModalLabel">
<i class="fas fa-save redis-logo"></i> 保存Redis配置组
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="saveRedisConfigForm">
<div class="mb-3">
<label for="redisConfigName" class="form-label">配置组名称 <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="redisConfigName" required>
</div>
<div class="mb-3">
<label for="redisConfigDescription" class="form-label">配置描述</label>
<textarea class="form-control" id="redisConfigDescription" rows="3" placeholder="请输入配置组的详细描述..."></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-redis" onclick="saveRedisConfigGroup()">
<i class="fas fa-save"></i> 保存
</button>
</div>
</div>
</div>
</div>
<!-- 管理Redis配置组模态框 -->
<div class="modal fade" id="manageRedisConfigModal" tabindex="-1" aria-labelledby="manageRedisConfigModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="manageRedisConfigModalLabel">
<i class="fas fa-cog redis-logo"></i> 管理Redis配置组
</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">
<h6 class="mb-0">已保存的配置组</h6>
<button class="btn btn-sm btn-outline-primary" onclick="refreshRedisConfigGroups()">
<i class="fas fa-sync-alt"></i> 刷新
</button>
</div>
<div id="redisConfigGroupsList" style="max-height: 600px; overflow-y: auto;">
<!-- Redis配置组列表将在这里动态生成 -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<!-- 保存Redis查询历史模态框 -->
<div class="modal fade" id="saveRedisHistoryModal" tabindex="-1" aria-labelledby="saveRedisHistoryModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="saveRedisHistoryModalLabel">
<i class="fas fa-bookmark redis-logo"></i> 保存Redis查询历史
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="saveRedisHistoryForm">
<div class="mb-3">
<label for="redisHistoryName" class="form-label">历史记录名称 <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="redisHistoryName" required>
</div>
<div class="mb-3">
<label for="redisHistoryDescription" class="form-label">历史描述</label>
<textarea class="form-control" id="redisHistoryDescription" rows="3" placeholder="请输入查询历史的详细描述..."></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-redis" onclick="saveRedisQueryHistory()">
<i class="fas fa-bookmark"></i> 保存
</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="/static/js/redis_compare.js"></script>
</body>
</html>