首页增加主题样式面板
This commit is contained in:
parent
7c492cb408
commit
f753076ee2
@ -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
113
app.py
@ -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'])
|
||||
|
||||
@ -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">«</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">»</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@ -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>
|
||||
|
||||
1547
templates/index.html
1547
templates/index.html
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user