首页增加主题样式面板

This commit is contained in:
wzj 2025-07-21 19:25:09 +08:00
parent 7c492cb408
commit f753076ee2
5 changed files with 1242 additions and 496 deletions

View File

@ -26,6 +26,7 @@
- [x] 不登录时一级分类和二级分类按钮没有颜色
- [x] 应用编辑页面没有回显带入图片
- [x] 游客通过接口能查看私有应用
- [ ] 游客样式保存到本地
## 待开发功能 ⏳
@ -36,6 +37,8 @@
4. 新增应用界面便捷增加图标图片
5. 每行卡片数量支持自定义4个、5个、6个、8个
6. 首页底线明暗变化
7. 视频背景首次加载卡顿优化,懒载背景白色和灰色
8. 静态页面生成
### 功能增强
1. 网站图标自动获取功能
@ -49,7 +52,8 @@
9. 音乐悬浮窗
10. 视频支持切片
11. 视频播放声音开关
12. 自定义卡片挂件
13. 自定义元素
### 批量操作
1. 应用批量选择功能:
- 批量删除

113
app.py
View File

@ -46,7 +46,43 @@ SETTINGS_FILE = os.path.join(DATA_DIR, 'settings.json')
GUEST_SETTINGS_FILE = os.path.join(DATA_DIR, 'guest_settings.json')
# 主题设置文件路径
THEME_SETTINGS_FILE = os.path.join(DATA_DIR, 'data/theme_settings.json')
def load_theme_settings():
"""加载主题设置"""
default_settings = {
'light': {
'primary_color': '#4361ee',
'title_color': '#4361ee',
'opacity': 0.3,
'bg_color': '#ffffff'
},
'dark': {
'primary_color': '#4361ee',
'title_color': '#4361ee',
'opacity': 0.7,
'bg_color': '#1e1e1e'
}
}
try:
if os.path.exists(THEME_SETTINGS_FILE):
with open(THEME_SETTINGS_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
return default_settings
except Exception as e:
print(f"加载主题设置失败: {e}")
return default_settings
def save_theme_settings(settings):
"""保存主题设置"""
try:
with open(THEME_SETTINGS_FILE, 'w', encoding='utf-8') as f:
json.dump(settings, f, ensure_ascii=False, indent=4)
except Exception as e:
print(f"保存主题设置失败: {e}")
def migrate_settings(settings):
"""迁移旧版设置到新版格式"""
if 'admin_password' in settings:
@ -221,12 +257,21 @@ def save_attachments(data):
with open(ATTACHMENTS_FILE, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
def add_attachment(filename, file_type):
attachments = load_attachments()
file_size = 0
for folder in [LOGO_FOLDER, BACKGROUND_FOLDER, VIDEO_FOLDER, ICON_FOLDER]:
filepath = os.path.join(folder, filename)
if os.path.exists(filepath):
file_size = os.path.getsize(filepath)
break
attachments.append({
'filename': filename,
'type': file_type,
'upload_time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
'upload_time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'size': file_size
})
save_attachments(attachments)
@ -362,11 +407,36 @@ def captcha():
return response
@app.route('/api/theme_settings', methods=['GET', 'POST'])
def handle_theme_settings():
if request.method == 'POST':
if 'username' not in session:
return jsonify({'error': 'Unauthorized'}), 401
data = request.json
# 验证数据格式
required_fields = ['light', 'dark']
for mode in required_fields:
if mode not in data:
return jsonify({'error': f'Missing {mode} settings'}), 400
for field in ['primary_color', 'title_color', 'opacity', 'bg_color']:
if field not in data[mode]:
return jsonify({'error': f'Missing {field} in {mode} settings'}), 400
# 保存设置
save_theme_settings(data)
return jsonify({'status': 'success'})
# GET请求返回当前主题设置
return jsonify(load_theme_settings())
@app.route('/settings', methods=['GET', 'POST'])
@login_required
def system_settings():
settings = load_settings()
guest_settings = load_guest_settings() # 加载游客设置
theme_settings = load_theme_settings()
guest_settings = load_guest_settings()
attachments = load_attachments()
if request.method == 'POST':
@ -460,10 +530,12 @@ def system_settings():
if old_password and new_password and confirm_password:
if not check_password_hash(settings.get('admin_password_hash', ''), old_password):
flash('原密码错误', 'danger')
return render_template('settings.html', settings=settings, icons=load_icons(), attachments=attachments)
return render_template('settings.html', settings=settings, icons=load_icons(),
attachments=attachments)
if new_password != confirm_password:
flash('新密码不匹配', 'danger')
return render_template('settings.html', settings=settings, icons=load_icons(), attachments=attachments)
return render_template('settings.html', settings=settings, icons=load_icons(),
attachments=attachments)
settings['admin_password_hash'] = generate_password_hash(new_password)
save_settings(settings)
@ -473,16 +545,17 @@ def system_settings():
except Exception as e:
print(f"保存设置时出错: {e}")
flash('保存设置时出错', 'danger')
return render_template('settings.html', settings=settings, icons=load_icons(), attachments=attachments)
return render_template('settings.html', settings=settings_to_return, theme_settings=theme_settings, guest_settings=guest_settings, icons=load_icons(), attachments=attachments)
# 从返回的settings中移除密码哈希
settings_to_return = settings.copy()
settings_to_return.pop('admin_password_hash', None)
return render_template('settings.html',
settings=settings_to_return,
guest_settings=guest_settings,
icons=load_icons(),
attachments=attachments)
settings=settings_to_return,
theme_settings=theme_settings,
guest_settings=guest_settings,
icons=load_icons(),
attachments=attachments)
# 添加附件管理路由
@ -491,12 +564,25 @@ def system_settings():
def manage_attachments():
type_filter = request.args.get('type', 'all')
search_query = request.args.get('search', '').lower()
sort_order = request.args.get('sort', 'desc') # 默认按时间倒序
page = int(request.args.get('page', 1))
per_page = 10 # 每页显示10条数据
settings = load_settings()
attachments = load_attachments()
# 添加文件大小信息
for attachment in attachments:
file_found = False
for folder in [LOGO_FOLDER, BACKGROUND_FOLDER, VIDEO_FOLDER, ICON_FOLDER]:
filepath = os.path.join(folder, attachment['filename'])
if os.path.exists(filepath):
attachment['size'] = os.path.getsize(filepath)
file_found = True
break
if not file_found:
attachment['size'] = 0
# 应用筛选
filtered_attachments = []
for attachment in attachments:
@ -510,6 +596,12 @@ def manage_attachments():
filtered_attachments.append(attachment)
# 排序处理
filtered_attachments.sort(
key=lambda x: x['upload_time'],
reverse=(sort_order == 'desc')
)
# 分页处理
total_pages = ceil(len(filtered_attachments) / per_page)
paginated_attachments = filtered_attachments[(page - 1) * per_page: page * per_page]
@ -520,7 +612,8 @@ def manage_attachments():
current_page=page,
total_pages=total_pages,
search_query=search_query,
type_filter=type_filter)
type_filter=type_filter,
sort_order=sort_order)
@app.route('/upload_attachment', methods=['POST'])

