|
|
{% extends "base.html" %} |
|
|
|
|
|
{% block title %}管理面板 - Wisdom Hub{% endblock %} |
|
|
|
|
|
{% block content %} |
|
|
<div class="dashboard-container" style=" |
|
|
max-width: 1200px; |
|
|
margin: 0 auto; |
|
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.8)); |
|
|
backdrop-filter: blur(10px); |
|
|
border-radius: 24px; |
|
|
box-shadow: 0 8px 32px rgba(99, 145, 197, 0.15); |
|
|
border: 1px solid rgba(99, 145, 197, 0.2); |
|
|
padding: 2rem; |
|
|
"> |
|
|
<div class="dashboard-header" style=" |
|
|
display: flex; |
|
|
justify-content: space-between; |
|
|
align-items: center; |
|
|
margin-bottom: 2rem; |
|
|
padding-bottom: 1.5rem; |
|
|
border-bottom: 2px solid #B3CFEF; |
|
|
"> |
|
|
<div style="display: flex; align-items: center; gap: 1rem;"> |
|
|
<i class="fas fa-chart-line" style=" |
|
|
font-size: 2rem; |
|
|
color: #6391C5; |
|
|
"></i> |
|
|
<h2 style=" |
|
|
font-size: 1.75rem; |
|
|
font-weight: 600; |
|
|
color: #6391C5; |
|
|
margin: 0; |
|
|
">文章管理</h2> |
|
|
</div> |
|
|
|
|
|
<div class="dashboard-actions" style=" |
|
|
display: flex; |
|
|
gap: 1rem; |
|
|
"> |
|
|
<button onclick="location.href='{{ url_for('admin.editor') }}'" style=" |
|
|
padding: 0.75rem 1.5rem; |
|
|
background: linear-gradient(135deg, #6391C5, #C5CDFD); |
|
|
color: white; |
|
|
border: none; |
|
|
border-radius: 12px; |
|
|
font-weight: 500; |
|
|
cursor: pointer; |
|
|
transition: all 0.3s ease; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 0.5rem; |
|
|
"> |
|
|
<i class="fas fa-plus"></i> |
|
|
新建文章 |
|
|
</button> |
|
|
|
|
|
<button onclick="exportData()" style=" |
|
|
padding: 0.75rem 1.5rem; |
|
|
background: rgba(99, 145, 197, 0.1); |
|
|
color: #6391C5; |
|
|
border: 2px solid #B3CFEF; |
|
|
border-radius: 12px; |
|
|
font-weight: 500; |
|
|
cursor: pointer; |
|
|
transition: all 0.3s ease; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 0.5rem; |
|
|
"> |
|
|
<i class="fas fa-download"></i> |
|
|
导出数据 |
|
|
</button> |
|
|
|
|
|
<button onclick="document.getElementById('importInput').click()" style=" |
|
|
padding: 0.75rem 1.5rem; |
|
|
background: rgba(99, 145, 197, 0.1); |
|
|
color: #6391C5; |
|
|
border: 2px solid #B3CFEF; |
|
|
border-radius: 12px; |
|
|
font-weight: 500; |
|
|
cursor: pointer; |
|
|
transition: all 0.3s ease; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 0.5rem; |
|
|
"> |
|
|
<i class="fas fa-upload"></i> |
|
|
导入数据 |
|
|
</button> |
|
|
<input type="file" id="importInput" style="display: none" accept=".db"> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="articles-table" style=" |
|
|
background: white; |
|
|
border-radius: 16px; |
|
|
box-shadow: 0 4px 16px rgba(99, 145, 197, 0.1); |
|
|
overflow: hidden; |
|
|
"> |
|
|
<table style="width: 100%; border-collapse: collapse;"> |
|
|
<thead> |
|
|
<tr style="background: #FEEEDA;"> |
|
|
<th style=" |
|
|
text-align: left; |
|
|
padding: 1rem 1.5rem; |
|
|
color: #6391C5; |
|
|
font-weight: 600; |
|
|
border-bottom: 2px solid #B3CFEF; |
|
|
">标题</th> |
|
|
<th style=" |
|
|
text-align: left; |
|
|
padding: 1rem 1.5rem; |
|
|
color: #6391C5; |
|
|
font-weight: 600; |
|
|
border-bottom: 2px solid #B3CFEF; |
|
|
">创建时间</th> |
|
|
<th style=" |
|
|
text-align: left; |
|
|
padding: 1rem 1.5rem; |
|
|
color: #6391C5; |
|
|
font-weight: 600; |
|
|
border-bottom: 2px solid #B3CFEF; |
|
|
">更新时间</th> |
|
|
<th style=" |
|
|
text-align: left; |
|
|
padding: 1rem 1.5rem; |
|
|
color: #6391C5; |
|
|
font-weight: 600; |
|
|
border-bottom: 2px solid #B3CFEF; |
|
|
">操作</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody> |
|
|
{% for article in articles %} |
|
|
<tr style="transition: all 0.3s ease;"> |
|
|
<td style=" |
|
|
padding: 1rem 1.5rem; |
|
|
border-bottom: 1px solid #B3CFEF; |
|
|
color: #6391C5; |
|
|
">{{ article.title }}</td> |
|
|
<td style=" |
|
|
padding: 1rem 1.5rem; |
|
|
border-bottom: 1px solid #B3CFEF; |
|
|
color: #6391C5; |
|
|
">{{ article.created_at.strftime('%Y-%m-%d') }}</td> |
|
|
<td style=" |
|
|
padding: 1rem 1.5rem; |
|
|
border-bottom: 1px solid #B3CFEF; |
|
|
color: #6391C5; |
|
|
">{{ article.updated_at.strftime('%Y-%m-%d') }}</td> |
|
|
<td style=" |
|
|
padding: 1rem 1.5rem; |
|
|
border-bottom: 1px solid #B3CFEF; |
|
|
"> |
|
|
<div style="display: flex; gap: 0.5rem;"> |
|
|
<a href="{{ url_for('admin.editor', slug=article.slug) }}" style=" |
|
|
padding: 0.5rem 1rem; |
|
|
background: linear-gradient(135deg, #6391C5, #C5CDFD); |
|
|
color: white; |
|
|
border-radius: 8px; |
|
|
text-decoration: none; |
|
|
font-size: 0.875rem; |
|
|
transition: all 0.3s ease; |
|
|
">编辑</a> |
|
|
<button onclick="deleteArticle('{{ article.slug }}')" style=" |
|
|
padding: 0.5rem 1rem; |
|
|
background: rgba(255, 71, 87, 0.1); |
|
|
color: #ff4757; |
|
|
border: 1px solid rgba(255, 71, 87, 0.2); |
|
|
border-radius: 8px; |
|
|
font-size: 0.875rem; |
|
|
cursor: pointer; |
|
|
transition: all 0.3s ease; |
|
|
">删除</button> |
|
|
</div> |
|
|
</td> |
|
|
</tr> |
|
|
{% endfor %} |
|
|
</tbody> |
|
|
</table> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
document.querySelectorAll('tbody tr').forEach(row => { |
|
|
row.addEventListener('mouseenter', () => { |
|
|
row.style.background = 'rgba(99, 145, 197, 0.05)'; |
|
|
}); |
|
|
row.addEventListener('mouseleave', () => { |
|
|
row.style.background = 'white'; |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll('button').forEach(button => { |
|
|
button.addEventListener('mouseenter', () => { |
|
|
if (button.style.background.includes('linear-gradient')) { |
|
|
button.style.transform = 'translateY(-2px)'; |
|
|
button.style.boxShadow = '0 4px 12px rgba(99, 145, 197, 0.2)'; |
|
|
} else if (button.style.background.includes('rgba(255, 71, 87')) { |
|
|
button.style.background = 'rgba(255, 71, 87, 0.2)'; |
|
|
} else { |
|
|
button.style.borderColor = '#6391C5'; |
|
|
button.style.background = 'rgba(99, 145, 197, 0.15)'; |
|
|
} |
|
|
}); |
|
|
|
|
|
button.addEventListener('mouseleave', () => { |
|
|
if (button.style.background.includes('linear-gradient')) { |
|
|
button.style.transform = 'none'; |
|
|
button.style.boxShadow = 'none'; |
|
|
} else if (button.style.background.includes('rgba(255, 71, 87')) { |
|
|
button.style.background = 'rgba(255, 71, 87, 0.1)'; |
|
|
} else { |
|
|
button.style.borderColor = '#B3CFEF'; |
|
|
button.style.background = 'rgba(99, 145, 197, 0.1)'; |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll('a').forEach(link => { |
|
|
if (link.style.background.includes('linear-gradient')) { |
|
|
link.addEventListener('mouseenter', () => { |
|
|
link.style.transform = 'translateY(-2px)'; |
|
|
link.style.boxShadow = '0 4px 12px rgba(99, 145, 197, 0.2)'; |
|
|
}); |
|
|
|
|
|
link.addEventListener('mouseleave', () => { |
|
|
link.style.transform = 'none'; |
|
|
link.style.boxShadow = 'none'; |
|
|
}); |
|
|
} |
|
|
}); |
|
|
|
|
|
async function deleteArticle(slug) { |
|
|
if (!confirm('确定要删除这篇文章吗?')) return; |
|
|
|
|
|
try { |
|
|
const response = await fetch(`/api/articles/${slug}`, { |
|
|
method: 'DELETE' |
|
|
}); |
|
|
|
|
|
if (response.ok) { |
|
|
location.reload(); |
|
|
} |
|
|
} catch (error) { |
|
|
console.error('Error:', error); |
|
|
alert('删除失败,请重试'); |
|
|
} |
|
|
} |
|
|
|
|
|
async function exportData() { |
|
|
try { |
|
|
window.location.href = '/api/export'; |
|
|
} catch (error) { |
|
|
console.error('Error:', error); |
|
|
alert('导出失败,请重试'); |
|
|
} |
|
|
} |
|
|
|
|
|
document.getElementById('importInput').addEventListener('change', async (event) => { |
|
|
const file = event.target.files[0]; |
|
|
if (!file) return; |
|
|
|
|
|
try { |
|
|
const formData = new FormData(); |
|
|
formData.append('file', file); |
|
|
|
|
|
const response = await fetch('/api/import', { |
|
|
method: 'POST', |
|
|
body: formData |
|
|
}); |
|
|
|
|
|
const data = await response.json(); |
|
|
if (response.ok) { |
|
|
alert('导入成功,页面将刷新'); |
|
|
location.reload(); |
|
|
} else { |
|
|
throw new Error(data.error || '导入失败'); |
|
|
} |
|
|
} catch (error) { |
|
|
console.error('Error:', error); |
|
|
alert('导入失败:' + error.message); |
|
|
} |
|
|
}); |
|
|
</script> |
|
|
{% endblock %} |