优化移动端样式,优化后台逻辑
This commit is contained in:
parent
ef39536ed9
commit
75c691c183
@ -25,7 +25,7 @@
|
||||
## 待开发功能 ⏳
|
||||
### 界面优化
|
||||
1. logo图标设置区分明亮和暗黑模式
|
||||
2. 私有应用在首页添加标识
|
||||
2. 私有应用在首页添加标识 - 已完成
|
||||
3. 首页卡片右键菜单
|
||||
4. 应用支持配置多个URL,左键打开默认URL,右键可选择URL进行复制地址或者打开或者编辑应用
|
||||
5. 新增应用界面便捷增加分类
|
||||
@ -37,10 +37,10 @@
|
||||
2. 书签收藏工具
|
||||
|
||||
### BUG修复
|
||||
1. 游客通过接口能查看私有应用;
|
||||
1. 游客通过接口能查看私有应用 - 已修复
|
||||
|
||||
### 移动端适配
|
||||
1. 后台页面适配
|
||||
1. 后台页面适配 - 已完成
|
||||
|
||||
### 批量操作
|
||||
1. 应用批量选择功能:
|
||||
|
||||
42
app.py
42
app.py
@ -1033,6 +1033,48 @@ def change_password(username, new_password):
|
||||
else:
|
||||
print("错误:只能修改管理员 (admin) 的密码")
|
||||
|
||||
@app.route('/app/add_from_category', methods=['POST'])
|
||||
@login_required
|
||||
def add_app_from_category():
|
||||
categories = load_categories()
|
||||
icons = load_icons()
|
||||
settings = load_settings()
|
||||
attachments = load_attachments()
|
||||
|
||||
if request.method == 'POST':
|
||||
title = request.form['title']
|
||||
url = request.form['url']
|
||||
icon = request.form['icon']
|
||||
description = request.form.get('description', '')
|
||||
private = 'private' in request.form
|
||||
main_category = request.form['main_category']
|
||||
sub_category = request.form['sub_category']
|
||||
|
||||
new_app = {
|
||||
"title": title,
|
||||
"url": url,
|
||||
"icon": icon,
|
||||
"description": description,
|
||||
"private": private,
|
||||
"category": {
|
||||
"main": main_category,
|
||||
"sub": sub_category
|
||||
}
|
||||
}
|
||||
|
||||
apps = load_apps()
|
||||
apps.append(new_app)
|
||||
save_apps(apps)
|
||||
|
||||
flash('应用添加成功', 'success')
|
||||
return redirect(url_for('manage_categories'))
|
||||
|
||||
return render_template('add_app.html',
|
||||
categories=categories,
|
||||
settings=settings,
|
||||
icons=load_icons(),
|
||||
attachments=attachments)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='导航系统管理工具')
|
||||
|
||||
@ -63,5 +63,25 @@
|
||||
"filename": "c77f6bbbb335.png",
|
||||
"type": "icon",
|
||||
"upload_time": "2025-07-06 20:22:28"
|
||||
},
|
||||
{
|
||||
"filename": "hpCMDHrCJVq3.png",
|
||||
"type": "logo",
|
||||
"upload_time": "2025-07-08 21:54:48"
|
||||
},
|
||||
{
|
||||
"filename": "ksDMQ8aEwEph.png",
|
||||
"type": "logo",
|
||||
"upload_time": "2025-07-08 21:55:09"
|
||||
},
|
||||
{
|
||||
"filename": "gfpWMYOvTTuI.png",
|
||||
"type": "logo",
|
||||
"upload_time": "2025-07-08 21:55:20"
|
||||
},
|
||||
{
|
||||
"filename": "G31hd5Kzu6t8.png",
|
||||
"type": "logo",
|
||||
"upload_time": "2025-07-08 21:55:29"
|
||||
}
|
||||
]
|
||||
@ -155,6 +155,11 @@
|
||||
"name": "营养",
|
||||
"color": "#4d908e",
|
||||
"weight": 2
|
||||
},
|
||||
"swim": {
|
||||
"name": "游泳",
|
||||
"color": "#4895ef",
|
||||
"weight": 4
|
||||
}
|
||||
},
|
||||
"weight": 4,
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
{
|
||||
"card_style": "compact",
|
||||
"search_history": [],
|
||||
"theme": "light",
|
||||
"theme": "dark",
|
||||
"bg_image": "/upload/background/5dd4f5d3cd7b.png",
|
||||
"dark_bg_image": "/upload/background/d078c01de3be.png",
|
||||
"site_title": "应用导航中心",
|
||||
"show_logo": true,
|
||||
"logo_type": "image",
|
||||
"logo_icon": "fa-solid fa-th-list",
|
||||
"logo_image": "/upload/logo/f40e2eb965b2.png",
|
||||
"logo_image": "/upload/logo/hpCMDHrCJVq3.png",
|
||||
"dark_bg_rotate": false,
|
||||
"admin_password_hash": "pbkdf2:sha256:260000$ig0vNC2QdipLHgAy$6d734e5112bbdf94bc5cb49a38ca4764376ebb51c877a416d1a8f27d5fb5c415",
|
||||
"footer_html": "<div class=\"flex flex-col items-center text-slate-400 text-sm py-4\" style=\"margin-top:10px\">\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>"
|
||||
"footer_html": "<div class=\"flex flex-col items-center text-slate-400 text-sm py-4\" style=\"margin-top:40px\">\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>"
|
||||
}
|
||||
@ -34,6 +34,8 @@
|
||||
<div class="mb-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<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;">
|
||||
<option value="all" {% if type_filter == 'all' %}selected{% endif %}>所有类型</option>
|
||||
@ -51,61 +53,118 @@
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>预览</th>
|
||||
<th>文件名</th>
|
||||
<th>类型</th>
|
||||
<th>上传时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for attachment in attachments %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if attachment.type == 'logo' %}
|
||||
<img src="{{ url_for('uploaded_logo', filename=attachment.filename) }}" style="max-width: 50px; max-height: 50px;" class="img-thumbnail">
|
||||
{% elif attachment.type == 'background' %}
|
||||
<img src="{{ url_for('uploaded_background', filename=attachment.filename) }}" style="max-width: 50px; max-height: 50px;" class="img-thumbnail">
|
||||
{% elif attachment.type == 'video' %}
|
||||
<video width="80" height="45" muted style="max-width: 50px; max-height: 50px;" class="img-thumbnail">
|
||||
<source src="{{ url_for('uploaded_video', filename=attachment.filename) }}" type="video/mp4">
|
||||
</video>
|
||||
{% else %}
|
||||
<img src="{{ url_for('uploaded_icon', filename=attachment.filename) }}" style="max-width: 50px; max-height: 50px;" class="img-thumbnail">
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ attachment.filename }}</td>
|
||||
<td>
|
||||
{% if attachment.type == 'logo' %}
|
||||
Logo
|
||||
{% elif attachment.type == 'background' %}
|
||||
背景图片
|
||||
{% elif attachment.type == 'video' %}
|
||||
背景视频
|
||||
{% else %}
|
||||
图标图片
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ attachment.upload_time }}</td>
|
||||
<td>
|
||||
<form method="POST" action="{{ url_for('delete_attachment_route', filename=attachment.filename) }}" style="display: inline;">
|
||||
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('确定要删除这个附件吗?')">
|
||||
<i class="fas fa-trash"></i> 删除
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">没有找到匹配的附件</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 桌面端显示表格 -->
|
||||
<div class="d-none d-md-block">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>预览</th>
|
||||
<th>文件名</th>
|
||||
<th>类型</th>
|
||||
<th>上传时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for attachment in attachments %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if attachment.type == 'logo' %}
|
||||
<img src="{{ url_for('uploaded_logo', filename=attachment.filename) }}" style="max-width: 50px; max-height: 50px;" class="img-thumbnail">
|
||||
{% elif attachment.type == 'background' %}
|
||||
<img src="{{ url_for('uploaded_background', filename=attachment.filename) }}" style="max-width: 50px; max-height: 50px;" class="img-thumbnail">
|
||||
{% elif attachment.type == 'video' %}
|
||||
<video width="80" height="45" muted style="max-width: 50px; max-height: 50px;" class="img-thumbnail">
|
||||
<source src="{{ url_for('uploaded_video', filename=attachment.filename) }}" type="video/mp4">
|
||||
</video>
|
||||
{% else %}
|
||||
<img src="{{ url_for('uploaded_icon', filename=attachment.filename) }}" style="max-width: 50px; max-height: 50px;" class="img-thumbnail">
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ attachment.filename }}</td>
|
||||
<td>
|
||||
{% if attachment.type == 'logo' %}
|
||||
Logo
|
||||
{% elif attachment.type == 'background' %}
|
||||
背景图片
|
||||
{% elif attachment.type == 'video' %}
|
||||
背景视频
|
||||
{% else %}
|
||||
图标图片
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ attachment.upload_time }}</td>
|
||||
<td>
|
||||
<form method="POST" action="{{ url_for('delete_attachment_route', filename=attachment.filename) }}" style="display: inline;">
|
||||
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('确定要删除这个附件吗?')">
|
||||
<i class="fas fa-trash"></i> 删除
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">没有找到匹配的附件</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 移动端显示卡片 -->
|
||||
<div class="d-md-none">
|
||||
<div class="row">
|
||||
{% for attachment in attachments %}
|
||||
<div class="col-12 mb-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-4">
|
||||
{% if attachment.type == 'logo' %}
|
||||
<img src="{{ url_for('uploaded_logo', filename=attachment.filename) }}" class="img-fluid rounded">
|
||||
{% elif attachment.type == 'background' %}
|
||||
<img src="{{ url_for('uploaded_background', filename=attachment.filename) }}" class="img-fluid rounded">
|
||||
{% elif attachment.type == 'video' %}
|
||||
<video width="100%" muted class="rounded">
|
||||
<source src="{{ url_for('uploaded_video', filename=attachment.filename) }}" type="video/mp4">
|
||||
</video>
|
||||
{% else %}
|
||||
<img src="{{ url_for('uploaded_icon', filename=attachment.filename) }}" class="img-fluid rounded">
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<h6 class="mb-1">{{ attachment.filename }}</h6>
|
||||
<p class="mb-1 text-muted small">
|
||||
{% if attachment.type == 'logo' %}
|
||||
Logo
|
||||
{% elif attachment.type == 'background' %}
|
||||
背景图片
|
||||
{% elif attachment.type == 'video' %}
|
||||
背景视频
|
||||
{% else %}
|
||||
图标图片
|
||||
{% endif %}
|
||||
</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('确定要删除这个附件吗?')">
|
||||
<i class="fas fa-trash"></i> 删除
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-12">
|
||||
<div class="alert alert-info text-center">没有找到匹配的附件</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分页导航 -->
|
||||
@ -133,11 +192,11 @@
|
||||
</nav>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<a href="{{ url_for('index') }}" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> 返回首页
|
||||
</a>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<a href="{{ url_for('index') }}" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> 返回首页
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -3,99 +3,310 @@
|
||||
{% block title %}分类管理{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- 添加子分类模态框 -->
|
||||
<div class="modal fade" id="addSubCategoryModal" tabindex="-1" aria-labelledby="addSubCategoryModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addSubCategoryModalLabel">添加子分类</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form method="post" action="{{ url_for('add_sub_category') }}" id="subCategoryForm">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="main_id" id="modalMainId">
|
||||
<div class="mb-3">
|
||||
<label for="sub_id" class="form-label">子分类 ID</label>
|
||||
<input type="text" name="sub_id" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="sub_name" class="form-label">子分类名称</label>
|
||||
<input type="text" name="sub_name" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="weight" class="form-label">权重</label>
|
||||
<input type="number" name="weight" class="form-control" value="0">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="color" class="form-label">颜色</label>
|
||||
<input type="color" name="color" class="form-control form-control-color" value="#4895ef">
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="sub_private_modal" name="private">
|
||||
<label class="form-check-label" for="sub_private_modal">私有</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="submit" class="btn btn-primary">添加</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加应用模态框 -->
|
||||
<div class="modal fade" id="addAppModal" tabindex="-1" aria-labelledby="addAppModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addAppModalLabel">添加应用</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form method="post" action="{{ url_for('add_app_from_category') }}">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="main_category" id="modalAppMainCategory">
|
||||
<input type="hidden" name="sub_category" id="modalAppSubCategory">
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">应用名称</label>
|
||||
<input type="text" name="title" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="url" class="form-label">URL</label>
|
||||
<input type="text" name="url" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="icon" class="form-label">图标</label>
|
||||
<select name="icon" class="form-select" required>
|
||||
{% for icon in icons %}
|
||||
<option value="{{ icon }}">{{ icon }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">描述</label>
|
||||
<textarea name="description" class="form-control"></textarea>
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="private" name="private">
|
||||
<label class="form-check-label" for="private">私有</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="submit" class="btn btn-primary">添加</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="mb-4">
|
||||
<h5>添加主分类</h5>
|
||||
<form method="post" action="{{ url_for('add_main_category') }}" class="d-flex align-items-center gap-2">
|
||||
<input type="text" name="id" class="form-control" placeholder="主分类 ID" required style="width: 120px;">
|
||||
<input type="text" name="name" class="form-control" placeholder="主分类名称" required style="width: 150px;">
|
||||
<input type="number" name="weight" class="form-control" placeholder="权重" value="0" style="width: 80px;">
|
||||
<input type="color" name="color" class="form-control form-control-color" value="#4361ee" title="选择颜色" style="width: 50px;">
|
||||
<div class="form-check form-check-inline">
|
||||
<input type="checkbox" class="form-check-input" id="main_private" name="private">
|
||||
<label class="form-check-label" for="main_private">私有</label>
|
||||
<form method="post" action="{{ url_for('add_main_category') }}">
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-md-2">
|
||||
<input type="text" name="id" class="form-control" placeholder="主分类 ID" required>
|
||||
</div>
|
||||
<div class="col-12 col-md-3">
|
||||
<input type="text" name="name" class="form-control" placeholder="主分类名称" required>
|
||||
</div>
|
||||
<div class="col-12 col-md-1">
|
||||
<input type="number" name="weight" class="form-control" placeholder="权重" value="0">
|
||||
</div>
|
||||
<div class="col-12 col-md-1">
|
||||
<input type="color" name="color" class="form-control form-control-color" value="#4361ee" title="选择颜色">
|
||||
</div>
|
||||
<div class="col-12 col-md-2 d-flex align-items-center">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" id="main_private" name="private">
|
||||
<label class="form-check-label" for="main_private">私有</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-1">
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
<i class="fas fa-plus"></i> 添加
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-plus"></i> 添加
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<h5>添加子分类</h5>
|
||||
<form method="post" action="{{ url_for('add_sub_category') }}" class="d-flex align-items-center gap-2">
|
||||
<select name="main_id" class="form-select" required style="width: 150px;">
|
||||
<option value="">选择主分类</option>
|
||||
{% for main_id, cat in categories.items() %}
|
||||
<option value="{{ main_id }}">{{ cat.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="text" name="sub_id" class="form-control" placeholder="子分类 ID" required style="width: 120px;">
|
||||
<input type="text" name="sub_name" class="form-control" placeholder="子分类名称" required style="width: 150px;">
|
||||
<input type="number" name="weight" class="form-control" placeholder="权重" value="0" style="width: 80px;">
|
||||
<input type="color" name="color" class="form-control form-control-color" value="#4895ef" title="选择颜色" style="width: 50px;">
|
||||
<div class="form-check form-check-inline">
|
||||
<input type="checkbox" class="form-check-input" id="sub_private" name="private">
|
||||
<label class="form-check-label" for="sub_private">私有</label>
|
||||
<form method="post" action="{{ url_for('add_sub_category') }}">
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-md-2">
|
||||
<select name="main_id" class="form-select" required>
|
||||
<option value="">选择主分类</option>
|
||||
{% for main_id, cat in categories.items()|sort(attribute='1.weight', reverse=True) %}
|
||||
<option value="{{ main_id }}">{{ cat.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12 col-md-2">
|
||||
<input type="text" name="sub_id" class="form-control" placeholder="子分类 ID" required>
|
||||
</div>
|
||||
<div class="col-12 col-md-3">
|
||||
<input type="text" name="sub_name" class="form-control" placeholder="子分类名称" required>
|
||||
</div>
|
||||
<div class="col-12 col-md-1">
|
||||
<input type="number" name="weight" class="form-control" placeholder="权重" value="0">
|
||||
</div>
|
||||
<div class="col-12 col-md-1">
|
||||
<input type="color" name="color" class="form-control form-control-color" value="#4895ef" title="选择颜色">
|
||||
</div>
|
||||
<div class="col-12 col-md-2 d-flex align-items-center">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" id="sub_private" name="private">
|
||||
<label class="form-check-label" for="sub_private">私有</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-1">
|
||||
<button type="submit" class="btn btn-success w-100">
|
||||
<i class="fas fa-plus"></i> 添加
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="fas fa-plus"></i> 添加
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h5>已存在的分类</h5>
|
||||
<div class="list-group">
|
||||
{% for main_id, cat in categories.items() %}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<strong>{{ cat.name }}</strong>(ID: {{ main_id }})
|
||||
<span class="badge" style="background-color: {{ cat.color }}; color: white;">{{ cat.color }}</span>
|
||||
{% if cat.get('private', False) %}
|
||||
<span class="badge bg-warning text-dark ms-2">私有</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<a href="{{ url_for('edit_main_category', main_id=main_id) }}"
|
||||
class="btn btn-sm btn-primary me-2">
|
||||
<i class="fas fa-edit"></i> 编辑
|
||||
</a>
|
||||
<a href="{{ url_for('delete_main_category', main_id=main_id) }}"
|
||||
class="btn btn-sm btn-danger"
|
||||
onclick="return confirm('确认删除整个主分类?')">
|
||||
<i class="fas fa-trash"></i> 删除
|
||||
</a>
|
||||
<!-- 桌面端显示 -->
|
||||
<div class="d-none d-md-block">
|
||||
<div class="list-group">
|
||||
{% for main_id, cat in categories.items()|sort(attribute='1.weight', reverse=True) %}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap">
|
||||
<div class="mb-2 mb-md-0">
|
||||
<strong>{{ cat.name }}</strong>(ID: {{ main_id }})
|
||||
<span class="badge" style="background-color: {{ cat.color }}; color: white;">{{ cat.get('weight', 0) }}</span>
|
||||
{% if cat.get('private', False) %}
|
||||
<span class="badge bg-warning text-dark ms-2">私有</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-success me-2"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#addSubCategoryModal"
|
||||
onclick="setMainId('{{ main_id }}')">
|
||||
<i class="fas fa-plus"></i> 添加子分类
|
||||
</button>
|
||||
<a href="{{ url_for('edit_main_category', main_id=main_id) }}"
|
||||
class="btn btn-sm btn-primary me-2">
|
||||
<i class="fas fa-edit"></i> 编辑
|
||||
</a>
|
||||
<a href="{{ url_for('delete_main_category', main_id=main_id) }}"
|
||||
class="btn btn-sm btn-danger"
|
||||
onclick="return confirm('确认删除整个主分类?')">
|
||||
<i class="fas fa-trash"></i> 删除
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="mt-2 list-group list-group-flush">
|
||||
{% for sub_id, subData in cat.sub.items()|sort(attribute='1.weight', reverse=True) %}
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center flex-wrap">
|
||||
<span class="mb-2 mb-md-0">
|
||||
{{ subData.name }}(ID: {{ sub_id }})
|
||||
<span class="badge" style="background-color: {{ subData.color }}; color: white;">{{ subData.get('weight', 0) }}</span>
|
||||
{% if cat.get('sub_private', {}).get(sub_id, False) %}
|
||||
<span class="badge bg-warning text-dark ms-2">私有</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-success me-2"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#addAppModal"
|
||||
onclick="setCategory('{{ main_id }}', '{{ sub_id }}')">
|
||||
<i class="fas fa-plus"></i> 添加应用
|
||||
</button>
|
||||
<a href="{{ url_for('edit_sub_category', main_id=main_id, sub_id=sub_id) }}"
|
||||
class="btn btn-sm btn-primary me-2">
|
||||
<i class="fas fa-edit"></i> 编辑
|
||||
</a>
|
||||
<a href="{{ url_for('delete_sub_category', main_id=main_id, sub_id=sub_id) }}"
|
||||
class="btn btn-sm btn-outline-danger"
|
||||
onclick="return confirm('确认删除该子分类?')">
|
||||
<i class="fas fa-trash"></i> 删除
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<ul class="mt-2 list-group list-group-flush">
|
||||
{% for sub_id, subData in cat.sub.items() %}
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span>
|
||||
{{ subData.name }}(ID: {{ sub_id }})
|
||||
<span class="badge" style="background-color: {{ subData.color }}; color: white;">{{ subData.color }}</span>
|
||||
{% if cat.get('sub_private', {}).get(sub_id, False) %}
|
||||
<span class="badge bg-warning text-dark ms-2">私有</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
<div>
|
||||
<a href="{{ url_for('edit_sub_category', main_id=main_id, sub_id=sub_id) }}"
|
||||
class="btn btn-sm btn-primary me-2">
|
||||
<i class="fas fa-edit"></i> 编辑
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 移动端显示 -->
|
||||
<div class="d-md-none">
|
||||
<div class="accordion" id="categoryAccordion">
|
||||
{% for main_id, cat in categories.items()|sort(attribute='1.weight', reverse=True) %}
|
||||
<div class="card mb-3">
|
||||
<div class="card-header d-flex justify-content-between align-items-center" style="background-color: {{ cat.color }}; color: white;">
|
||||
<div>
|
||||
<strong>{{ cat.name }}</strong> (ID: {{ main_id }})
|
||||
<span class="badge bg-light text-dark ms-2">{{ cat.get('weight', 0) }}</span>
|
||||
{% if cat.get('private', False) %}
|
||||
<span class="badge bg-warning text-dark ms-2">私有</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-light me-2" type="button" data-bs-toggle="collapse" data-bs-target="#collapse{{ loop.index }}" aria-expanded="true" aria-controls="collapse{{ loop.index }}">
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="collapse{{ loop.index }}" class="collapse show" data-bs-parent="#categoryAccordion">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<button class="btn btn-sm btn-success flex-grow-1 me-2"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#addSubCategoryModal"
|
||||
onclick="setMainId('{{ main_id }}')">
|
||||
<i class="fas fa-plus"></i> 添加子分类
|
||||
</button>
|
||||
<a href="{{ url_for('edit_main_category', main_id=main_id) }}"
|
||||
class="btn btn-sm btn-primary flex-grow-1 me-2">
|
||||
<i class="fas fa-edit"></i> 编辑主分类
|
||||
</a>
|
||||
<a href="{{ url_for('delete_sub_category', main_id=main_id, sub_id=sub_id) }}"
|
||||
class="btn btn-sm btn-outline-danger"
|
||||
onclick="return confirm('确认删除该子分类?')">
|
||||
<a href="{{ url_for('delete_main_category', main_id=main_id) }}"
|
||||
class="btn btn-sm btn-danger flex-grow-1"
|
||||
onclick="return confirm('确认删除整个主分类?')">
|
||||
<i class="fas fa-trash"></i> 删除
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<h6 class="mb-3">子分类列表</h6>
|
||||
{% for sub_id, subData in cat.sub.items()|sort(attribute='1.weight', reverse=True) %}
|
||||
<div class="card mb-2">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<div>
|
||||
<strong>{{ subData.name }}</strong> (ID: {{ sub_id }})
|
||||
<span class="badge" style="background-color: {{ subData.color }}; color: white;">{{ subData.get('weight', 0) }}</span>
|
||||
{% if cat.get('sub_private', {}).get(sub_id, False) %}
|
||||
<span class="badge bg-warning text-dark ms-2">私有</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<button class="btn btn-sm btn-success flex-grow-1 me-2"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#addAppModal"
|
||||
onclick="setCategory('{{ main_id }}', '{{ sub_id }}')">
|
||||
<i class="fas fa-plus"></i> 添加应用
|
||||
</button>
|
||||
<a href="{{ url_for('edit_sub_category', main_id=main_id, sub_id=sub_id) }}"
|
||||
class="btn btn-sm btn-primary flex-grow-1 me-2">
|
||||
<i class="fas fa-edit"></i> 编辑
|
||||
</a>
|
||||
<a href="{{ url_for('delete_sub_category', main_id=main_id, sub_id=sub_id) }}"
|
||||
class="btn btn-sm btn-outline-danger flex-grow-1"
|
||||
onclick="return confirm('确认删除该子分类?')">
|
||||
<i class="fas fa-trash"></i> 删除
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -106,4 +317,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function setMainId(mainId) {
|
||||
document.getElementById('modalMainId').value = mainId;
|
||||
}
|
||||
|
||||
function setCategory(mainId, subId) {
|
||||
document.getElementById('modalAppMainCategory').value = mainId;
|
||||
document.getElementById('modalAppSubCategory').value = subId;
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
1478
templates/index.html
1478
templates/index.html
File diff suppressed because it is too large
Load Diff
@ -47,64 +47,125 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>图标</th>
|
||||
<th>标题</th>
|
||||
<th>分类/权重</th>
|
||||
<th>URL</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for app in apps %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if app.icon.startswith('/upload/icon/') %}
|
||||
<img src="{{ app.icon }}" style="max-width: 24px; max-height: 24px;" class="img-fluid">
|
||||
{% else %}
|
||||
<i class="fas {{ app.icon }} fa-lg"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ app.title }}
|
||||
{% if app.get('private', False) %}
|
||||
<span class="badge bg-warning text-dark ms-2">私有</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex flex-wrap gap-1">
|
||||
<span class="badge d-flex align-items-center" style="background-color: {{ categories[app.category.main].color }}; color: white;">
|
||||
{{ categories[app.category.main].name }}
|
||||
<span class="badge bg-light text-dark ms-1">{{ categories[app.category.main].weight }}</span>
|
||||
</span>
|
||||
{% if app.category.sub %}
|
||||
<span class="badge d-flex align-items-center" style="background-color: {{ categories[app.category.main].sub[app.category.sub].color }}; color: white;">
|
||||
{{ categories[app.category.main].sub[app.category.sub].name }}
|
||||
<span class="badge bg-light text-dark ms-1">{{ categories[app.category.main].sub[app.category.sub].weight }}</span>
|
||||
</span>
|
||||
<!-- 桌面端显示表格 -->
|
||||
<div class="d-none d-md-block">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>图标</th>
|
||||
<th>标题</th>
|
||||
<th>分类/权重</th>
|
||||
<th>URL</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for app in apps %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if app.icon.startswith('/upload/icon/') %}
|
||||
<img src="{{ app.icon }}" style="max-width: 24px; max-height: 24px;" class="img-fluid">
|
||||
{% else %}
|
||||
<i class="fas {{ app.icon }} fa-lg"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ app.title }}
|
||||
{% if app.get('private', False) %}
|
||||
<span class="badge bg-warning text-dark ms-2">私有</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex flex-wrap gap-1">
|
||||
<span class="badge d-flex align-items-center" style="background-color: {{ categories[app.category.main].color }}; color: white;">
|
||||
{{ categories[app.category.main].name }}
|
||||
<span class="badge bg-light text-dark ms-1">{{ categories[app.category.main].weight }}</span>
|
||||
</span>
|
||||
{% if app.category.sub %}
|
||||
<span class="badge d-flex align-items-center" style="background-color: {{ categories[app.category.main].sub[app.category.sub].color }}; color: white;">
|
||||
{{ categories[app.category.main].sub[app.category.sub].name }}
|
||||
<span class="badge bg-light text-dark ms-1">{{ categories[app.category.main].sub[app.category.sub].weight }}</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td><a href="{{ app.url }}" target="_blank">{{ app.url[:30] }}...</a></td>
|
||||
<td>
|
||||
<a href="{{ url_for('edit_app', index=(current_page-1)*per_page + loop.index0) }}" class="btn btn-sm btn-warning">
|
||||
<i class="fas fa-edit"></i> 编辑
|
||||
</a>
|
||||
<a href="{{ url_for('delete_app', index=(current_page-1)*per_page + loop.index0) }}" class="btn btn-sm btn-danger" onclick="return confirm('确定删除吗?')">
|
||||
<i class="fas fa-trash"></i> 删除
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">没有找到匹配的应用</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 移动端显示卡片 -->
|
||||
<div class="d-md-none">
|
||||
<div class="row">
|
||||
{% for app in apps %}
|
||||
<div class="col-12 mb-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-3">
|
||||
{% if app.icon.startswith('/upload/icon/') %}
|
||||
<img src="{{ app.icon }}" class="img-fluid rounded">
|
||||
{% else %}
|
||||
<i class="fas {{ app.icon }} fa-2x"></i>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<h5 class="card-title mb-1">
|
||||
{{ app.title }}
|
||||
{% if app.get('private', False) %}
|
||||
<span class="badge bg-warning text-dark ms-1">私有</span>
|
||||
{% endif %}
|
||||
</h5>
|
||||
<div class="d-flex flex-wrap gap-1 mb-1">
|
||||
<span class="badge d-flex align-items-center" style="background-color: {{ categories[app.category.main].color }}; color: white;">
|
||||
{{ categories[app.category.main].name }}
|
||||
<span class="badge bg-light text-dark ms-1">{{ categories[app.category.main].weight }}</span>
|
||||
</span>
|
||||
{% if app.category.sub %}
|
||||
<span class="badge d-flex align-items-center" style="background-color: {{ categories[app.category.main].sub[app.category.sub].color }}; color: white;">
|
||||
{{ categories[app.category.main].sub[app.category.sub].name }}
|
||||
<span class="badge bg-light text-dark ms-1">{{ categories[app.category.main].sub[app.category.sub].weight }}</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<p class="card-text mb-2">
|
||||
<a href="{{ app.url }}" target="_blank" class="text-truncate d-block">{{ app.url }}</a>
|
||||
</p>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="{{ url_for('edit_app', index=(current_page-1)*per_page + loop.index0) }}" class="btn btn-sm btn-warning flex-grow-1">
|
||||
<i class="fas fa-edit"></i> 编辑
|
||||
</a>
|
||||
<a href="{{ url_for('delete_app', index=(current_page-1)*per_page + loop.index0) }}" class="btn btn-sm btn-danger flex-grow-1" onclick="return confirm('确定删除吗?')">
|
||||
<i class="fas fa-trash"></i> 删除
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><a href="{{ app.url }}" target="_blank">{{ app.url[:30] }}...</a></td>
|
||||
<td>
|
||||
<a href="{{ url_for('edit_app', index=(current_page-1)*per_page + loop.index0) }}" class="btn btn-sm btn-warning">
|
||||
<i class="fas fa-edit"></i> 编辑
|
||||
</a>
|
||||
<a href="{{ url_for('delete_app', index=loop.index0) }}" class="btn btn-sm btn-danger" onclick="return confirm('确定删除吗?')">
|
||||
<i class="fas fa-trash"></i> 删除
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">没有找到匹配的应用</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-12">
|
||||
<div class="alert alert-info text-center">没有找到匹配的应用</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分页导航 -->
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user