upgrade_book_manage

This commit is contained in:
superlishunqin 2025-05-17 16:43:45 +08:00
parent 868cc88d54
commit d86151bf6e
3 changed files with 362 additions and 100 deletions

View File

@ -21,6 +21,13 @@
z-index: 0;
}
.category-filter-dropdown,
.sort-dropdown,
.order-dropdown {
z-index: 9999;
}
@keyframes bubble {
0% {
transform: translateY(100%) scale(0);
@ -181,30 +188,188 @@
max-width: 800px;
}
/* 现代化下拉列表样式 */
.filter-section .custom-select-container {
position: relative;
width: 100%;
}
.filter-section .filter-header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #f8f9fa;
border-radius: 12px;
padding: 12px 16px;
cursor: pointer;
user-select: none;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
border: 2px solid #f9c0d0;
transition: all 0.3s ease;
background-color: rgba(255, 255, 255, 0.95);
color: #666;
font-weight: 500;
}
.filter-section .filter-header:hover {
border-color: #e67e9f;
box-shadow: 0 0 0 3px rgba(230, 126, 159, 0.1);
background-color: #fff;
transform: translateY(-2px);
}
.filter-section .filter-header .filter-title {
display: flex;
align-items: center;
gap: 10px;
}
.filter-section .filter-header .filter-title i {
color: #e67e9f;
font-size: 16px;
}
.filter-section .filter-header .icon-arrow {
transition: transform 0.3s ease;
}
.filter-section .filter-header.open .icon-arrow {
transform: rotate(180deg);
}
.filter-section .dropdown-options {
position: absolute;
top: calc(100% + 8px);
left: 0;
right: 0;
max-height: 300px;
overflow-y: auto;
background-color: white;
border-radius: 12px;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1);
z-index: 9999;
padding: 8px 0;
display: none;
animation: dropdownFadeIn 0.3s ease;
border: 1px solid rgba(233, 152, 174, 0.3);
}
.filter-section .dropdown-options.show {
display: block;
}
@keyframes dropdownFadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.filter-section .option-item {
padding: 10px 16px;
color: #666;
display: flex;
align-items: center;
gap: 10px;
cursor: pointer;
transition: all 0.2s ease;
}
.filter-section .option-item:hover {
background: linear-gradient(135deg, #ffeef2, #ffd9e2);
color: #d23f6e;
}
.filter-section .option-item.selected {
background: linear-gradient(135deg, #ffeef2, #ffd9e2);
color: #d23f6e;
font-weight: 500;
}
.filter-section .option-item i {
font-size: 16px;
width: 20px;
text-align: center;
}
.filter-section .option-item.all-option {
background-color: #f0f7ff;
margin: 0 8px 8px 8px;
border-radius: 8px;
}
.filter-section .option-item.all-option i {
color: #4a73c7;
}
/* 全局类别和选项图标 */
.filter-section .icon-all {
color: #4a73c7;
}
.filter-section .icon-literature {
color: #e67e9f;
}
.filter-section .icon-novel {
color: #f0ad4e;
}
.filter-section .icon-essay {
color: #56c596;
}
.filter-section .icon-computer {
color: #5bc0de;
}
.filter-section .icon-programming {
color: #9c27b0;
}
.filter-section .icon-sort {
color: #5c88da;
}
.filter-section .icon-order {
color: #e67e9f;
}
/* 使搜索框和下拉列表样式保持一致 */
.search-group .form-control {
border: 1px solid #f9c0d0;
border: 2px solid #f9c0d0;
height: 46px;
font-weight: 500;
color: #666;
transition: all 0.3s ease;
border-right: none;
border-radius: 25px 0 0 25px;
border-radius: 12px 0 0 12px;
padding: 10px 20px;
height: 42px;
font-size: 0.95rem;
background-color: rgba(255, 255, 255, 0.9);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05);
transition: all 0.3s;
background-color: rgba(255, 255, 255, 0.95);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
flex: 1;
}
.search-group .form-control:focus {
outline: none;
.search-group .form-control:hover {
border-color: #e67e9f;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 0 0 3px rgba(230, 126, 159, 0.2);
box-shadow: 0 0 0 3px rgba(230, 126, 159, 0.1);
}
.search-group .form-control:focus {
border-color: #d23f6e;
box-shadow: 0 0 0 3px rgba(230, 126, 159, 0.2);
color: #333;
}
.search-group .btn {
border-radius: 50%;
width: 42px;
height: 42px;
min-width: 42px;
border-radius: 0 12px 12px 0;
width: 46px;
height: 46px;
min-width: 46px;
padding: 0;
background: linear-gradient(135deg, #e67e9f 60%, #ffd3e1 100%);
color: white;
@ -232,28 +397,7 @@
.filter-group {
flex: 1;
min-width: 130px;
}
.filter-section .form-control {
border: 1px solid #f9c0d0;
border-radius: 25px;
height: 42px;
padding: 10px 20px;
background-color: rgba(255, 255, 255, 0.9);
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23e67e9f' d='M6 8.825L1.175 4 2.238 2.938 6 6.7 9.763 2.937 10.825 4z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 15px center;
background-size: 12px;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05);
width: 100%;
}
.filter-section .form-control:focus {
outline: none;
border-color: #e67e9f;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 0 0 3px rgba(230, 126, 159, 0.2);
min-width: 180px;
}
/* 图书网格布局 */
@ -263,7 +407,7 @@
gap: 24px;
margin-bottom: 30px;
position: relative;
z-index: 2;
/*z-index: 2;*/
}
/* 图书卡片样式 */
@ -685,9 +829,17 @@
}
.filter-section {
padding: 15px;
margin-bottom: 25px;
padding: 18px;
background-color: rgba(255, 255, 255, 0.8);
border-radius: 16px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
position: relative;
z-index: 100; /* Higher than books-grid but lower than dropdown-options */
backdrop-filter: blur(5px);
}
.search-form {
flex-direction: column;
gap: 12px;
@ -702,7 +854,12 @@
}
.books-grid {
grid-template-columns: 1fr;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 24px;
margin-bottom: 30px;
position: relative;
z-index: 10; /* Lower than filter-section */
}
.book-actions {

View File

@ -1,41 +1,81 @@
// 图书列表页面脚本
$(document).ready(function() {
// 处理分类筛选
function setFilter(button, categoryId) {
// 移除所有按钮的活跃状态
$('.filter-btn').removeClass('active');
// 为当前点击的按钮添加活跃状态
$(button).addClass('active');
// 设置隐藏的分类ID输入值
$('#category_id').val(categoryId);
// 提交表单
$(button).closest('form').submit();
}
// 自定义下拉列表处理
const filterHeaders = document.querySelectorAll('.filter-header');
// 处理排序方向切换
function toggleSortDirection(button) {
const $button = $(button);
const isAsc = $button.hasClass('asc');
filterHeaders.forEach(header => {
header.addEventListener('click', function() {
// 切换当前下拉列表
const dropdown = this.nextElementSibling;
const isOpen = dropdown.classList.contains('show');
// 切换方向类
$button.toggleClass('asc desc');
// 关闭所有下拉列表
document.querySelectorAll('.dropdown-options').forEach(el => {
el.classList.remove('show');
});
document.querySelectorAll('.filter-header').forEach(el => {
el.classList.remove('open');
});
// 更新图标
if (isAsc) {
$button.find('i').removeClass('fa-sort-amount-up').addClass('fa-sort-amount-down');
$('#sort_order').val('desc');
} else {
$button.find('i').removeClass('fa-sort-amount-down').addClass('fa-sort-amount-up');
$('#sort_order').val('asc');
// 如果当前不是打开状态,则打开它
if(!isOpen) {
dropdown.classList.add('show');
this.classList.add('open');
}
});
});
// 点击选项时更新并提交表单
const optionItems = document.querySelectorAll('.option-item');
optionItems.forEach(item => {
item.addEventListener('click', function() {
const container = this.closest('.custom-select-container');
const header = container.querySelector('.filter-header .filter-title');
const dropdown = this.closest('.dropdown-options');
const hiddenInput = container.querySelector('input[type="hidden"]');
// 移除之前的选中项
dropdown.querySelectorAll('.option-item').forEach(opt => {
opt.classList.remove('selected');
});
// 设置新的选中项
this.classList.add('selected');
// 更新隐藏输入值
const value = this.getAttribute('data-value');
hiddenInput.value = value;
// 更新显示的文本和图标
const icon = this.querySelector('i').cloneNode(true);
const text = this.querySelector('span').textContent;
header.innerHTML = '';
header.appendChild(icon);
const span = document.createElement('span');
span.textContent = text;
header.appendChild(span);
// 关闭下拉列表
dropdown.classList.remove('show');
container.querySelector('.filter-header').classList.remove('open');
// 提交表单
document.getElementById('filter-form').submit();
});
});
// 点击页面其他地方关闭下拉列表
document.addEventListener('click', function(e) {
if(!e.target.closest('.custom-select-container')) {
document.querySelectorAll('.dropdown-options').forEach(el => {
el.classList.remove('show');
});
document.querySelectorAll('.filter-header').forEach(el => {
el.classList.remove('open');
});
}
// 提交表单
$button.closest('form').submit();
}
// 将函数暴露到全局作用域
window.setFilter = setFilter;
window.toggleSortDirection = toggleSortDirection;
});
// 处理删除图书
let bookIdToDelete = null;
@ -278,16 +318,18 @@ $(document).ready(function() {
$(window).on('resize', adjustCardHeights);
// 为封面图片添加加载错误处理
$('.book-cover').on('error', function() {
$('.book-cover img').on('error', function() {
const $this = $(this);
const title = $this.attr('alt') || '图书';
const bookTitle = $this.attr('alt') || '图书';
const $cover = $this.closest('.book-cover');
// 替换为默认封面
$this.replaceWith(`
<div class="default-cover">
// 替换为无封面显示
$cover.html(`
<div class="no-cover">
<i class="fas fa-book"></i>
<span class="default-cover-text">${title.charAt(0)}</span>
<span>无封面</span>
</div>
<div class="cover-title-bar">${bookTitle}</div>
`);
});

View File

@ -8,6 +8,10 @@
{% block content %}
<div class="book-list-container">
<!-- 添加泡泡动画元素 -->
{% for i in range(15) %}
<div class="bubble"></div>
{% endfor %}
<div class="page-header">
<h1>图书管理</h1>
@ -31,7 +35,7 @@
</div>
<div class="filter-section">
<form method="GET" action="{{ url_for('book.book_list') }}" class="search-form">
<form method="GET" action="{{ url_for('book.book_list') }}" class="search-form" id="filter-form">
<div class="search-row">
<div class="form-group search-group">
<input type="text" name="search" class="form-control" placeholder="搜索书名/作者/ISBN" value="{{ search }}">
@ -41,29 +45,89 @@
</div>
</div>
<div class="filter-row">
<div class="form-group filter-group">
<select name="category_id" class="form-control" onchange="this.form.submit()">
<option value="">全部分类</option>
{% for category in categories %}
<option value="{{ category.id }}" {% if category_id == category.id %}selected{% endif %}>
{{ category.name }}
</option>
{% endfor %}
</select>
<div class="filter-group">
<div class="custom-select-container" id="category-select">
<div class="filter-header">
<div class="filter-title">
<i class="fas fa-tags icon-literature"></i>
<span>{% if category_id %}{% for category in categories %}{% if category.id == category_id %}{{ category.name }}{% endif %}{% endfor %}{% else %}全部分类{% endif %}</span>
</div>
<i class="fas fa-chevron-down icon-arrow"></i>
</div>
<div class="dropdown-options">
<div class="option-item all-option {% if not category_id %}selected{% endif %}" data-value="">
<i class="fas fa-globe icon-all"></i>
<span>全部分类</span>
</div>
{% for category in categories %}
<div class="option-item {% if category_id == category.id %}selected{% endif %}" data-value="{{ category.id }}">
<i class="fas fa-book icon-literature"></i>
<span>{{ category.name }}</span>
</div>
{% endfor %}
</div>
<input type="hidden" name="category_id" value="{{ category_id }}">
</div>
</div>
<div class="form-group filter-group">
<select name="sort" class="form-control" onchange="this.form.submit()">
<option value="id" {% if sort == 'id' %}selected{% endif %}>默认排序</option>
<option value="created_at" {% if sort == 'created_at' %}selected{% endif %}>入库时间</option>
<option value="title" {% if sort == 'title' %}selected{% endif %}>书名</option>
<option value="stock" {% if sort == 'stock' %}selected{% endif %}>库存</option>
</select>
<div class="filter-group">
<div class="custom-select-container" id="sort-select">
<div class="filter-header">
<div class="filter-title">
<i class="fas fa-sort-amount-down icon-sort"></i>
<span>
{% if sort == 'id' %}默认排序
{% elif sort == 'created_at' %}入库时间
{% elif sort == 'title' %}书名
{% elif sort == 'stock' %}库存
{% else %}默认排序{% endif %}
</span>
</div>
<i class="fas fa-chevron-down icon-arrow"></i>
</div>
<div class="dropdown-options">
<div class="option-item {% if sort == 'id' %}selected{% endif %}" data-value="id">
<i class="fas fa-sort-numeric-down"></i>
<span>默认排序</span>
</div>
<div class="option-item {% if sort == 'created_at' %}selected{% endif %}" data-value="created_at">
<i class="fas fa-calendar-alt"></i>
<span>入库时间</span>
</div>
<div class="option-item {% if sort == 'title' %}selected{% endif %}" data-value="title">
<i class="fas fa-font"></i>
<span>书名</span>
</div>
<div class="option-item {% if sort == 'stock' %}selected{% endif %}" data-value="stock">
<i class="fas fa-warehouse"></i>
<span>库存</span>
</div>
</div>
<input type="hidden" name="sort" value="{{ sort }}">
</div>
</div>
<div class="form-group filter-group">
<select name="order" class="form-control" onchange="this.form.submit()">
<option value="desc" {% if order == 'desc' %}selected{% endif %}>降序</option>
<option value="asc" {% if order == 'asc' %}selected{% endif %}>升序</option>
</select>
<div class="filter-group">
<div class="custom-select-container" id="order-select">
<div class="filter-header">
<div class="filter-title">
<i class="fas fa-{% if order == 'asc' %}arrow-up{% else %}arrow-down{% endif %} icon-order"></i>
<span>{% if order == 'asc' %}升序{% else %}降序{% endif %}</span>
</div>
<i class="fas fa-chevron-down icon-arrow"></i>
</div>
<div class="dropdown-options">
<div class="option-item {% if order == 'desc' %}selected{% endif %}" data-value="desc">
<i class="fas fa-arrow-down"></i>
<span>降序</span>
</div>
<div class="option-item {% if order == 'asc' %}selected{% endif %}" data-value="asc">
<i class="fas fa-arrow-up"></i>
<span>升序</span>
</div>
</div>
<input type="hidden" name="order" value="{{ order }}">
</div>
</div>
</div>
</form>
@ -205,4 +269,3 @@
<script src="{{ url_for('static', filename='js/book-list.js') }}"></script>
{{ super() }}
{% endblock %}