916 lines
28 KiB
HTML
916 lines
28 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>Naga解析系统</title>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
min-height: 100vh;
|
||
padding: 20px;
|
||
}
|
||
|
||
.container {
|
||
max-width: 1400px;
|
||
margin: 0 auto;
|
||
background: white;
|
||
border-radius: 12px;
|
||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.header {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
padding: 30px;
|
||
text-align: center;
|
||
}
|
||
|
||
.header h1 {
|
||
font-size: 28px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.admin-badge {
|
||
display: inline-block;
|
||
background: rgba(255, 255, 255, 0.2);
|
||
padding: 5px 15px;
|
||
border-radius: 20px;
|
||
font-size: 14px;
|
||
margin-top: 10px;
|
||
}
|
||
|
||
.auth-section {
|
||
padding: 20px 30px;
|
||
background: #f8f9fa;
|
||
border-bottom: 1px solid #e0e0e0;
|
||
}
|
||
|
||
.auth-controls {
|
||
display: flex;
|
||
gap: 10px;
|
||
align-items: center;
|
||
}
|
||
|
||
.auth-controls input {
|
||
flex: 1;
|
||
max-width: 300px;
|
||
padding: 10px 15px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.btn {
|
||
padding: 10px 20px;
|
||
border: none;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: #667eea;
|
||
color: white;
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
background: #5568d3;
|
||
}
|
||
|
||
.btn-secondary {
|
||
background: #6c757d;
|
||
color: white;
|
||
}
|
||
|
||
.btn-secondary:hover {
|
||
background: #5a6268;
|
||
}
|
||
|
||
.form-section {
|
||
padding: 30px;
|
||
background: #fafbfc;
|
||
border-bottom: 2px solid #e0e0e0;
|
||
}
|
||
|
||
.form-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||
gap: 20px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.form-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.form-group label {
|
||
font-weight: 600;
|
||
margin-bottom: 8px;
|
||
color: #333;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.form-group input,
|
||
.form-group textarea {
|
||
padding: 10px 15px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
font-family: inherit;
|
||
transition: border-color 0.3s;
|
||
}
|
||
|
||
.form-group input:focus,
|
||
.form-group textarea:focus {
|
||
outline: none;
|
||
border-color: #667eea;
|
||
}
|
||
|
||
.form-group textarea {
|
||
resize: vertical;
|
||
min-height: 80px;
|
||
}
|
||
|
||
.char-count {
|
||
font-size: 12px;
|
||
color: #666;
|
||
margin-top: 5px;
|
||
text-align: right;
|
||
}
|
||
|
||
.char-count.warning {
|
||
color: #e74c3c;
|
||
}
|
||
|
||
.submit-btn {
|
||
width: 100%;
|
||
padding: 12px;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: transform 0.2s;
|
||
}
|
||
|
||
.submit-btn:hover {
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.submit-btn:active {
|
||
transform: translateY(0);
|
||
}
|
||
|
||
.table-section {
|
||
padding: 30px;
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.table-section h2 {
|
||
margin-bottom: 20px;
|
||
color: #333;
|
||
}
|
||
|
||
.table-controls {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
gap: 15px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.sort-control {
|
||
display: flex;
|
||
gap: 10px;
|
||
align-items: center;
|
||
}
|
||
|
||
.sort-btn {
|
||
padding: 8px 15px;
|
||
background: #667eea;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.sort-btn:hover {
|
||
background: #5568d3;
|
||
}
|
||
|
||
.sort-btn.active {
|
||
background: #764ba2;
|
||
}
|
||
|
||
.pagination {
|
||
display: flex;
|
||
gap: 8px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.page-btn {
|
||
padding: 8px 12px;
|
||
background: #f0f0f0;
|
||
border: 1px solid #ddd;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.page-btn:hover {
|
||
background: #e0e0e0;
|
||
}
|
||
|
||
.page-btn.active {
|
||
background: #667eea;
|
||
color: white;
|
||
border-color: #667eea;
|
||
}
|
||
|
||
.page-btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.page-info {
|
||
font-size: 13px;
|
||
color: #666;
|
||
}
|
||
|
||
table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
background: white;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
thead {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
}
|
||
|
||
th {
|
||
padding: 15px 10px;
|
||
text-align: left;
|
||
font-weight: 600;
|
||
font-size: 14px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
td {
|
||
padding: 12px 10px;
|
||
border-bottom: 1px solid #e0e0e0;
|
||
font-size: 14px;
|
||
}
|
||
|
||
tbody tr:hover {
|
||
background: #f8f9fa;
|
||
}
|
||
|
||
.link-cell {
|
||
max-width: 200px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.link-cell a {
|
||
color: #667eea;
|
||
text-decoration: none;
|
||
}
|
||
|
||
.link-cell a:hover {
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.status-yes {
|
||
color: #27ae60;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.status-no {
|
||
color: #e74c3c;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.edit-input {
|
||
width: 100%;
|
||
padding: 5px 8px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 4px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.edit-btn {
|
||
padding: 5px 12px;
|
||
background: #667eea;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
margin-right: 5px;
|
||
}
|
||
|
||
.edit-btn:hover {
|
||
background: #5568d3;
|
||
}
|
||
|
||
.save-btn {
|
||
background: #27ae60;
|
||
}
|
||
|
||
.save-btn:hover {
|
||
background: #229954;
|
||
}
|
||
|
||
.cancel-btn {
|
||
background: #95a5a6;
|
||
}
|
||
|
||
.cancel-btn:hover {
|
||
background: #7f8c8d;
|
||
}
|
||
|
||
.delete-btn {
|
||
background: #e74c3c;
|
||
}
|
||
|
||
.delete-btn:hover {
|
||
background: #c0392b;
|
||
}
|
||
|
||
.empty-state {
|
||
text-align: center;
|
||
padding: 60px 20px;
|
||
color: #999;
|
||
}
|
||
|
||
.empty-state svg {
|
||
width: 80px;
|
||
height: 80px;
|
||
margin-bottom: 20px;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.error-message {
|
||
background: #fee;
|
||
color: #c33;
|
||
padding: 10px 15px;
|
||
border-radius: 6px;
|
||
margin-bottom: 15px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.form-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
table {
|
||
font-size: 12px;
|
||
}
|
||
|
||
th, td {
|
||
padding: 8px 5px;
|
||
}
|
||
|
||
.table-controls {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="header">
|
||
<h1>Naga解析系统</h1>
|
||
<div id="roleDisplay"></div>
|
||
</div>
|
||
|
||
<div class="auth-section">
|
||
<div class="auth-controls">
|
||
<input type="password" id="adminPassword" placeholder="输入管理员密码">
|
||
<button class="btn btn-primary" onclick="login()">登录为管理员</button>
|
||
<button class="btn btn-secondary" onclick="logout()">退出管理员</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-section">
|
||
<h2>提交新数据</h2>
|
||
<div id="errorMessage"></div>
|
||
<form id="dataForm">
|
||
<div class="form-grid">
|
||
<div class="form-group">
|
||
<label for="userName">人名 *</label>
|
||
<input type="text" id="userName" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="recordLink">牌谱链接 *</label>
|
||
<input type="url" id="recordLink" required placeholder="https://tenhou.net/0/" maxlength="200">
|
||
</div>
|
||
<div class="form-group" style="grid-column: 1 / -1;">
|
||
<label for="remarks">备注说明 (选填,少于100字)</label>
|
||
<textarea id="remarks" maxlength="100"></textarea>
|
||
<div class="char-count" id="charCount">0 / 100</div>
|
||
</div>
|
||
</div>
|
||
<button type="submit" class="submit-btn">提交数据</button>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="table-section">
|
||
<h2>数据列表</h2>
|
||
<div class="table-controls">
|
||
<div class="sort-control">
|
||
<span>排序方式:</span>
|
||
<button class="sort-btn active" onclick="setSortOrder('desc')">降序</button>
|
||
<button class="sort-btn" onclick="setSortOrder('asc')">升序</button>
|
||
</div>
|
||
<div class="page-info" id="pageInfo"></div>
|
||
</div>
|
||
<div id="tableContainer"></div>
|
||
<div class="pagination" id="pagination"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// 数据存储
|
||
let dataList = [];
|
||
let isAdmin = false;
|
||
let editingId = null;
|
||
|
||
// 分页和排序
|
||
let currentPage = 1;
|
||
let itemsPerPage = 10;
|
||
let sortOrder = 'desc'; // 'asc' 或 'desc'
|
||
|
||
// 初始化
|
||
async function init() {
|
||
await loadData();
|
||
updateRoleDisplay();
|
||
renderTable();
|
||
setupEventListeners();
|
||
}
|
||
|
||
// 加载数据
|
||
async function loadData() {
|
||
try {
|
||
const response = await fetch('api.php');
|
||
const data = await response.json();
|
||
dataList = data;
|
||
} catch (error) {
|
||
console.error('加载数据失败:', error);
|
||
dataList = [];
|
||
}
|
||
|
||
const adminStatus = localStorage.getItem('isAdmin');
|
||
if (adminStatus === 'true') {
|
||
isAdmin = true;
|
||
}
|
||
}
|
||
|
||
// 保存数据
|
||
async function saveData() {
|
||
try {
|
||
const response = await fetch('api.php', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(dataList)
|
||
});
|
||
const result = await response.json();
|
||
if (!result.success) {
|
||
console.error('保存失败');
|
||
}
|
||
} catch (error) {
|
||
console.error('保存数据失败:', error);
|
||
}
|
||
}
|
||
|
||
// 设置排序顺序
|
||
function setSortOrder(order) {
|
||
sortOrder = order;
|
||
currentPage = 1;
|
||
|
||
// 更新按钮状态
|
||
document.querySelectorAll('.sort-btn').forEach(btn => {
|
||
btn.classList.remove('active');
|
||
});
|
||
event.target.classList.add('active');
|
||
|
||
renderTable();
|
||
}
|
||
|
||
// 获取排序后的数据
|
||
function getSortedData() {
|
||
const sorted = [...dataList];
|
||
if (sortOrder === 'asc') {
|
||
return sorted.reverse();
|
||
}
|
||
return sorted;
|
||
}
|
||
|
||
// 获取当前页数据
|
||
function getCurrentPageData() {
|
||
const sorted = getSortedData();
|
||
const start = (currentPage - 1) * itemsPerPage;
|
||
const end = start + itemsPerPage;
|
||
return sorted.slice(start, end);
|
||
}
|
||
|
||
// 渲染分页控件
|
||
function renderPagination() {
|
||
const totalPages = Math.ceil(dataList.length / itemsPerPage);
|
||
const paginationDiv = document.getElementById('pagination');
|
||
|
||
if (totalPages <= 1) {
|
||
paginationDiv.innerHTML = '';
|
||
return;
|
||
}
|
||
|
||
let html = '';
|
||
|
||
// 上一页按钮
|
||
html += `<button class="page-btn" onclick="changePage(${currentPage - 1})" ${currentPage === 1 ? 'disabled' : ''}>上一页</button>`;
|
||
|
||
// 页码按钮
|
||
for (let i = 1; i <= totalPages; i++) {
|
||
if (i === 1 || i === totalPages || (i >= currentPage - 2 && i <= currentPage + 2)) {
|
||
html += `<button class="page-btn ${i === currentPage ? 'active' : ''}" onclick="changePage(${i})">${i}</button>`;
|
||
} else if (i === currentPage - 3 || i === currentPage + 3) {
|
||
html += `<span>...</span>`;
|
||
}
|
||
}
|
||
|
||
// 下一页按钮
|
||
html += `<button class="page-btn" onclick="changePage(${currentPage + 1})" ${currentPage === totalPages ? 'disabled' : ''}>下一页</button>`;
|
||
|
||
paginationDiv.innerHTML = html;
|
||
|
||
// 更新页面信息
|
||
const start = (currentPage - 1) * itemsPerPage + 1;
|
||
const end = Math.min(currentPage * itemsPerPage, dataList.length);
|
||
document.getElementById('pageInfo').textContent = `显示 ${start}-${end} / 共 ${dataList.length} 条`;
|
||
}
|
||
|
||
// 切换页码
|
||
function changePage(page) {
|
||
const totalPages = Math.ceil(dataList.length / itemsPerPage);
|
||
if (page < 1 || page > totalPages) return;
|
||
currentPage = page;
|
||
renderTable();
|
||
}
|
||
|
||
// 更新角色显示
|
||
function updateRoleDisplay() {
|
||
const display = document.getElementById('roleDisplay');
|
||
if (isAdmin) {
|
||
display.innerHTML = '<span class="admin-badge">✓ 管理员模式</span>';
|
||
} else {
|
||
display.innerHTML = '<span class="admin-badge">访客模式</span>';
|
||
}
|
||
}
|
||
|
||
// 登录
|
||
async function login() {
|
||
const password = document.getElementById('adminPassword').value;
|
||
try {
|
||
const response = await fetch('api.php', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/x-www-form-urlencoded',
|
||
},
|
||
body: `action=login&password=${encodeURIComponent(password)}`
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
isAdmin = true;
|
||
localStorage.setItem('isAdmin', 'true');
|
||
updateRoleDisplay();
|
||
renderTable();
|
||
document.getElementById('adminPassword').value = '';
|
||
showError('登录成功!', 'success');
|
||
} else {
|
||
showError('密码错误!');
|
||
}
|
||
} catch (error) {
|
||
showError('登录失败,请重试!');
|
||
}
|
||
}
|
||
|
||
// 登出
|
||
function logout() {
|
||
isAdmin = false;
|
||
localStorage.removeItem('isAdmin');
|
||
updateRoleDisplay();
|
||
renderTable();
|
||
showError('已退出管理员模式', 'success');
|
||
}
|
||
|
||
// 显示错误信息
|
||
function showError(message, type = 'error') {
|
||
const errorDiv = document.getElementById('errorMessage');
|
||
errorDiv.innerHTML = `<div class="error-message" style="background: ${type === 'success' ? '#d4edda' : '#fee'}; color: ${type === 'success' ? '#155724' : '#c33'};">${message}</div>`;
|
||
setTimeout(() => {
|
||
errorDiv.innerHTML = '';
|
||
}, 3000);
|
||
}
|
||
|
||
// 设置事件监听
|
||
function setupEventListeners() {
|
||
// 表单提交
|
||
document.getElementById('dataForm').addEventListener('submit', handleSubmit);
|
||
|
||
// 字数统计
|
||
const remarks = document.getElementById('remarks');
|
||
remarks.addEventListener('input', function() {
|
||
const count = this.value.length;
|
||
const countDiv = document.getElementById('charCount');
|
||
countDiv.textContent = `${count} / 100`;
|
||
if (count > 100) {
|
||
countDiv.classList.add('warning');
|
||
} else {
|
||
countDiv.classList.remove('warning');
|
||
}
|
||
});
|
||
}
|
||
|
||
// 处理表单提交
|
||
function handleSubmit(e) {
|
||
e.preventDefault();
|
||
|
||
const userName = document.getElementById('userName').value.trim();
|
||
const recordLink = document.getElementById('recordLink').value.trim();
|
||
const remarks = document.getElementById('remarks').value.trim();
|
||
|
||
if (remarks.length > 100) {
|
||
showError('备注说明不能超过100字!');
|
||
return;
|
||
}
|
||
|
||
// URL验证
|
||
try {
|
||
new URL(recordLink);
|
||
} catch {
|
||
showError('请输入有效的URL链接!');
|
||
return;
|
||
}
|
||
|
||
// 创建新数据
|
||
const newData = {
|
||
id: Date.now(),
|
||
submitTime: formatDate(new Date()),
|
||
userName,
|
||
recordLink,
|
||
remarks,
|
||
nagaLink: '',
|
||
nagaRemarks: ''
|
||
};
|
||
|
||
dataList.unshift(newData);
|
||
saveData();
|
||
currentPage = 1; // 回到第一页
|
||
renderTable();
|
||
|
||
// 重置表单
|
||
document.getElementById('dataForm').reset();
|
||
document.getElementById('charCount').textContent = '0 / 100';
|
||
|
||
showError('提交成功!', 'success');
|
||
}
|
||
|
||
// 格式化日期
|
||
function formatDate(date) {
|
||
const year = date.getFullYear();
|
||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
const day = String(date.getDate()).padStart(2, '0');
|
||
const hours = String(date.getHours()).padStart(2, '0');
|
||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||
}
|
||
|
||
// 渲染表格
|
||
function renderTable() {
|
||
const container = document.getElementById('tableContainer');
|
||
|
||
if (dataList.length === 0) {
|
||
container.innerHTML = `
|
||
<div class="empty-state">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||
<path d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
<p>暂无数据,请提交第一条记录</p>
|
||
</div>
|
||
`;
|
||
document.getElementById('pagination').innerHTML = '';
|
||
document.getElementById('pageInfo').textContent = '';
|
||
return;
|
||
}
|
||
|
||
const pageData = getCurrentPageData();
|
||
const sortedData = getSortedData();
|
||
const startIndex = (currentPage - 1) * itemsPerPage;
|
||
|
||
let html = '<table><thead><tr>';
|
||
html += '<th>序号</th>';
|
||
html += '<th>提交时间</th>';
|
||
html += '<th>人名</th>';
|
||
html += '<th>牌谱链接</th>';
|
||
html += '<th>备注说明</th>';
|
||
html += '<th>是否有Naga链接</th>';
|
||
html += '<th>Naga链接</th>';
|
||
html += '<th>Naga说明</th>';
|
||
if (isAdmin) {
|
||
html += '<th>操作</th>';
|
||
}
|
||
html += '</tr></thead><tbody>';
|
||
|
||
pageData.forEach((item, index) => {
|
||
const isEditing = editingId === item.id;
|
||
const hasNaga = item.nagaLink && item.nagaLink.trim() !== '';
|
||
const displayIndex = startIndex + index + 1;
|
||
|
||
html += '<tr>';
|
||
html += `<td>${displayIndex}</td>`;
|
||
html += `<td>${item.submitTime}</td>`;
|
||
|
||
// 管理员可编辑人名
|
||
if (isEditing && isAdmin) {
|
||
html += `<td><input type="text" class="edit-input" id="editUserName_${item.id}" value="${escapeHtml(item.userName)}"></td>`;
|
||
} else {
|
||
html += `<td>${escapeHtml(item.userName)}</td>`;
|
||
}
|
||
|
||
// 管理员可编辑牌谱链接
|
||
if (isEditing && isAdmin) {
|
||
html += `<td><input type="url" class="edit-input" id="editRecordLink_${item.id}" value="${escapeHtml(item.recordLink)}"></td>`;
|
||
} else {
|
||
html += `<td class="link-cell"><a href="${escapeHtml(item.recordLink)}" target="_blank">${escapeHtml(item.recordLink)}</a></td>`;
|
||
}
|
||
|
||
// 管理员可编辑备注
|
||
if (isEditing && isAdmin) {
|
||
html += `<td><input type="text" class="edit-input" id="editRemarks_${item.id}" value="${escapeHtml(item.remarks)}" maxlength="100"></td>`;
|
||
} else {
|
||
html += `<td>${escapeHtml(item.remarks)}</td>`;
|
||
}
|
||
|
||
html += `<td class="${hasNaga ? 'status-yes' : 'status-no'}">${hasNaga ? '✔ 有' : '✘ 无'}</td>`;
|
||
|
||
if (isEditing && isAdmin) {
|
||
html += `<td><input type="url" class="edit-input" id="editNagaLink_${item.id}" value="${escapeHtml(item.nagaLink)}"></td>`;
|
||
html += `<td><input type="text" class="edit-input" id="editNagaRemarks_${item.id}" value="${escapeHtml(item.nagaRemarks)}" maxlength="100"></td>`;
|
||
} else {
|
||
html += `<td class="link-cell">${item.nagaLink ? `<a href="${escapeHtml(item.nagaLink)}" target="_blank">${escapeHtml(item.nagaLink)}</a>` : '-'}</td>`;
|
||
html += `<td>${item.nagaRemarks ? escapeHtml(item.nagaRemarks) : '-'}</td>`;
|
||
}
|
||
|
||
if (isAdmin) {
|
||
if (isEditing) {
|
||
html += `<td>
|
||
<button class="edit-btn save-btn" onclick="saveEdit(${item.id})">保存</button>
|
||
<button class="edit-btn cancel-btn" onclick="cancelEdit()">取消</button>
|
||
</td>`;
|
||
} else {
|
||
html += `<td>
|
||
<button class="edit-btn" onclick="startEdit(${item.id})">编辑</button>
|
||
<button class="edit-btn delete-btn" onclick="deleteRecord(${item.id})">删除</button>
|
||
</td>`;
|
||
}
|
||
}
|
||
|
||
html += '</tr>';
|
||
});
|
||
|
||
html += '</tbody></table>';
|
||
container.innerHTML = html;
|
||
renderPagination();
|
||
}
|
||
|
||
// 开始编辑
|
||
function startEdit(id) {
|
||
editingId = id;
|
||
renderTable();
|
||
}
|
||
|
||
// 保存编辑
|
||
function saveEdit(id) {
|
||
const userName = document.getElementById(`editUserName_${id}`).value.trim();
|
||
const recordLink = document.getElementById(`editRecordLink_${id}`).value.trim();
|
||
const remarks = document.getElementById(`editRemarks_${id}`).value.trim();
|
||
const nagaLink = document.getElementById(`editNagaLink_${id}`).value.trim();
|
||
const nagaRemarks = document.getElementById(`editNagaRemarks_${id}`).value.trim();
|
||
|
||
// 验证人名
|
||
if (!userName) {
|
||
showError('人名不能为空!');
|
||
return;
|
||
}
|
||
|
||
// URL验证 - 牌谱链接
|
||
try {
|
||
new URL(recordLink);
|
||
} catch {
|
||
showError('请输入有效的牌谱链接URL!');
|
||
return;
|
||
}
|
||
|
||
// URL验证 - Naga链接
|
||
if (nagaLink && nagaLink !== '') {
|
||
try {
|
||
new URL(nagaLink);
|
||
} catch {
|
||
showError('请输入有效的Naga链接URL!');
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 字数验证
|
||
if (remarks.length > 100) {
|
||
showError('备注说明不能超过100字!');
|
||
return;
|
||
}
|
||
|
||
if (nagaRemarks.length > 100) {
|
||
showError('Naga说明不能超过100字!');
|
||
return;
|
||
}
|
||
|
||
const item = dataList.find(d => d.id === id);
|
||
if (item) {
|
||
item.userName = userName;
|
||
item.recordLink = recordLink;
|
||
item.remarks = remarks;
|
||
item.nagaLink = nagaLink;
|
||
item.nagaRemarks = nagaRemarks;
|
||
saveData();
|
||
editingId = null;
|
||
renderTable();
|
||
showError('更新成功!', 'success');
|
||
}
|
||
}
|
||
|
||
// 取消编辑
|
||
function cancelEdit() {
|
||
editingId = null;
|
||
renderTable();
|
||
}
|
||
|
||
// 删除记录
|
||
function deleteRecord(id) {
|
||
if (confirm('确定要删除这条记录吗?此操作不可恢复!')) {
|
||
const index = dataList.findIndex(d => d.id === id);
|
||
if (index !== -1) {
|
||
dataList.splice(index, 1);
|
||
saveData();
|
||
|
||
// 如果当前页没有数据了,回到上一页
|
||
const totalPages = Math.ceil(dataList.length / itemsPerPage);
|
||
if (currentPage > totalPages && currentPage > 1) {
|
||
currentPage = totalPages;
|
||
}
|
||
|
||
renderTable();
|
||
showError('删除成功!', 'success');
|
||
}
|
||
}
|
||
}
|
||
|
||
// HTML转义
|
||
function escapeHtml(text) {
|
||
const div = document.createElement('div');
|
||
div.textContent = text;
|
||
return div.innerHTML;
|
||
}
|
||
|
||
// 页面加载时初始化
|
||
init();
|
||
</script>
|
||
</body>
|
||
</html>
|