View File

@ -23,7 +23,7 @@
<input type="file" class="form-control" id="file" name="file" accept="image/*,video/mp4" required>
</div>
<div class="col-md-2 mb-3 d-flex align-items-end">
<button type="submit" class="btn btn-primary">
<button type="submit" class="btn btn-primary w-100" style="height: 38px; width: 100px">
<i class="fas fa-upload me-2"></i>上传
</button>
</div>
@ -36,20 +36,21 @@
<h5 class="border-bottom pb-2 mb-0">附件列表</h5>
</div>
<div class="d-flex justify-content-between align-items-center mb-3">
<form class="d-flex" method="GET">
<select name="type" class="form-select me-2" style="width: 120px;">
<form class="d-flex align-items-center" method="GET">
<select name="type" class="form-select me-2" style="width: 120px; height: 38px;">
<option value="all" {% if type_filter == 'all' %}selected{% endif %}>所有类型</option>
<option value="logo" {% if type_filter == 'logo' %}selected{% endif %}>Logo</option>
<option value="background" {% if type_filter == 'background' %}selected{% endif %}>背景图片</option>
<option value="video" {% if type_filter == 'video' %}selected{% endif %}>背景视频</option>
<option value="icon" {% if type_filter == 'icon' %}selected{% endif %}>图标图片</option>
</select>
<input type="text" name="search" class="form-control me-2" placeholder="搜索文件名..." value="{{ search_query }}">
<button type="submit" class="btn btn-primary">
<input type="text" name="search" class="form-control me-2" placeholder="搜索文件名..." value="{{ search_query }}" style="height: 38px;">
<input type="hidden" name="sort" value="{{ sort_order }}">
<button type="submit" class="btn btn-primary me-2" style="height: 38px;">
<i class="fas fa-search"></i>
</button>
{% if type_filter != 'all' or search_query %}
<a href="{{ url_for('manage_attachments') }}" class="btn btn-secondary ms-2">重置</a>
<a href="{{ url_for('manage_attachments') }}" class="btn btn-secondary" style="height: 38px; width: 100px">重置</a>
{% endif %}
</form>
</div>
@ -63,7 +64,17 @@
<th>预览</th>
<th>文件名</th>
<th>类型</th>
<th>上传时间</th>
<th>文件大小</th>
<th>
<a href="{{ url_for('manage_attachments', type=type_filter, search=search_query, sort='asc' if sort_order == 'desc' else 'desc') }}" class="text-decoration-none">
上传时间
{% if sort_order == 'desc' %}
<i class="fas fa-sort-up"></i>
{% else %}
<i class="fas fa-sort-down"></i>
{% endif %}
</a>
</th>
<th>操作</th>
</tr>
</thead>
@ -95,6 +106,7 @@
图标图片
{% endif %}
</td>
<td>{{ attachment.size|filesizeformat }}</td>
<td>{{ attachment.upload_time }}</td>
<td>
<form method="POST" action="{{ url_for('delete_attachment_route', filename=attachment.filename) }}" style="display: inline;">
@ -106,7 +118,7 @@
</tr>
{% else %}
<tr>
<td colspan="5" class="text-center">没有找到匹配的附件</td>
<td colspan="6" class="text-center">没有找到匹配的附件</td>
</tr>
{% endfor %}
</tbody>
@ -148,6 +160,7 @@
图标图片
{% endif %}
</p>
<p class="mb-1 text-muted small">{{ attachment.size|filesizeformat }}</p>
<p class="mb-2 text-muted small">{{ attachment.upload_time }}</p>
<form method="POST" action="{{ url_for('delete_attachment_route', filename=attachment.filename) }}" class="mt-2">
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('确定要删除这个附件吗?')">
@ -172,19 +185,19 @@
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center">
<li class="page-item {% if current_page == 1 %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('manage_attachments', page=current_page-1, type=type_filter, search=search_query) }}" aria-label="Previous">
<a class="page-link" href="{{ url_for('manage_attachments', page=current_page-1, type=type_filter, search=search_query, sort=sort_order) }}" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
{% for page_num in range(1, total_pages + 1) %}
<li class="page-item {% if page_num == current_page %}active{% endif %}">
<a class="page-link" href="{{ url_for('manage_attachments', page=page_num, type=type_filter, search=search_query) }}">{{ page_num }}</a>
<a class="page-link" href="{{ url_for('manage_attachments', page=page_num, type=type_filter, search=search_query, sort=sort_order) }}">{{ page_num }}</a>
</li>
{% endfor %}
<li class="page-item {% if current_page == total_pages %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('manage_attachments', page=current_page+1, type=type_filter, search=search_query) }}" aria-label="Next">
<a class="page-link" href="{{ url_for('manage_attachments', page=current_page+1, type=type_filter, search=search_query, sort=sort_order) }}" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>

