Files
tech-blog/app/templates/admin/settings.html
hz4th_coder e689fed71e 初始化技术博客项目
功能特性:
- 前端文章展示 (Markdown渲染、代码高亮)
- 后台管理系统 (文章、分类、标签、作者管理)
- 图片上传 (点击、拖拽、粘贴)
- 网站设置 (Logo、底部宣传图片上传)
- 访问统计 (IP记录、趋势图表)
- 参考来源、附件支持
- 单端口部署 (16012)

技术栈:
- Flask 3.0 + SQLAlchemy
- SQLite数据库
- marked.js + highlight.js (Markdown渲染)
- Chart.js (统计图表)
2026-06-04 23:12:34 +08:00

422 lines
15 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.
{% extends 'admin/base.html' %}
{% block title %}网站设置{% endblock %}
{% block page_title %}网站设置{% endblock %}
{% block content %}
<form method="POST" class="settings-form" enctype="multipart/form-data">
<div class="settings-section">
<h3>基本信息</h3>
<div class="form-group">
<label for="site_name">网站名称</label>
<input type="text" id="site_name" name="site_name"
value="{{ config.site_name }}" required>
</div>
<div class="form-group">
<label for="site_subtitle">网站副标题</label>
<input type="text" id="site_subtitle" name="site_subtitle"
value="{{ config.site_subtitle or '' }}"
placeholder="显示在名称旁边的简短描述">
</div>
<div class="form-group">
<label for="site_logo">网站Logo</label>
<div class="image-upload-wrapper">
<input type="url" id="site_logo" name="site_logo"
value="{{ config.site_logo or '' }}"
placeholder="Logo图片URL">
<div class="upload-btn-wrapper">
<button type="button" class="btn btn-sm btn-secondary" onclick="document.getElementById('logo_file').click()">上传图片</button>
<input type="file" id="logo_file" name="logo_file" accept="image/*" style="display:none" onchange="handleUpload(this, 'site_logo')">
</div>
</div>
{% if config.site_logo %}
<div class="image-preview">
<img src="{{ config.site_logo }}" alt="Logo预览" style="max-height: 50px;">
</div>
{% endif %}
</div>
</div>
<div class="settings-section">
<h3>SEO设置</h3>
<div class="form-group">
<label for="site_keywords">网站关键词</label>
<input type="text" id="site_keywords" name="site_keywords"
value="{{ config.site_keywords or '' }}"
placeholder="技术博客,Python,开发,编程">
</div>
<div class="form-group">
<label for="site_description">网站描述</label>
<textarea id="site_description" name="site_description" rows="3"
placeholder="分享技术知识,记录成长历程">{{ config.site_description or '' }}</textarea>
</div>
</div>
<div class="settings-section">
<h3>底部信息</h3>
<div class="form-group">
<label for="footer_text">底部主文字</label>
<input type="text" id="footer_text" name="footer_text"
value="{{ config.footer_text or '' }}"
placeholder="© 2024 技术博客">
</div>
<div class="form-group">
<label for="footer_copyright">版权信息</label>
<input type="text" id="footer_copyright" name="footer_copyright"
value="{{ config.footer_copyright or '' }}"
placeholder="Copyright © 2024 All Rights Reserved">
</div>
<div class="form-group">
<label for="footer_technology">技术支持说明</label>
<input type="text" id="footer_technology" name="footer_technology"
value="{{ config.footer_technology or '' }}"
placeholder="Powered by Flask">
</div>
</div>
<div class="settings-section">
<h3>底部宣传图片</h3>
<small class="form-hint">可在页面底部右侧展示宣传图片、二维码等支持上传或输入URL</small>
<div class="promo-images-grid">
<!-- 宣传图片1 -->
<div class="promo-image-item">
<h4>图片 1</h4>
<div class="form-group">
<label>图片</label>
<div class="image-upload-box" onclick="document.getElementById('promo_image_1_file').click()">
<input type="file" id="promo_image_1_file" name="promo_image_1_file" accept="image/*" onchange="previewUpload(this, 'preview1', 'promo_image_1')">
<input type="hidden" id="promo_image_1" name="promo_image_1" value="{{ config.promo_image_1 or '' }}">
{% if config.promo_image_1 %}
<div id="preview1" class="upload-preview-has-image">
<img src="{{ config.promo_image_1 }}" alt="图片1">
<button type="button" class="remove-btn" onclick="removeImage('preview1', 'promo_image_1')">×</button>
</div>
{% else %}
<div id="preview1" class="upload-preview-empty">
<span>点击上传图片</span>
</div>
{% endif %}
</div>
</div>
<div class="form-group">
<label>标题/说明</label>
<input type="text" name="promo_image_1_title"
value="{{ config.promo_image_1_title or '' }}"
placeholder="扫码关注公众号">
</div>
<div class="form-group">
<label>点击链接</label>
<input type="url" name="promo_image_1_link"
value="{{ config.promo_image_1_link or '' }}"
placeholder="https://example.com可选">
</div>
</div>
<!-- 宣传图片2 -->
<div class="promo-image-item">
<h4>图片 2</h4>
<div class="form-group">
<label>图片</label>
<div class="image-upload-box" onclick="document.getElementById('promo_image_2_file').click()">
<input type="file" id="promo_image_2_file" name="promo_image_2_file" accept="image/*" onchange="previewUpload(this, 'preview2', 'promo_image_2')">
<input type="hidden" id="promo_image_2" name="promo_image_2" value="{{ config.promo_image_2 or '' }}">
{% if config.promo_image_2 %}
<div id="preview2" class="upload-preview-has-image">
<img src="{{ config.promo_image_2 }}" alt="图片2">
<button type="button" class="remove-btn" onclick="removeImage('preview2', 'promo_image_2')">×</button>
</div>
{% else %}
<div id="preview2" class="upload-preview-empty">
<span>点击上传图片</span>
</div>
{% endif %}
</div>
</div>
<div class="form-group">
<label>标题/说明</label>
<input type="text" name="promo_image_2_title"
value="{{ config.promo_image_2_title or '' }}"
placeholder="加入技术社群">
</div>
<div class="form-group">
<label>点击链接</label>
<input type="url" name="promo_image_2_link"
value="{{ config.promo_image_2_link or '' }}"
placeholder="https://example.com可选">
</div>
</div>
<!-- 宣传图片3二维码 -->
<div class="promo-image-item">
<h4>图片 3二维码</h4>
<div class="form-group">
<label>图片</label>
<div class="image-upload-box" onclick="document.getElementById('promo_image_3_file').click()">
<input type="file" id="promo_image_3_file" name="promo_image_3_file" accept="image/*" onchange="previewUpload(this, 'preview3', 'promo_image_3')">
<input type="hidden" id="promo_image_3" name="promo_image_3" value="{{ config.promo_image_3 or '' }}">
{% if config.promo_image_3 %}
<div id="preview3" class="upload-preview-has-image">
<img src="{{ config.promo_image_3 }}" alt="图片3">
<button type="button" class="remove-btn" onclick="removeImage('preview3', 'promo_image_3')">×</button>
</div>
{% else %}
<div id="preview3" class="upload-preview-empty">
<span>点击上传二维码</span>
</div>
{% endif %}
</div>
</div>
<div class="form-group">
<label>标题/说明</label>
<input type="text" name="promo_image_3_title"
value="{{ config.promo_image_3_title or '' }}"
placeholder="微信扫码关注">
</div>
</div>
</div>
</div>
<div class="settings-section">
<h3>联系方式</h3>
<div class="form-row">
<div class="form-group">
<label for="contact_email">联系邮箱</label>
<input type="email" id="contact_email" name="contact_email"
value="{{ config.contact_email or '' }}"
placeholder="contact@example.com">
</div>
<div class="form-group">
<label for="contact_github">GitHub链接</label>
<input type="url" id="contact_github" name="contact_github"
value="{{ config.contact_github or '' }}"
placeholder="https://github.com/yourname">
</div>
</div>
</div>
<div class="settings-section">
<h3>显示设置</h3>
<div class="form-group">
<label for="articles_per_page">首页文章列表每页显示数量</label>
<input type="number" id="articles_per_page" name="articles_per_page"
value="{{ config.articles_per_page }}" min="5" max="50">
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">保存设置</button>
</div>
</form>
<script>
// 图片上传预览
function previewUpload(input, previewId, hiddenId) {
const file = input.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
const preview = document.getElementById(previewId);
preview.className = 'upload-preview-has-image';
preview.innerHTML = `
<img src="${e.target.result}" alt="预览">
<button type="button" class="remove-btn" onclick="removeImage('${previewId}', '${hiddenId}')">×</button>
`;
};
reader.readAsDataURL(file);
}
}
// Logo上传处理异步上传
function handleUpload(input, urlInputId) {
const file = input.files[0];
if (file) {
const formData = new FormData();
formData.append('file', file);
fetch('{{ url_for("admin.upload_image") }}', {
method: 'POST',
body: formData
})
.then(res => res.json())
.then(data => {
if (data.success) {
document.getElementById(urlInputId).value = data.url;
// 显示预览
const preview = input.closest('.form-group').querySelector('.image-preview');
if (preview) {
preview.innerHTML = `<img src="${data.url}" alt="预览" style="max-height: 50px;">`;
} else {
const newPreview = document.createElement('div');
newPreview.className = 'image-preview';
newPreview.innerHTML = `<img src="${data.url}" alt="预览" style="max-height: 50px;">`;
input.closest('.form-group').appendChild(newPreview);
}
}
});
}
}
// 移除图片
function removeImage(previewId, hiddenId) {
const preview = document.getElementById(previewId);
preview.className = 'upload-preview-empty';
const placeholder = previewId === 'preview3' ? '点击上传二维码' : '点击上传图片';
preview.innerHTML = `<span>${placeholder}</span>`;
document.getElementById(hiddenId).value = '';
}
</script>
<style>
.settings-form {
background: var(--admin-card-bg);
border-radius: 8px;
padding: 30px;
box-shadow: var(--admin-shadow);
}
.settings-section {
margin-bottom: 30px;
padding-bottom: 25px;
border-bottom: 1px solid var(--admin-border);
}
.settings-section:last-of-type {
border-bottom: none;
margin-bottom: 20px;
}
.settings-section h3 {
margin-bottom: 20px;
color: var(--admin-primary);
}
.form-hint {
display: block;
margin-bottom: 15px;
color: var(--admin-text-light);
font-size: 0.85rem;
}
.image-upload-wrapper {
display: flex;
gap: 10px;
}
.image-upload-wrapper input[type="url"] {
flex: 1;
}
.promo-images-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.promo-image-item {
background: #f8f9fa;
border-radius: 8px;
padding: 15px;
}
.promo-image-item h4 {
margin-bottom: 15px;
color: var(--admin-text);
font-size: 1rem;
}
.promo-image-item .form-group {
margin-bottom: 12px;
}
.promo-image-item .form-group label {
font-size: 0.9rem;
margin-bottom: 5px;
}
.promo-image-item .form-group input {
font-size: 0.9rem;
padding: 8px 10px;
}
.image-upload-box {
border: 2px dashed var(--admin-border);
border-radius: 8px;
min-height: 120px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
position: relative;
overflow: hidden;
background: #fff;
}
.image-upload-box:hover {
border-color: var(--admin-primary);
}
.image-upload-box input[type="file"] {
display: none;
}
.upload-preview-empty {
color: var(--admin-text-light);
font-size: 0.9rem;
}
.upload-preview-has-image {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.upload-preview-has-image img {
max-width: 100%;
max-height: 120px;
object-fit: contain;
}
.remove-btn {
position: absolute;
top: 5px;
right: 5px;
width: 24px;
height: 24px;
border-radius: 50%;
background: #dc3545;
color: #fff;
border: none;
cursor: pointer;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
}
.image-preview {
margin-top: 10px;
text-align: center;
}
@media (max-width: 992px) {
.promo-images-grid {
grid-template-columns: 1fr;
}
}
</style>
{% endblock %}