xianyu-auto-reply/static/data_management.html
2025-07-26 14:08:42 +08:00

541 lines
21 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>数据管理 - 闲鱼自动回复系统</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" rel="stylesheet">
<style>
.admin-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 1rem 0;
margin-bottom: 2rem;
}
.table-container {
max-height: 600px;
overflow-y: auto;
border: 1px solid #dee2e6;
border-radius: 0.375rem;
}
.table th {
position: sticky;
top: 0;
background: #f8f9fa;
z-index: 10;
}
.btn-delete {
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
}
.table-info {
background: #e3f2fd;
border-left: 4px solid #2196f3;
padding: 1rem;
margin-bottom: 1rem;
}
.data-stats {
background: linear-gradient(135deg, #4caf50 0%, #45a049 100%);
color: white;
border: none;
}
</style>
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="/">
<i class="bi bi-robot"></i> 闲鱼自动回复系统
</a>
<div class="navbar-nav ms-auto">
<a class="nav-link" href="/">
<i class="bi bi-house"></i> 首页
</a>
<a class="nav-link" href="/user_management.html">
<i class="bi bi-people"></i> 用户管理
</a>
<a class="nav-link active" href="/data_management.html">
<i class="bi bi-database"></i> 数据管理
</a>
<a class="nav-link" href="/log_management.html">
<i class="bi bi-file-text"></i> 日志管理
</a>
<a class="nav-link" href="#" onclick="logout()">
<i class="bi bi-box-arrow-right"></i> 退出
</a>
</div>
</div>
</nav>
<!-- 管理员标题 -->
<div class="admin-header">
<div class="container">
<h1 class="mb-0">
<i class="bi bi-database"></i> 数据管理
</h1>
<p class="mb-0 mt-2">查看和管理数据库中的所有表数据</p>
</div>
</div>
<div class="container">
<!-- 表选择器 -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="bi bi-table"></i> 数据表选择
</h5>
</div>
<div class="card-body">
<div class="row align-items-center">
<div class="col-md-6">
<label class="form-label">选择数据表</label>
<select class="form-select" id="tableSelect" onchange="loadTableData()">
<option value="">请选择数据表...</option>
<option value="users">users - 用户表</option>
<option value="cookies">cookies - Cookie账号表</option>
<option value="keywords">keywords - 关键字表</option>
<option value="default_replies">default_replies - 默认回复表</option>
<option value="ai_reply_settings">ai_reply_settings - AI回复设置表</option>
<option value="message_notifications">message_notifications - 消息通知表</option>
<option value="cards">cards - 卡券表</option>
<option value="delivery_rules">delivery_rules - 发货规则表</option>
<option value="notification_channels">notification_channels - 通知渠道表</option>
<option value="user_settings">user_settings - 用户设置表</option>
<option value="email_verifications">email_verifications - 邮箱验证表</option>
<option value="captcha_codes">captcha_codes - 验证码表</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label">数据统计</label>
<div class="card data-stats">
<div class="card-body text-center py-2">
<h5 id="recordCount" class="mb-0">-</h5>
<small>条记录</small>
</div>
</div>
</div>
<div class="col-md-3">
<label class="form-label">&nbsp;</label>
<div class="d-grid">
<button class="btn btn-primary" onclick="refreshTableData()">
<i class="bi bi-arrow-clockwise"></i> 刷新数据
</button>
</div>
</div>
</div>
</div>
</div>
<!-- 表信息 -->
<div id="tableInfo" class="table-info" style="display: none;">
<h6 class="mb-2">
<i class="bi bi-info-circle"></i> 表信息
</h6>
<div class="row">
<div class="col-md-4">
<strong>表名:</strong> <span id="tableName">-</span>
</div>
<div class="col-md-4">
<strong>中文名:</strong> <span id="tableDescription">-</span>
</div>
<div class="col-md-4">
<strong>记录数:</strong> <span id="tableRecordCount">-</span>
</div>
</div>
</div>
<!-- 数据表格 -->
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="bi bi-table"></i> 数据内容
</h5>
<div class="d-flex gap-2">
<button class="btn btn-sm btn-outline-warning" onclick="confirmDeleteAll()" style="display: none;" id="deleteAllBtn">
<i class="bi bi-trash"></i> 清空表
</button>
</div>
</div>
<div class="card-body p-0">
<div id="loadingData" class="text-center py-4" style="display: none;">
<div class="spinner-border" role="status">
<span class="visually-hidden">加载中...</span>
</div>
<p class="mt-2">正在加载数据...</p>
</div>
<div id="noTableSelected" class="text-center py-4">
<i class="bi bi-table" style="font-size: 3rem; color: #ccc;"></i>
<p class="mt-2 text-muted">请选择要查看的数据表</p>
</div>
<div id="noData" class="text-center py-4" style="display: none;">
<i class="bi bi-inbox" style="font-size: 3rem; color: #ccc;"></i>
<p class="mt-2 text-muted">该表暂无数据</p>
</div>
<div id="tableContainer" class="table-container" style="display: none;">
<table class="table table-striped table-hover mb-0">
<thead id="tableHead"></thead>
<tbody id="tableBody"></tbody>
</table>
</div>
</div>
</div>
</div>
<!-- 删除确认模态框 -->
<div class="modal fade" id="deleteModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="bi bi-exclamation-triangle text-warning"></i> 确认删除
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>您确定要删除这条记录吗?</p>
<div class="alert alert-warning">
<i class="bi bi-exclamation-triangle"></i>
<strong>警告:</strong>此操作不可恢复!
</div>
<div id="deleteRecordInfo" class="bg-light p-2 rounded">
<!-- 记录信息将在这里显示 -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-danger" onclick="confirmDelete()">
<i class="bi bi-trash"></i> 确认删除
</button>
</div>
</div>
</div>
</div>
<!-- 清空表确认模态框 -->
<div class="modal fade" id="deleteAllModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="bi bi-exclamation-triangle text-danger"></i> 确认清空表
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>您确定要清空整个表的所有数据吗?</p>
<div class="alert alert-danger">
<i class="bi bi-exclamation-triangle"></i>
<strong>危险操作:</strong>这将删除表中的所有记录,此操作不可恢复!
</div>
<p>表名: <strong id="deleteAllTableName">-</strong></p>
<p>记录数: <strong id="deleteAllRecordCount">-</strong></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-danger" onclick="confirmDeleteAll()">
<i class="bi bi-trash"></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>
let currentTable = '';
let currentData = [];
let deleteModal = null;
let deleteAllModal = null;
let currentDeleteId = null;
// 表的中文描述
const tableDescriptions = {
'users': '用户表',
'cookies': 'Cookie账号表',
'keywords': '关键字表',
'default_replies': '默认回复表',
'ai_reply_settings': 'AI回复设置表',
'message_notifications': '消息通知表',
'cards': '卡券表',
'delivery_rules': '发货规则表',
'notification_channels': '通知渠道表',
'user_settings': '用户设置表',
'email_verifications': '邮箱验证表',
'captcha_codes': '验证码表'
};
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
// 检查管理员权限
checkAdminPermission();
// 初始化模态框
deleteModal = new bootstrap.Modal(document.getElementById('deleteModal'));
deleteAllModal = new bootstrap.Modal(document.getElementById('deleteAllModal'));
});
// 检查管理员权限
function checkAdminPermission() {
const token = localStorage.getItem('auth_token');
if (!token) {
alert('请先登录');
window.location.href = '/login.html';
return;
}
// 验证token并检查是否为管理员
fetch('/verify', {
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(response => response.json())
.then(data => {
if (!data.authenticated) {
alert('登录已过期,请重新登录');
window.location.href = '/login.html';
return;
}
// 检查是否为管理员
if (data.username !== 'admin') {
alert('此功能仅限管理员使用');
window.location.href = '/';
return;
}
})
.catch(error => {
console.error('权限验证失败:', error);
alert('权限验证失败');
window.location.href = '/login.html';
});
}
// 加载表数据
function loadTableData() {
const tableSelect = document.getElementById('tableSelect');
const selectedTable = tableSelect.value;
if (!selectedTable) {
showNoTableSelected();
return;
}
currentTable = selectedTable;
showLoading();
const token = localStorage.getItem('auth_token');
fetch(`/admin/data/${selectedTable}`, {
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
currentData = data.data;
displayTableData(data.data, data.columns);
updateTableInfo(selectedTable, data.data.length);
} else {
alert('加载数据失败: ' + data.message);
showNoData();
}
})
.catch(error => {
console.error('加载数据失败:', error);
alert('加载数据失败');
showNoData();
});
}
// 刷新表数据
function refreshTableData() {
if (currentTable) {
loadTableData();
}
}
// 显示表数据
function displayTableData(data, columns) {
const tableHead = document.getElementById('tableHead');
const tableBody = document.getElementById('tableBody');
// 清空表格
tableHead.innerHTML = '';
tableBody.innerHTML = '';
if (!data || data.length === 0) {
showNoData();
return;
}
// 创建表头
const headerRow = document.createElement('tr');
columns.forEach(column => {
const th = document.createElement('th');
th.textContent = column;
headerRow.appendChild(th);
});
// 添加操作列
const actionTh = document.createElement('th');
actionTh.textContent = '操作';
actionTh.style.width = '100px';
headerRow.appendChild(actionTh);
tableHead.appendChild(headerRow);
// 创建表格数据
data.forEach((row, index) => {
const tr = document.createElement('tr');
columns.forEach(column => {
const td = document.createElement('td');
let value = row[column];
// 处理特殊值
if (value === null || value === undefined) {
value = '-';
} else if (typeof value === 'string' && value.length > 50) {
value = value.substring(0, 50) + '...';
}
td.textContent = value;
tr.appendChild(td);
});
// 添加操作按钮
const actionTd = document.createElement('td');
const deleteBtn = document.createElement('button');
deleteBtn.className = 'btn btn-danger btn-delete';
deleteBtn.innerHTML = '<i class="bi bi-trash"></i>';
deleteBtn.onclick = () => deleteRecord(row, index);
actionTd.appendChild(deleteBtn);
tr.appendChild(actionTd);
tableBody.appendChild(tr);
});
showTableContainer();
updateRecordCount(data.length);
}
// 更新表信息
function updateTableInfo(tableName, recordCount) {
document.getElementById('tableName').textContent = tableName;
document.getElementById('tableDescription').textContent = tableDescriptions[tableName] || '未知表';
document.getElementById('tableRecordCount').textContent = recordCount;
document.getElementById('tableInfo').style.display = 'block';
// 显示/隐藏清空表按钮
const deleteAllBtn = document.getElementById('deleteAllBtn');
if (recordCount > 0) {
deleteAllBtn.style.display = 'inline-block';
} else {
deleteAllBtn.style.display = 'none';
}
}
// 更新记录数
function updateRecordCount(count) {
document.getElementById('recordCount').textContent = count;
}
// 显示状态函数
function showLoading() {
document.getElementById('loadingData').style.display = 'block';
document.getElementById('noTableSelected').style.display = 'none';
document.getElementById('noData').style.display = 'none';
document.getElementById('tableContainer').style.display = 'none';
document.getElementById('tableInfo').style.display = 'none';
}
function showNoTableSelected() {
document.getElementById('loadingData').style.display = 'none';
document.getElementById('noTableSelected').style.display = 'block';
document.getElementById('noData').style.display = 'none';
document.getElementById('tableContainer').style.display = 'none';
document.getElementById('tableInfo').style.display = 'none';
updateRecordCount('-');
}
function showNoData() {
document.getElementById('loadingData').style.display = 'none';
document.getElementById('noTableSelected').style.display = 'none';
document.getElementById('noData').style.display = 'block';
document.getElementById('tableContainer').style.display = 'none';
}
function showTableContainer() {
document.getElementById('loadingData').style.display = 'none';
document.getElementById('noTableSelected').style.display = 'none';
document.getElementById('noData').style.display = 'none';
document.getElementById('tableContainer').style.display = 'block';
}
// 删除记录
function deleteRecord(record, index) {
currentDeleteId = record.id || record.user_id || index;
// 显示记录信息
const deleteRecordInfo = document.getElementById('deleteRecordInfo');
deleteRecordInfo.innerHTML = '';
Object.keys(record).forEach(key => {
const div = document.createElement('div');
div.innerHTML = `<strong>${key}:</strong> ${record[key] || '-'}`;
deleteRecordInfo.appendChild(div);
});
deleteModal.show();
}
// 确认删除
function confirmDelete() {
if (!currentDeleteId || !currentTable) return;
const token = localStorage.getItem('auth_token');
fetch(`/admin/data/${currentTable}/${currentDeleteId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(response => response.json())
.then(data => {
deleteModal.hide();
if (data.success) {
alert('删除成功');
loadTableData(); // 重新加载数据
} else {
alert('删除失败: ' + data.message);
}
})
.catch(error => {
console.error('删除失败:', error);
alert('删除失败');
});
}
// 确认清空表
function confirmDeleteAll() {
if (!currentTable) return;
document.getElementById('deleteAllTableName').textContent = currentTable;
document.getElementById('deleteAllRecordCount').textContent = currentData.length;
deleteAllModal.show();
}
// 退出登录
function logout() {
localStorage.removeItem('auth_token');
window.location.href = '/login.html';
}
</script>
</body>
</html>