View File

@ -18,9 +18,9 @@
}
.navbar {
margin-bottom: 20px;
position: sticky; /* 新增 */
top: 0; /* 新增 */
z-index: 1020; /* 新增,确保导航栏在其他内容之上 */
position: sticky;
top: 0;
z-index: 1020;
}
.footer {
margin-top: 40px;
@ -29,7 +29,6 @@
text-align: center;
color: #777;
}
/* 添加选中状态的样式 */
.nav-item .nav-link.active {
font-weight: bold;
color: #0d6efd !important;
@ -55,18 +54,26 @@
<div class="container">
<nav class="navbar navbar-expand-lg navbar-light bg-light rounded">
<div class="container-fluid">
{% if settings and settings.show_logo and settings.logo_type == 'image' and settings.logo_image %}
<a class="navbar-brand" href="{{ url_for('navigation') }}">
<img src="{{ settings.logo_image }}" height="30" class="d-inline-block align-top me-2" alt="Logo">
{{ settings.site_title if settings.site_title else '前往导航' }}
</a>
{% elif settings and settings.show_logo and settings.logo_type == 'icon' and settings.logo_icon %}
<a class="navbar-brand" href="{{ url_for('navigation') }}">
<i class="{{ settings.logo_icon }} me-2"></i>
{{ settings.site_title if settings.site_title else '前往导航' }}
</a>
{% if settings and settings.show_logo %}
{% if settings.logo_type == 'image' and settings.logo_image %}
<a class="navbar-brand" href="{{ url_for('navigation') }}">
<img src="{{ settings.logo_image }}" height="30" class="d-inline-block align-top me-2" alt="Logo">
{{ settings.site_title if settings.site_title else '前往导航' }}
</a>
{% elif settings.logo_type == 'icon' and settings.logo_icon %}
<a class="navbar-brand" href="{{ url_for('navigation') }}">
<i class="{{ settings.logo_icon }} me-2"></i>
{{ settings.site_title if settings.site_title else '前往导航' }}
</a>
{% else %}
<a class="navbar-brand" href="{{ url_for('navigation') }}">
{{ settings.site_title if settings.site_title else '前往导航' }}
</a>
{% endif %}
{% else %}
<a class="navbar-brand" href="{{ url_for('navigation') }}">前往导航</a>
<a class="navbar-brand" href="{{ url_for('navigation') }}">
{{ settings.site_title if settings.site_title else '前往导航' }}
</a>
{% endif %}
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>

File diff suppressed because it is too large Load Diff