变迁左右滑动、自定义页脚等功能,应用编辑图标BUG修复
This commit is contained in:
parent
ee114dda50
commit
e9a755fbe1
25
README.md
25
README.md
@ -14,29 +14,28 @@
|
||||
### 界面优化
|
||||
1. 每行卡片数量支持自定义(4个、5个、6个、8个)
|
||||
2. 应用的图标支持上传图片自定义 - 已完成
|
||||
3. 首页增加页脚
|
||||
4. 一级分类和二级分类固定宽度,超出宽度可左右滑动查看
|
||||
3. 自定义页脚 - 已完成
|
||||
4. 一级分类和二级分类固定宽度,超出宽度可左右滑动查看 - 已完成
|
||||
5. 气泡的小箭头靠左对齐 - 已完成
|
||||
6. logo图标设置区分明亮和暗黑模式
|
||||
7. 私有应用在首页添加标识
|
||||
8. 首页卡片右键菜单
|
||||
9. 应用支持配置多个URL,左键打开默认URL,右键可选择URL进行复制地址或者打开或者编辑应用
|
||||
10. 新增应用界面便捷增加分类
|
||||
11. 新增应用界面便捷增加图标图片
|
||||
|
||||
### 功能增强
|
||||
8. 应用管理页支持分页和按一级分类筛选
|
||||
9. 应用分页和附件分页功能
|
||||
10. 网站图标自动获取功能
|
||||
11. 书签收藏工具
|
||||
1. 应用管理页支持分页和按一级分类筛选 - 已完成
|
||||
2. 应用分页和附件分页功能 - 已完成
|
||||
3. 网站图标自动获取功能
|
||||
4. 书签收藏工具
|
||||
|
||||
### BUG修复
|
||||
12. 应用编辑页面没有回显带入图片
|
||||
1. 应用编辑页面没有回显带入图片 - 已解决
|
||||
|
||||
### 批量操作
|
||||
13. 应用批量选择功能:
|
||||
1. 应用批量选择功能:
|
||||
- 批量删除
|
||||
- 批量设置私有化/公有化
|
||||
14. 附件批量选择功能:
|
||||
- 批量删除
|
||||
|
||||
15. 新增应用界面便捷增加分类
|
||||
16. 新增应用界面便捷增加图标图片
|
||||
2. 附件批量选择功能:
|
||||
- 批量删除
|
||||
95
app.py
95
app.py
@ -11,6 +11,7 @@ from functools import wraps
|
||||
from werkzeug.utils import secure_filename
|
||||
import requests
|
||||
from urllib.parse import urlparse
|
||||
from math import ceil
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = 'your_secret_key_here' # 请更改为安全的密钥
|
||||
@ -44,6 +45,7 @@ SETTINGS_FILE = os.path.join(DATA_DIR, 'settings.json')
|
||||
|
||||
GUEST_SETTINGS_FILE = os.path.join(DATA_DIR, 'guest_settings.json')
|
||||
|
||||
|
||||
def migrate_settings(settings):
|
||||
"""迁移旧版设置到新版格式"""
|
||||
if 'admin_password' in settings:
|
||||
@ -59,6 +61,12 @@ def migrate_settings(settings):
|
||||
'123456',
|
||||
method='pbkdf2:sha256'
|
||||
)
|
||||
|
||||
# 确保有页脚设置
|
||||
if 'footer_html' not in settings:
|
||||
settings[
|
||||
'footer_html'] = '<div class="flex justify-center text-slate-300" style="margin-top:100px">Powered By <a href="https://github.com" target="_blank" class="ml-[5px]">AIDaohang</a></div>'
|
||||
|
||||
return settings
|
||||
|
||||
|
||||
@ -78,7 +86,8 @@ def init_settings():
|
||||
"logo_icon": "fa-th-list",
|
||||
"logo_image": "",
|
||||
"uploaded_backgrounds": [],
|
||||
"uploaded_logos": []
|
||||
"uploaded_logos": [],
|
||||
"footer_html": '<div class="flex justify-center text-slate-300" style="margin-top:100px">Powered By <a href="https://github.com" target="_blank" class="ml-[5px]">AIDaohang</a></div>'
|
||||
}
|
||||
with open(SETTINGS_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(default_settings, f, ensure_ascii=False, indent=2)
|
||||
@ -90,6 +99,14 @@ def init_settings():
|
||||
if 'admin_password_hash' not in settings:
|
||||
# 需要迁移
|
||||
settings = migrate_settings(settings)
|
||||
# 添加新字段的默认值
|
||||
if 'footer_html' not in settings:
|
||||
settings['footer_html'] = '<div class="flex justify-center text-slate-300" style="margin-top:100px">Powered By <a href="https://github.com" target="_blank" class="ml-[5px]">AIDaohang</a></div>'
|
||||
with open(SETTINGS_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(settings, f, ensure_ascii=False, indent=2)
|
||||
elif 'footer_html' not in settings:
|
||||
# 已有密码哈希但没有页脚设置的情况
|
||||
settings['footer_html'] = '<div class="flex justify-center text-slate-300" style="margin-top:100px">Powered By <a href="https://github.com" target="_blank" class="ml-[5px]">AIDaohang</a></div>'
|
||||
with open(SETTINGS_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(settings, f, ensure_ascii=False, indent=2)
|
||||
|
||||
@ -356,6 +373,7 @@ def system_settings():
|
||||
settings['site_title'] = request.form.get('site_title', '应用导航中心')
|
||||
settings['show_logo'] = 'show_logo' in request.form
|
||||
settings['logo_type'] = request.form.get('logo_type', 'icon')
|
||||
settings['footer_html'] = request.form.get('footer_html', '')
|
||||
|
||||
# 处理logo设置
|
||||
if settings['logo_type'] == 'icon':
|
||||
@ -467,9 +485,38 @@ def system_settings():
|
||||
@app.route('/attachments')
|
||||
@login_required
|
||||
def manage_attachments():
|
||||
type_filter = request.args.get('type', 'all')
|
||||
search_query = request.args.get('search', '').lower()
|
||||
page = int(request.args.get('page', 1))
|
||||
per_page = 10 # 每页显示10条数据
|
||||
|
||||
settings = load_settings()
|
||||
attachments = load_attachments()
|
||||
return render_template('attachments.html', settings=settings, attachments=attachments)
|
||||
|
||||
# 应用筛选
|
||||
filtered_attachments = []
|
||||
for attachment in attachments:
|
||||
# 类型筛选
|
||||
if type_filter != 'all' and attachment['type'] != type_filter:
|
||||
continue
|
||||
|
||||
# 搜索筛选
|
||||
if search_query and search_query not in attachment['filename'].lower():
|
||||
continue
|
||||
|
||||
filtered_attachments.append(attachment)
|
||||
|
||||
# 分页处理
|
||||
total_pages = ceil(len(filtered_attachments) / per_page)
|
||||
paginated_attachments = filtered_attachments[(page - 1) * per_page: page * per_page]
|
||||
|
||||
return render_template('attachments.html',
|
||||
settings=settings,
|
||||
attachments=paginated_attachments,
|
||||
current_page=page,
|
||||
total_pages=total_pages,
|
||||
search_query=search_query,
|
||||
type_filter=type_filter)
|
||||
|
||||
|
||||
@app.route('/upload_attachment', methods=['POST'])
|
||||
@ -573,21 +620,45 @@ def handle_settings():
|
||||
@login_required
|
||||
def index():
|
||||
category_filter = request.args.get('category')
|
||||
search_query = request.args.get('search', '').lower()
|
||||
page = int(request.args.get('page', 1))
|
||||
per_page = 10 # 每页显示10条数据
|
||||
|
||||
apps = load_apps()
|
||||
categories = load_categories()
|
||||
settings = load_settings()
|
||||
|
||||
if category_filter:
|
||||
filtered_apps = []
|
||||
for app in apps:
|
||||
# 检查是否匹配主分类或子分类
|
||||
if (app['category']['main'] == category_filter or
|
||||
app['category']['sub'] == category_filter or
|
||||
(category_filter in categories and app['category']['main'] == category_filter)):
|
||||
filtered_apps.append(app)
|
||||
apps = filtered_apps
|
||||
# 应用筛选
|
||||
filtered_apps = []
|
||||
for app in apps:
|
||||
# 分类筛选
|
||||
if category_filter and not (app['category']['main'] == category_filter or
|
||||
app['category']['sub'] == category_filter or
|
||||
(category_filter in categories and app['category']['main'] == category_filter)):
|
||||
continue
|
||||
|
||||
return render_template('manage.html', apps=apps, settings=settings, categories=categories)
|
||||
# 搜索筛选
|
||||
if search_query and not (search_query in app['title'].lower() or
|
||||
search_query in app['url'].lower() or
|
||||
search_query in app.get('description', '').lower() or
|
||||
search_query in app['category']['main'].lower() or
|
||||
search_query in app['category']['sub'].lower()):
|
||||
continue
|
||||
|
||||
filtered_apps.append(app)
|
||||
|
||||
# 分页处理
|
||||
total_pages = ceil(len(filtered_apps) / per_page)
|
||||
paginated_apps = filtered_apps[(page - 1) * per_page: page * per_page]
|
||||
|
||||
return render_template('manage.html',
|
||||
apps=paginated_apps,
|
||||
settings=settings,
|
||||
categories=categories,
|
||||
current_page=page,
|
||||
total_pages=total_pages,
|
||||
search_query=search_query,
|
||||
category_filter=category_filter)
|
||||
|
||||
@app.route('/')
|
||||
def navigation():
|
||||
|
||||
@ -8,7 +8,8 @@
|
||||
"show_logo": true,
|
||||
"logo_type": "image",
|
||||
"logo_icon": "fa-solid fa-th-list",
|
||||
"logo_image": "/upload/logo/5378dda810964da9a7515ec844628738.png",
|
||||
"logo_image": "/upload/logo/b2c128cf2d4e47daa349c5e7f38c932c.png",
|
||||
"dark_bg_rotate": false,
|
||||
"admin_password_hash": "scrypt:32768:8:1$mPFCfRRzOrcjE6z3$e72ef50a2d3f7292f64bcfc5e21f32c95ea8665414ea8d5f6b216735d68f151166c99fae21132c7949bd92ea32041f969cd4a471adb110a99328089541f7dccb"
|
||||
"admin_password_hash": "scrypt:32768:8:1$mPFCfRRzOrcjE6z3$e72ef50a2d3f7292f64bcfc5e21f32c95ea8665414ea8d5f6b216735d68f151166c99fae21132c7949bd92ea32041f969cd4a471adb110a99328089541f7dccb",
|
||||
"footer_html": "<div class=\"flex flex-col items-center text-slate-400 text-sm py-4\" style=\"margin-top:100px\">\r\n <div class=\"flex items-center\">\r\n <span>© 2023 AIDaohang. All rights reserved.</span>\r\n <span class=\"mx-2\">|</span>\r\n <span>Powered by <a href=\"https://github.com\" target=\"_blank\" class=\"hover:text-slate-200 ml-1\">AIDaohang</a></span>\r\n </div>\r\n</div>"
|
||||
}
|
||||
@ -32,7 +32,25 @@
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<h5 class="border-bottom pb-2 mb-3">附件列表</h5>
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h5 class="border-bottom pb-2 mb-0">附件列表</h5>
|
||||
<form class="d-flex" method="GET">
|
||||
<select name="type" class="form-select me-2" style="width: 120px;">
|
||||
<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">
|
||||
<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>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
@ -83,12 +101,37 @@
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">暂无附件</td>
|
||||
<td colspan="5" class="text-center">没有找到匹配的附件</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 分页导航 -->
|
||||
{% if total_pages > 1 %}
|
||||
<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">
|
||||
<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>
|
||||
</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">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<a href="{{ url_for('index') }}" class="btn btn-secondary">
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
<link rel="stylesheet" href="/static/css/all.min.css">
|
||||
<style>
|
||||
body {
|
||||
padding-top: 20px; /* 修改了这里 */
|
||||
padding-top: 0px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
.navbar {
|
||||
@ -120,9 +120,9 @@
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
<footer class="footer">
|
||||
<p>© 2025 导航管理系统</p>
|
||||
</footer>
|
||||
<footer class="footer">
|
||||
{% include 'footer.html' %}
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="/static/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
@ -17,64 +17,69 @@
|
||||
<label class="form-label">URL</label>
|
||||
<input type="url" name="url" class="form-control" value="{{ app.url }}" required>
|
||||
</div>
|
||||
<!-- 在图标选择部分修改 -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">图标</label>
|
||||
<input type="hidden" name="icon" id="selectedIcon" value="" required>
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="icon-preview me-3">
|
||||
<i id="iconPreview" class="fas fa-question-circle fa-2x"></i>
|
||||
<img id="iconImagePreview" src="" style="display: none; max-width: 32px; max-height: 32px;">
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-primary me-2" data-bs-toggle="modal" data-bs-target="#iconModal">
|
||||
<i class="fas fa-icons me-2"></i>选择图标
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#iconImageModal">
|
||||
<i class="fas fa-image me-2"></i>选择图片
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加图标图片选择模态框 -->
|
||||
<div class="modal fade" id="iconImageModal" tabindex="-1" aria-labelledby="iconImageModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="iconImageModalLabel">选择图标图片</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container-fluid">
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
<input type="text" id="iconImageSearch" class="form-control" placeholder="搜索图标图片...">
|
||||
<!-- 修改后的图标选择部分 -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">图标</label>
|
||||
<input type="hidden" name="icon" id="selectedIcon" value="{{ app.icon }}" required>
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="icon-preview me-3">
|
||||
{% if app.icon.startswith('/upload/icon/') %}
|
||||
<img id="iconImagePreview" src="{{ app.icon }}" style="max-width: 32px; max-height: 32px;">
|
||||
<i id="iconPreview" class="fas fa-question-circle fa-2x" style="display: none;"></i>
|
||||
{% else %}
|
||||
<i id="iconPreview" class="{{ app.icon }} fa-2x"></i>
|
||||
<img id="iconImagePreview" src="" style="display: none; max-width: 32px; max-height: 32px;">
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="row row-cols-3 row-cols-sm-4 row-cols-md-5 row-cols-lg-6 row-cols-xl-8 g-3" id="iconImageGrid">
|
||||
{% for attachment in attachments if attachment.type == 'icon' %}
|
||||
<div class="col">
|
||||
<div class="icon-image-item p-3 text-center rounded" data-filename="{{ attachment.filename }}" title="{{ attachment.filename }}">
|
||||
<img src="{{ url_for('uploaded_icon', filename=attachment.filename) }}" style="max-width: 50px; max-height: 50px;" class="img-thumbnail mb-2">
|
||||
<div class="small text-truncate">{{ attachment.filename }}</div>
|
||||
<button type="button" class="btn btn-outline-primary me-2" data-bs-toggle="modal" data-bs-target="#iconModal">
|
||||
<i class="fas fa-icons me-2"></i>选择图标
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#iconImageModal">
|
||||
<i class="fas fa-image me-2"></i>选择图片
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图标图片选择模态框 -->
|
||||
<div class="modal fade" id="iconImageModal" tabindex="-1" aria-labelledby="iconImageModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="iconImageModalLabel">选择图标图片</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container-fluid">
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
<input type="text" id="iconImageSearch" class="form-control" placeholder="搜索图标图片...">
|
||||
</div>
|
||||
<div class="row row-cols-3 row-cols-sm-4 row-cols-md-5 row-cols-lg-6 row-cols-xl-8 g-3" id="iconImageGrid">
|
||||
{% for attachment in attachments if attachment.type == 'icon' %}
|
||||
<div class="col">
|
||||
<div class="icon-image-item p-3 text-center rounded" data-filename="{{ attachment.filename }}" title="{{ attachment.filename }}">
|
||||
<img src="{{ url_for('uploaded_icon', filename=attachment.filename) }}" style="max-width: 50px; max-height: 50px;" class="img-thumbnail mb-2">
|
||||
<div class="small text-truncate">{{ attachment.filename }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-12 text-center py-3">
|
||||
<p>暂无图标图片</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-12 text-center py-3">
|
||||
<p>暂无图标图片</p>
|
||||
<div class="modal-footer">
|
||||
<div class="me-auto">
|
||||
<span id="selectedIconImageName">未选择图片</span>
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" id="confirmIconImage">确认选择</button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="me-auto">
|
||||
<span id="selectedIconImageName">未选择图片</span>
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" id="confirmIconImage">确认选择</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">主分类</label>
|
||||
<select name="main_category" id="main_category" class="form-select" required>
|
||||
@ -205,7 +210,10 @@
|
||||
document.getElementById('confirmIcon').addEventListener('click', function() {
|
||||
if (selectedIcon) {
|
||||
document.getElementById('selectedIcon').value = selectedIcon;
|
||||
document.getElementById('iconPreview').className = selectedIcon + ' fa-2x';
|
||||
const iconPreview = document.getElementById('iconPreview');
|
||||
iconPreview.className = selectedIcon + ' fa-2x';
|
||||
iconPreview.style.display = 'inline';
|
||||
document.getElementById('iconImagePreview').style.display = 'none';
|
||||
bootstrap.Modal.getInstance(document.getElementById('iconModal')).hide();
|
||||
} else {
|
||||
alert('请先选择一个图标');
|
||||
@ -238,63 +246,61 @@
|
||||
});
|
||||
|
||||
// 初始化时高亮已选图标
|
||||
if (selectedIcon) {
|
||||
if (selectedIcon && !selectedIcon.startsWith('/upload/icon/')) {
|
||||
const selectedItem = document.querySelector(`.icon-item[data-icon="${selectedIcon}"]`);
|
||||
if (selectedItem) {
|
||||
selectedItem.classList.add('selected');
|
||||
}
|
||||
}
|
||||
});
|
||||
// 图标图片选择功能
|
||||
let selectedIconImage = '';
|
||||
let selectedIconImageName = '';
|
||||
|
||||
// 图标图片搜索功能
|
||||
document.getElementById('iconImageSearch').addEventListener('input', function() {
|
||||
const searchTerm = this.value.toLowerCase();
|
||||
const iconImageItems = document.querySelectorAll('.icon-image-item');
|
||||
// 图标图片搜索功能
|
||||
document.getElementById('iconImageSearch').addEventListener('input', function() {
|
||||
const searchTerm = this.value.toLowerCase();
|
||||
const iconImageItems = document.querySelectorAll('.icon-image-item');
|
||||
|
||||
iconImageItems.forEach(item => {
|
||||
const filename = item.dataset.filename.toLowerCase();
|
||||
if (filename.includes(searchTerm)) {
|
||||
item.closest('.col').style.display = 'block';
|
||||
} else {
|
||||
item.closest('.col').style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 图标图片点击选择
|
||||
document.querySelectorAll('.icon-image-item').forEach(item => {
|
||||
item.addEventListener('click', function() {
|
||||
// 移除所有选中状态
|
||||
document.querySelectorAll('.icon-image-item').forEach(i => {
|
||||
i.classList.remove('selected');
|
||||
iconImageItems.forEach(item => {
|
||||
const filename = item.dataset.filename.toLowerCase();
|
||||
if (filename.includes(searchTerm)) {
|
||||
item.closest('.col').style.display = 'block';
|
||||
} else {
|
||||
item.closest('.col').style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 添加选中状态
|
||||
this.classList.add('selected');
|
||||
selectedIconImage = this.dataset.filename;
|
||||
selectedIconImageName = this.getAttribute('title');
|
||||
// 图标图片点击选择
|
||||
document.querySelectorAll('.icon-image-item').forEach(item => {
|
||||
item.addEventListener('click', function() {
|
||||
// 移除所有选中状态
|
||||
document.querySelectorAll('.icon-image-item').forEach(i => {
|
||||
i.classList.remove('selected');
|
||||
});
|
||||
|
||||
// 更新底部显示
|
||||
document.getElementById('selectedIconImageName').textContent = selectedIconImageName;
|
||||
// 添加选中状态
|
||||
this.classList.add('selected');
|
||||
selectedIconImage = this.dataset.filename;
|
||||
selectedIconImageName = this.getAttribute('title');
|
||||
|
||||
// 更新底部显示
|
||||
document.getElementById('selectedIconImageName').textContent = selectedIconImageName;
|
||||
});
|
||||
});
|
||||
|
||||
// 确认选择图标图片
|
||||
document.getElementById('confirmIconImage').addEventListener('click', function() {
|
||||
if (selectedIconImage) {
|
||||
const iconPath = '/upload/icon/' + selectedIconImage;
|
||||
document.getElementById('selectedIcon').value = iconPath;
|
||||
const iconImagePreview = document.getElementById('iconImagePreview');
|
||||
iconImagePreview.src = iconPath;
|
||||
iconImagePreview.style.display = 'inline';
|
||||
document.getElementById('iconPreview').style.display = 'none';
|
||||
bootstrap.Modal.getInstance(document.getElementById('iconImageModal')).hide();
|
||||
} else {
|
||||
alert('请先选择一张图片');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 确认选择图标图片
|
||||
document.getElementById('confirmIconImage').addEventListener('click', function() {
|
||||
if (selectedIconImage) {
|
||||
document.getElementById('selectedIcon').value = '/upload/icon/' + selectedIconImage;
|
||||
document.getElementById('iconPreview').style.display = 'none';
|
||||
const iconImagePreview = document.getElementById('iconImagePreview');
|
||||
iconImagePreview.src = '/upload/icon/' + selectedIconImage;
|
||||
iconImagePreview.style.display = 'inline';
|
||||
bootstrap.Modal.getInstance(document.getElementById('iconImageModal')).hide();
|
||||
} else {
|
||||
alert('请先选择一张图片');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@ -325,12 +331,29 @@ document.getElementById('confirmIconImage').addEventListener('click', function()
|
||||
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
|
||||
}
|
||||
|
||||
#iconGrid {
|
||||
.icon-image-item {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.icon-image-item:hover {
|
||||
background-color: #f8f9fa;
|
||||
border-color: #dee2e6;
|
||||
}
|
||||
|
||||
.icon-image-item.selected {
|
||||
background-color: #e7f1ff;
|
||||
border-color: #86b7fe;
|
||||
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
|
||||
}
|
||||
|
||||
#iconGrid, #iconImageGrid {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#selectedIconName {
|
||||
#selectedIconName, #selectedIconImageName {
|
||||
font-weight: bold;
|
||||
color: #0d6efd;
|
||||
}
|
||||
|
||||
6
templates/footer.html
Normal file
6
templates/footer.html
Normal file
@ -0,0 +1,6 @@
|
||||
<!-- templates/footer.html -->
|
||||
{% if settings and settings.footer_html %}
|
||||
{{ settings.footer_html|safe }}
|
||||
{% else %}
|
||||
<div class="flex justify-center text-slate-300" style="margin-top:100px">Powered By <a href="https://github.com" target="_blank" class="ml-[5px]">AIDaohang</a></div>
|
||||
{% endif %}
|
||||
2298
templates/index.html
2298
templates/index.html
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,15 @@
|
||||
<i class="fas fa-plus"></i> 添加应用
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<form class="d-flex" method="GET">
|
||||
<input type="hidden" name="category" value="{{ category_filter }}">
|
||||
<input type="text" name="search" class="form-control form-control-sm me-2" placeholder="搜索应用..." value="{{ search_query }}">
|
||||
<button type="submit" class="btn btn-sm btn-light">
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
@ -31,6 +40,9 @@
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-primary">筛选</button>
|
||||
{% if category_filter or search_query %}
|
||||
<a href="{{ url_for('index') }}" class="btn btn-secondary ms-2">重置</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@ -86,10 +98,39 @@
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">没有找到匹配的应用</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 分页导航 -->
|
||||
{% if total_pages > 1 %}
|
||||
<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('index', page=current_page-1, category=category_filter, search=search_query) }}" 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('index', page=page_num, category=category_filter, search=search_query) }}">{{ page_num }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
<li class="page-item {% if current_page == total_pages %}disabled{% endif %}">
|
||||
<a class="page-link" href="{{ url_for('index', page=current_page+1, category=category_filter, search=search_query) }}" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -280,6 +280,27 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 页脚设置 -->
|
||||
<div class="card mb-4 border-light shadow-sm">
|
||||
<div class="card-header bg-light">
|
||||
<h5 class="mb-0"><i class="fas fa-code me-2"></i>页脚设置</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="footer_html" class="form-label">自定义页脚HTML代码</label>
|
||||
<textarea class="form-control" id="footer_html" name="footer_html" rows="4">{{ settings.footer_html if settings.footer_html else '' }}</textarea>
|
||||
<div class="form-text">支持HTML代码,可用于添加版权信息、统计代码等</div>
|
||||
</div>
|
||||
|
||||
<div class="preview-area mt-3 p-3 bg-light rounded">
|
||||
<h6>预览效果:</h6>
|
||||
<div id="footer_preview">
|
||||
{{ settings.footer_html|safe if settings.footer_html else '' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 修改密码 -->
|
||||
<div class="card mb-4 border-light shadow-sm">
|
||||
<div class="card-header bg-light">
|
||||
@ -464,6 +485,10 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 页脚预览
|
||||
document.getElementById('footer_html').addEventListener('input', function() {
|
||||
document.getElementById('footer_preview').innerHTML = this.value;
|
||||
});
|
||||
// 全局选择函数
|
||||
function selectLogo(filename) {
|
||||
document.getElementById('selected_logo').value = filename;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user