================================================================================
File: ./config.py
================================================================================
import os
# 数据库配置
"""
DB_HOST = os.environ.get('DB_HOST', '27.124.22.104')
DB_PORT = os.environ.get('DB_PORT', '3306')
DB_USER = os.environ.get('DB_USER', 'book20250428')
DB_PASSWORD = os.environ.get('DB_PASSWORD', 'booksystem')
DB_NAME = os.environ.get('DB_NAME', 'book_system')
"""
DB_HOST = os.environ.get('DB_HOST', 'rm-bp1h5oqo8ld21viftro.mysql.rds.aliyuncs.com')
DB_PORT = os.environ.get('DB_PORT', '3306')
DB_USER = os.environ.get('DB_USER', 'shiqi')
DB_PASSWORD = os.environ.get('DB_PASSWORD', 'Shiqi1234!')
DB_NAME = os.environ.get('DB_NAME', 'book_system')
# 数据库连接字符串
SQLALCHEMY_DATABASE_URI = f'mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}'
SQLALCHEMY_TRACK_MODIFICATIONS = False
# 应用密钥
SECRET_KEY = os.environ.get('SECRET_KEY', 'dev_key_replace_in_production')
# 邮件配置
EMAIL_HOST = os.environ.get('EMAIL_HOST', 'smtp.qq.com')
EMAIL_PORT = int(os.environ.get('EMAIL_PORT', 587))
EMAIL_ENCRYPTION = os.environ.get('EMAIL_ENCRYPTION', 'starttls')
EMAIL_USERNAME = os.environ.get('EMAIL_USERNAME', '3399560459@qq.com')
EMAIL_PASSWORD = os.environ.get('EMAIL_PASSWORD', 'fzwhyirhbqdzcjgf')
EMAIL_FROM = os.environ.get('EMAIL_FROM', '3399560459@qq.com')
EMAIL_FROM_NAME = os.environ.get('EMAIL_FROM_NAME', 'BOOKSYSTEM_OFFICIAL')
# 会话配置
PERMANENT_SESSION_LIFETIME = 86400 * 7
================================================================================
File: ./all_file_output.py
================================================================================
import os
import sys
def collect_code_files(output_file="code_collection.txt"):
    # 定义代码文件扩展名
    code_extensions = [
        '.py', '.java', '.cpp', '.c', '.h', '.hpp', '.cs',
        '.js', '.html', '.css', '.php', '.go', '.rb',
        '.swift', '.kt', '.ts', '.sh', '.pl', '.r'
    ]
    # 定义要排除的目录
    excluded_dirs = [
        'venv', 'env', '.venv', '.env', 'virtualenv',
        '__pycache__', 'node_modules', '.git', '.idea',
        'dist', 'build', 'target', 'bin'
    ]
    # 计数器
    file_count = 0
    # 打开输出文件
    with open(output_file, 'w', encoding='utf-8') as out_file:
        # 遍历当前目录及所有子目录
        for root, dirs, files in os.walk('.'):
            # 从dirs中移除排除的目录,这会阻止os.walk进入这些目录
            dirs[:] = [d for d in dirs if d not in excluded_dirs]
            for file in files:
                # 获取文件扩展名
                _, ext = os.path.splitext(file)
                # 检查是否为代码文件
                if ext.lower() in code_extensions:
                    file_path = os.path.join(root, file)
                    file_count += 1
                    # 写入文件路径作为分隔
                    out_file.write(f"\n{'=' * 80}\n")
                    out_file.write(f"File: {file_path}\n")
                    out_file.write(f"{'=' * 80}\n\n")
                    # 尝试读取文件内容并写入
                    try:
                        with open(file_path, 'r', encoding='utf-8') as code_file:
                            out_file.write(code_file.read())
                    except UnicodeDecodeError:
                        # 尝试用不同的编码
                        try:
                            with open(file_path, 'r', encoding='latin-1') as code_file:
                                out_file.write(code_file.read())
                        except Exception as e:
                            out_file.write(f"无法读取文件内容: {str(e)}\n")
                    except Exception as e:
                        out_file.write(f"读取文件时出错: {str(e)}\n")
    print(f"已成功收集 {file_count} 个代码文件到 {output_file}")
if __name__ == "__main__":
    # 如果提供了命令行参数,则使用它作为输出文件名
    output_file = sys.argv[1] if len(sys.argv) > 1 else "code_collection.txt"
    collect_code_files(output_file)
================================================================================
File: ./app.py
================================================================================
from app import create_app
app = create_app()
if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=49666)
================================================================================
File: ./main.py
================================================================================
# 这是一个示例 Python 脚本。
# 按 ⌃R 执行或将其替换为您的代码。
# 按 双击 ⇧ 在所有地方搜索类、文件、工具窗口、操作和设置。
def print_hi(name):
    # 在下面的代码行中使用断点来调试脚本。
    print(f'Hi, {name}')  # 按 ⌘F8 切换断点。
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
    print_hi('PyCharm')
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
================================================================================
File: ./app/__init__.py
================================================================================
from flask import Flask, render_template, session, g, Markup, redirect, url_for, request
from flask_login import LoginManager
from app.models.database import db
from app.models.user import User
from app.controllers.user import user_bp
from app.controllers.book import book_bp
from app.controllers.borrow import borrow_bp
from app.controllers.inventory import inventory_bp
from flask_login import LoginManager, current_user
from app.controllers.statistics import statistics_bp
from app.controllers.announcement import announcement_bp
from app.models.notification import Notification
from app.controllers.log import log_bp
import os
from datetime import datetime
login_manager = LoginManager()
def create_app(config=None):
    app = Flask(__name__)
    # 加载默认配置
    app.config.from_object('config')
    # 如果提供了配置对象,则加载它
    if config:
        if isinstance(config, dict):
            app.config.update(config)
        else:
            app.config.from_object(config)
    # 从环境变量指定的文件加载配置(如果有)
    app.config.from_envvar('APP_CONFIG_FILE', silent=True)
    # 初始化数据库
    db.init_app(app)
    # 初始化 Flask-Login
    login_manager.init_app(app)
    login_manager.login_view = 'user.login'
    @login_manager.user_loader
    def load_user(user_id):
        return User.query.get(int(user_id))
    from app.utils.template_helpers import register_template_helpers
    # 注册蓝图
    register_template_helpers(app)
    app.register_blueprint(user_bp, url_prefix='/user')
    app.register_blueprint(book_bp, url_prefix='/book')
    app.register_blueprint(borrow_bp, url_prefix='/borrow')
    app.register_blueprint(statistics_bp)
    app.register_blueprint(inventory_bp)
    app.register_blueprint(log_bp)
    app.register_blueprint(announcement_bp, url_prefix='/announcement')
    # 创建数据库表
    with app.app_context():
        # 先导入基础模型
        from app.models.user import User, Role
        from app.models.book import Book, Category
        # 创建表
        db.create_all()
        # 再导入依赖模型 - 但不在这里定义关系
        from app.models.borrow import BorrowRecord
        from app.models.inventory import InventoryLog
        from app.models.log import Log
        # 移除这些重复的关系定义
        # Book.borrow_records = db.relationship('BorrowRecord', backref='book', lazy='dynamic')
        # Book.inventory_logs = db.relationship('InventoryLog', backref='book', lazy='dynamic')
        # Category.books = db.relationship('Book', backref='category', lazy='dynamic')
        # 创建默认角色
        from app.models.user import Role
        if not Role.query.filter_by(id=1).first():
            admin_role = Role(id=1, role_name='管理员', description='系统管理员')
            db.session.add(admin_role)
        if not Role.query.filter_by(id=2).first():
            user_role = Role(id=2, role_name='普通用户', description='普通用户')
            db.session.add(user_role)
        # 创建管理员账号
        if not User.query.filter_by(username='admin').first():
            admin = User(
                username='admin',
                password='admin123',
                email='admin@example.com',
                role_id=1,
                nickname='系统管理员'
            )
            db.session.add(admin)
        # 创建基础分类
        from app.models.book import Category
        if not Category.query.first():
            categories = [
                Category(name='文学', sort=1),
                Category(name='计算机', sort=2),
                Category(name='历史', sort=3),
                Category(name='科学', sort=4),
                Category(name='艺术', sort=5),
                Category(name='经济', sort=6),
                Category(name='哲学', sort=7),
                Category(name='教育', sort=8)
            ]
            db.session.add_all(categories)
        db.session.commit()
    # 添加缓存控制中间件
    @app.after_request
    def add_cache_headers(response):
        # 为HTML页面和主页添加禁止缓存的头
        if request.path == '/' or 'text/html' in response.headers.get('Content-Type', ''):
            response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate, max-age=0"
            response.headers["Pragma"] = "no-cache"
            response.headers["Expires"] = "0"
            response.headers['Vary'] = 'Cookie, Authorization'
        return response
    # 其余代码保持不变...
    @app.before_request
    def load_logged_in_user():
        user_id = session.get('user_id')
        if user_id is None:
            g.user = None
        else:
            g.user = User.query.get(user_id)
    @app.route('/')
    def index():
        from app.models.book import Book
        from app.models.user import User
        from app.models.borrow import BorrowRecord
        from app.models.announcement import Announcement
        from app.models.notification import Notification
        from sqlalchemy import func, desc
        from flask_login import current_user
        # 获取统计数据
        stats = {
            'total_books': Book.query.count(),
            'total_users': User.query.count(),
            'active_borrows': BorrowRecord.query.filter(BorrowRecord.return_date.is_(None)).count(),
            'user_borrows': 0
        }
        # 如果用户已登录,获取其待还图书数量
        if current_user.is_authenticated:
            stats['user_borrows'] = BorrowRecord.query.filter(
                BorrowRecord.user_id == current_user.id,
                BorrowRecord.return_date.is_(None)
            ).count()
        # 获取最新图书
        latest_books = Book.query.filter_by(status=1).order_by(Book.created_at.desc()).limit(4).all()
        # 获取热门图书(根据借阅次数)
        try:
            # 这里假设你的数据库中有表记录借阅次数
            popular_books_query = db.session.query(
                Book, func.count(BorrowRecord.id).label('borrow_count')
            ).join(
                BorrowRecord, Book.id == BorrowRecord.book_id, isouter=True
            ).filter(
                Book.status == 1
            ).group_by(
                Book.id
            ).order_by(
                desc('borrow_count')
            ).limit(5)
            # 提取图书对象并添加借阅计数
            popular_books = []
            for book, count in popular_books_query:
                book.borrow_count = count
                popular_books.append(book)
        except Exception as e:
            # 如果查询有问题,使用最新的书作为备选
            popular_books = latest_books.copy() if latest_books else []
            print(f"获取热门图书失败: {str(e)}")
        # 获取最新公告
        announcements = Announcement.query.filter_by(status=1).order_by(
            Announcement.is_top.desc(),
            Announcement.created_at.desc()
        ).limit(3).all()
        now = datetime.now()
        # 获取用户的未读通知
        user_notifications = []
        if current_user.is_authenticated:
            user_notifications = Notification.query.filter_by(
                user_id=current_user.id,
                status=0
            ).order_by(
                Notification.created_at.desc()
            ).limit(5).all()
        return render_template('index.html',
                               stats=stats,
                               latest_books=latest_books,
                               popular_books=popular_books,
                               announcements=announcements,
                               user_notifications=user_notifications,
                               now=now
                               )
    @app.errorhandler(404)
    def page_not_found(e):
        return render_template('404.html'), 404
    @app.template_filter('nl2br')
    def nl2br_filter(s):
        if s:
            return Markup(s.replace('\n', '
'))
        return s
    @app.context_processor
    def utility_processor():
        def get_unread_notifications_count(user_id):
            if user_id:
                return Notification.get_unread_count(user_id)
            return 0
        def get_recent_notifications(user_id, limit=5):
            if user_id:
                # 按时间倒序获取最近的几条通知
                notifications = Notification.query.filter_by(user_id=user_id) \
                    .order_by(Notification.created_at.desc()) \
                    .limit(limit) \
                    .all()
                return notifications
            return []
        return dict(
            get_unread_notifications_count=get_unread_notifications_count,
            get_recent_notifications=get_recent_notifications
        )
    @app.context_processor
    def inject_now():
        return {'now': datetime.now()}
    return app
================================================================================
File: ./app/init_permissions.py
================================================================================
from app import create_app
from app.models.database import db
from app.models.user import Role
from app.models.permission import Permission
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def init_permissions():
    """初始化系统权限"""
    logger.info("开始初始化系统权限...")
    # 只定义管理类权限,对应现有的 @admin_required 装饰的路由
    permissions = [
        # 公告管理权限
        {'code': 'manage_announcements', 'name': '公告管理', 'description': '允许管理系统公告(发布、编辑、删除、置顶等)'},
        # 图书管理权限
        {'code': 'manage_books', 'name': '图书管理', 'description': '允许管理图书(添加、编辑、删除图书)'},
        {'code': 'manage_categories', 'name': '分类管理', 'description': '允许管理图书分类'},
        {'code': 'import_export_books', 'name': '导入导出图书', 'description': '允许批量导入和导出图书数据'},
        # 借阅管理权限
        {'code': 'manage_borrows', 'name': '借阅管理', 'description': '允许管理全系统借阅记录和处理借还书操作'},
        {'code': 'manage_overdue', 'name': '逾期管理', 'description': '允许查看和处理逾期借阅'},
        # 库存管理权限
        {'code': 'manage_inventory', 'name': '库存管理', 'description': '允许查看和调整图书库存'},
        # 日志权限
        {'code': 'view_logs', 'name': '查看日志', 'description': '允许查看系统操作日志'},
        # 统计权限
        {'code': 'view_statistics', 'name': '查看统计', 'description': '允许查看统计分析数据'},
        # 用户管理权限
        {'code': 'manage_users', 'name': '用户管理', 'description': '允许管理用户(添加、编辑、禁用、删除用户)'},
        {'code': 'manage_roles', 'name': '角色管理', 'description': '允许管理角色和权限'},
    ]
    # 添加权限记录
    added_count = 0
    updated_count = 0
    for perm_data in permissions:
        # 检查权限是否已存在
        existing_perm = Permission.query.filter_by(code=perm_data['code']).first()
        if existing_perm:
            # 更新现有权限信息
            existing_perm.name = perm_data['name']
            existing_perm.description = perm_data['description']
            updated_count += 1
        else:
            # 创建新权限
            permission = Permission(**perm_data)
            db.session.add(permission)
            added_count += 1
    # 提交所有权限
    db.session.commit()
    logger.info(f"权限初始化完成: 新增 {added_count} 个, 更新 {updated_count} 个")
    # 处理角色权限分配
    assign_role_permissions()
def assign_role_permissions():
    """为系统默认角色分配权限"""
    logger.info("开始分配角色权限...")
    # 获取所有权限
    all_permissions = Permission.query.all()
    # 获取系统内置角色
    admin_role = Role.query.get(1)  # 管理员角色
    user_role = Role.query.get(2)  # 普通用户角色
    if admin_role and user_role:
        # 管理员拥有所有权限
        admin_role.permissions = all_permissions
        # 普通用户无需特殊管理权限
        user_role.permissions = []
        db.session.commit()
        logger.info(f"管理员角色分配了 {len(all_permissions)} 个权限")
        logger.info(f"普通用户角色无管理权限")
    else:
        logger.error("无法找到内置角色,请确保角色表已正确初始化")
def main():
    """主函数"""
    app = create_app()
    with app.app_context():
        init_permissions()
if __name__ == "__main__":
    main()
================================================================================
File: ./app/utils/auth.py
================================================================================
from functools import wraps
from flask import redirect, url_for, flash, request
from flask_login import current_user
def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        print(f"DEBUG: login_required 检查 - current_user.is_authenticated = {current_user.is_authenticated}")
        if not current_user.is_authenticated:
            flash('请先登录', 'warning')
            return redirect(url_for('user.login', next=request.url))
        return f(*args, **kwargs)
    return decorated_function
def admin_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        print(f"DEBUG: admin_required 检查 - current_user.is_authenticated = {current_user.is_authenticated}")
        if not current_user.is_authenticated:
            flash('请先登录', 'warning')
            return redirect(url_for('user.login', next=request.url))
        print(f"DEBUG: admin_required 检查 - current_user.role_id = {getattr(current_user, 'role_id', None)}")
        if getattr(current_user, 'role_id', None) != 1:  # 安全地获取role_id属性
            flash('权限不足', 'danger')
            return redirect(url_for('index'))
        return f(*args, **kwargs)
    return decorated_function
def permission_required(permission_code):
    """
    检查用户是否拥有特定权限的装饰器
    :param permission_code: 权限代码,例如 'manage_books'
    """
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            print(
                f"DEBUG: permission_required({permission_code}) 检查 - current_user.is_authenticated = {current_user.is_authenticated}")
            # 检查用户是否登录
            if not current_user.is_authenticated:
                flash('请先登录', 'warning')
                return redirect(url_for('user.login', next=request.url))
            # 管理员拥有所有权限
            if getattr(current_user, 'role_id', None) == 1:
                return f(*args, **kwargs)
            # 获取用户角色并检查是否有指定权限
            from app.models.user import Role
            role = Role.query.get(current_user.role_id)
            if not role:
                flash('用户角色异常', 'danger')
                return redirect(url_for('index'))
            # 检查角色是否有指定权限
            has_permission = False
            for perm in role.permissions:
                if perm.code == permission_code:
                    has_permission = True
                    break
            if not has_permission:
                print(f"DEBUG: 用户 {current_user.username} 缺少权限 {permission_code}")
                flash('您没有执行此操作的权限', 'danger')
                return redirect(url_for('index'))
            return f(*args, **kwargs)
        return decorated_function
    return decorator
================================================================================
File: ./app/utils/db.py
================================================================================
================================================================================
File: ./app/utils/__init__.py
================================================================================
================================================================================
File: ./app/utils/logger.py
================================================================================
from flask import request, current_app
from flask_login import current_user
from app.models.log import Log
def record_activity(action, target_type=None, target_id=None, description=None):
    """
    记录用户活动
    参数:
    - action: 操作类型,如 'login', 'logout', 'create', 'update', 'delete', 'borrow', 'return' 等
    - target_type: 操作对象类型,如 'book', 'user', 'borrow' 等
    - target_id: 操作对象ID
    - description: 操作详细描述
    """
    try:
        # 获取当前用户ID
        user_id = current_user.id if current_user.is_authenticated else None
        # 获取客户端IP地址
        ip_address = request.remote_addr
        if 'X-Forwarded-For' in request.headers:
            ip_address = request.headers.getlist("X-Forwarded-For")[0].rpartition(' ')[-1]
        # 记录日志
        Log.add_log(
            action=action,
            user_id=user_id,
            target_type=target_type,
            target_id=target_id,
            ip_address=ip_address,
            description=description
        )
        return True
    except Exception as e:
        # 记录错误,但不影响主要功能
        if current_app:
            current_app.logger.error(f"Error recording activity log: {str(e)}")
        return False
================================================================================
File: ./app/utils/template_helpers.py
================================================================================
from app.models.permission import Permission
from flask import current_app
def register_template_helpers(app):
    @app.context_processor
    def inject_permissions():
        def has_permission(user, permission_code):
            """检查用户是否拥有指定权限"""
            if not user or not user.is_authenticated:
                return False
            # 管理员拥有所有权限
            if user.role_id == 1:
                return True
            # 检查用户角色权限
            if user.role:
                for perm in user.role.permissions:
                    if perm.code == permission_code:
                        return True
            return False
        return dict(has_permission=has_permission)
# 在 create_app 函数中调用
# register_template_helpers(app)
================================================================================
File: ./app/utils/email.py
================================================================================
import smtplib
import random
import string
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from flask import current_app
import logging
# 配置日志
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
# 配置邮件发送功能
def send_verification_email(to_email, verification_code):
    """
    发送验证码邮件
    """
    try:
        # 从应用配置获取邮件设置
        email_host = current_app.config['EMAIL_HOST']
        email_port = current_app.config['EMAIL_PORT']
        email_username = current_app.config['EMAIL_USERNAME']
        email_password = current_app.config['EMAIL_PASSWORD']
        email_from = current_app.config['EMAIL_FROM']
        email_from_name = current_app.config['EMAIL_FROM_NAME']
        logger.info(f"准备发送邮件到: {to_email}, 验证码: {verification_code}")
        logger.debug(f"邮件配置: 主机={email_host}, 端口={email_port}")
        # 邮件内容
        msg = MIMEMultipart()
        msg['From'] = f"{email_from_name} <{email_from}>"
        msg['To'] = to_email
        msg['Subject'] = "图书管理系统 - 验证码"
        # 邮件正文
        body = f"""
        
        
            
                图书管理系统 - 邮箱验证
                您好,
                感谢您注册图书管理系统,您的验证码是:
                
                    {verification_code}
                
                该验证码将在10分钟内有效,请勿将验证码分享给他人。
                如果您没有请求此验证码,请忽略此邮件。
                
                    此邮件为系统自动发送,请勿回复。
                    © 2025 图书管理系统
                 
             
        
        
        """
        msg.attach(MIMEText(body, 'html'))
        logger.debug("尝试连接到SMTP服务器...")
        # 连接服务器发送邮件
        server = smtplib.SMTP(email_host, email_port)
        server.set_debuglevel(1)  # 启用详细的SMTP调试输出
        logger.debug("检查是否需要STARTTLS加密...")
        if current_app.config.get('EMAIL_ENCRYPTION') == 'starttls':
            logger.debug("启用STARTTLS...")
            server.starttls()
        logger.debug(f"尝试登录邮箱: {email_username}")
        server.login(email_username, email_password)
        logger.debug("发送邮件...")
        server.send_message(msg)
        logger.debug("关闭连接...")
        server.quit()
        logger.info(f"邮件发送成功: {to_email}")
        return True
    except Exception as e:
        logger.error(f"邮件发送失败: {str(e)}", exc_info=True)
        return False
def generate_verification_code(length=6):
    """
    生成数字验证码
    """
    return ''.join(random.choice(string.digits) for _ in range(length))
================================================================================
File: ./app/utils/helpers.py
================================================================================
================================================================================
File: ./app/models/user.py
================================================================================
from app.models.database import db
from werkzeug.security import generate_password_hash, check_password_hash
from datetime import datetime
from flask_login import UserMixin
from app.models.permission import RolePermission, Permission
#db = SQLAlchemy()
class User(db.Model, UserMixin):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(64), unique=True, nullable=False)
    password = db.Column(db.String(255), nullable=False)
    email = db.Column(db.String(128), unique=True, nullable=True)
    phone = db.Column(db.String(20), unique=True, nullable=True)
    nickname = db.Column(db.String(64), nullable=True)
    status = db.Column(db.Integer, default=1)  # 1: active, 0: disabled
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'), default=2)  # 2: 普通用户, 1: 管理员
    created_at = db.Column(db.DateTime, default=datetime.now)
    updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
    def __init__(self, username, password, email=None, phone=None, nickname=None, role_id=2, status=1):
        self.username = username
        self.set_password(password)
        self.email = email
        self.phone = phone
        self.nickname = nickname
        self.role_id = role_id
        self.status = status  # 新增
    @property
    def is_active(self):
        return self.status == 1
    def set_password(self, password):
        """设置密码,使用哈希加密"""
        self.password = generate_password_hash(password)
    def check_password(self, password):
        """验证密码"""
        return check_password_hash(self.password, password)
    def to_dict(self):
        """转换为字典格式"""
        return {
            'id': self.id,
            'username': self.username,
            'email': self.email,
            'phone': self.phone,
            'nickname': self.nickname,
            'status': self.status,
            'role_id': self.role_id,
            'created_at': self.created_at.strftime('%Y-%m-%d %H:%M:%S'),
            'updated_at': self.updated_at.strftime('%Y-%m-%d %H:%M:%S')
        }
    @classmethod
    def create_user(cls, username, password, email=None, phone=None, nickname=None, role_id=2):
        """创建新用户"""
        user = User(
            username=username,
            password=password,
            email=email,
            phone=phone,
            nickname=nickname,
            role_id=role_id
        )
        db.session.add(user)
        db.session.commit()
        return user
class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    role_name = db.Column(db.String(32), unique=True, nullable=False)
    description = db.Column(db.String(128))
    permissions = db.relationship(
        'Permission',
        secondary='role_permissions',
        backref=db.backref('roles', lazy='dynamic'),
        lazy='dynamic'
    )
    users = db.relationship('User', backref='role')
================================================================================
File: ./app/models/permission.py
================================================================================
from app.models.database import db
from datetime import datetime
# 这是权限表 model
class Permission(db.Model):
    __tablename__ = 'permissions'
    id = db.Column(db.Integer, primary_key=True)
    code = db.Column(db.String(64), unique=True, nullable=False, comment='权限代码,用于系统识别')
    name = db.Column(db.String(64), nullable=False, comment='权限名称,用于界面显示')
    description = db.Column(db.String(255), comment='权限描述,说明权限用途')
# 角色-权限 关联表(辅助对象模式,方便ORM关系管理)
class RolePermission(db.Model):
    __tablename__ = 'role_permissions'
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id', ondelete='CASCADE'), primary_key=True, comment='角色ID,关联roles表')
    permission_id = db.Column(db.Integer, db.ForeignKey('permissions.id', ondelete='CASCADE'), primary_key=True, comment='权限ID,关联permissions表')
    created_at = db.Column(db.DateTime, default=datetime.now, comment='权限分配时间')
================================================================================
File: ./app/models/log.py
================================================================================
from datetime import datetime
from app.models.user import db, User  # 从user模块导入db,而不是从utils导入
class Log(db.Model):
    __tablename__ = 'logs'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
    action = db.Column(db.String(64), nullable=False)
    target_type = db.Column(db.String(32), nullable=True)
    target_id = db.Column(db.Integer, nullable=True)
    ip_address = db.Column(db.String(45), nullable=True)
    description = db.Column(db.String(255), nullable=True)
    created_at = db.Column(db.DateTime, nullable=False, default=datetime.now)
    # 关联用户
    user = db.relationship('User', backref=db.backref('logs', lazy=True))
    def __init__(self, action, user_id=None, target_type=None, target_id=None,
                 ip_address=None, description=None):
        self.user_id = user_id
        self.action = action
        self.target_type = target_type
        self.target_id = target_id
        self.ip_address = ip_address
        self.description = description
        self.created_at = datetime.now()
    @staticmethod
    def add_log(action, user_id=None, target_type=None, target_id=None,
                ip_address=None, description=None):
        """添加一条日志记录"""
        try:
            log = Log(
                action=action,
                user_id=user_id,
                target_type=target_type,
                target_id=target_id,
                ip_address=ip_address,
                description=description
            )
            db.session.add(log)
            db.session.commit()
            return True, "日志记录成功"
        except Exception as e:
            db.session.rollback()
            return False, f"日志记录失败: {str(e)}"
    @staticmethod
    def get_logs(page=1, per_page=20, user_id=None, action=None,
                 target_type=None, start_date=None, end_date=None):
        """查询日志记录"""
        query = Log.query.order_by(Log.created_at.desc())
        if user_id:
            query = query.filter(Log.user_id == user_id)
        if action:
            query = query.filter(Log.action == action)
        if target_type:
            query = query.filter(Log.target_type == target_type)
        if start_date:
            query = query.filter(Log.created_at >= start_date)
        if end_date:
            query = query.filter(Log.created_at <= end_date)
        return query.paginate(page=page, per_page=per_page)
================================================================================
File: ./app/models/notification.py
================================================================================
from datetime import datetime
from app.models.user import db, User  # 从user模块导入db,而不是从app.models导入
class Notification(db.Model):
    __tablename__ = 'notifications'
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    title = db.Column(db.String(128), nullable=False)
    content = db.Column(db.Text, nullable=False)
    type = db.Column(db.String(32), nullable=False)  # 通知类型:system, borrow, return, overdue, etc.
    status = db.Column(db.Integer, default=0)  # 0-未读, 1-已读
    sender_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    created_at = db.Column(db.DateTime, nullable=False, default=datetime.now)
    read_at = db.Column(db.DateTime)
    # 关联关系
    user = db.relationship('User', foreign_keys=[user_id], backref='notifications')
    sender = db.relationship('User', foreign_keys=[sender_id], backref='sent_notifications')
    def to_dict(self):
        """将通知转换为字典"""
        return {
            'id': self.id,
            'user_id': self.user_id,
            'title': self.title,
            'content': self.content,
            'type': self.type,
            'status': self.status,
            'sender_id': self.sender_id,
            'sender_name': self.sender.username if self.sender else 'System',
            'created_at': self.created_at.strftime('%Y-%m-%d %H:%M:%S'),
            'read_at': self.read_at.strftime('%Y-%m-%d %H:%M:%S') if self.read_at else None
        }
    @staticmethod
    def get_user_notifications(user_id, page=1, per_page=10, unread_only=False):
        """获取用户通知"""
        query = Notification.query.filter_by(user_id=user_id)
        if unread_only:
            query = query.filter_by(status=0)
        return query.order_by(Notification.created_at.desc()).paginate(
            page=page, per_page=per_page, error_out=False
        )
    @staticmethod
    def get_unread_count(user_id):
        """获取用户未读通知数量"""
        return Notification.query.filter_by(user_id=user_id, status=0).count()
    @staticmethod
    def mark_as_read(notification_id, user_id=None):
        """将通知标记为已读"""
        notification = Notification.query.get(notification_id)
        if not notification:
            return False, "通知不存在"
        # 验证用户权限
        if user_id and notification.user_id != user_id:
            return False, "无权操作此通知"
        notification.status = 1
        notification.read_at = datetime.now()
        try:
            db.session.commit()
            return True, "已标记为已读"
        except Exception as e:
            db.session.rollback()
            return False, str(e)
    @staticmethod
    def create_notification(user_id, title, content, notification_type, sender_id=None):
        """创建新通知"""
        notification = Notification(
            user_id=user_id,
            title=title,
            content=content,
            type=notification_type,
            sender_id=sender_id
        )
        try:
            db.session.add(notification)
            db.session.commit()
            return True, notification
        except Exception as e:
            db.session.rollback()
            return False, str(e)
    @staticmethod
    def create_system_notification(user_ids, title, content, notification_type, sender_id=None):
        """创建系统通知,发送给多个用户"""
        success_count = 0
        fail_count = 0
        for user_id in user_ids:
            success, _ = Notification.create_notification(
                user_id=user_id,
                title=title,
                content=content,
                notification_type=notification_type,
                sender_id=sender_id
            )
            if success:
                success_count += 1
            else:
                fail_count += 1
        return success_count, fail_count
================================================================================
File: ./app/models/database.py
================================================================================
from flask_sqlalchemy import SQLAlchemy
# 创建共享的SQLAlchemy实例
db = SQLAlchemy()
================================================================================
File: ./app/models/__init__.py
================================================================================
def create_app():
    app = Flask(__name__)
    # ... 配置代码 ...
    # 初始化数据库
    db.init_app(app)
    # 导入模型,确保所有模型在创建表之前被加载
    from app.models.user import User, Role
    from app.models.book import Book, Category
    from app.models.borrow import BorrowRecord
    from app.models.inventory import InventoryLog
    # 创建数据库表
    with app.app_context():
        db.create_all()
        # ... 其余代码 ...
================================================================================
File: ./app/models/book.py
================================================================================
from app.models.user import db
from datetime import datetime
class Category(db.Model):
    __tablename__ = 'categories'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), nullable=False)
    parent_id = db.Column(db.Integer, db.ForeignKey('categories.id'), nullable=True)
    sort = db.Column(db.Integer, default=0)
    # 关系 - 只保留与自身的关系
    parent = db.relationship('Category', remote_side=[id], backref='children')
    def __repr__(self):
        return f''
class Book(db.Model):
    __tablename__ = 'books'
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(255), nullable=False)
    author = db.Column(db.String(128), nullable=False)
    publisher = db.Column(db.String(128), nullable=True)
    category_id = db.Column(db.Integer, db.ForeignKey('categories.id'), nullable=True)
    tags = db.Column(db.String(255), nullable=True)
    isbn = db.Column(db.String(32), unique=True, nullable=True)
    publish_year = db.Column(db.String(16), nullable=True)
    description = db.Column(db.Text, nullable=True)
    cover_url = db.Column(db.String(255), nullable=True)
    stock = db.Column(db.Integer, default=0)
    price = db.Column(db.Numeric(10, 2), nullable=True)
    status = db.Column(db.Integer, default=1)  # 1:可用, 0:不可用
    created_at = db.Column(db.DateTime, nullable=False, default=datetime.now)
    updated_at = db.Column(db.DateTime, nullable=False, default=datetime.now)
    # 添加与 InventoryLog 的关系
    inventory_logs = db.relationship('InventoryLog', backref='book', lazy='dynamic')
    def __repr__(self):
        return f''
================================================================================
File: ./app/models/borrow.py
================================================================================
from app.models.user import db
from datetime import datetime
class BorrowRecord(db.Model):
    __tablename__ = 'borrow_records'
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    book_id = db.Column(db.Integer, db.ForeignKey('books.id'), nullable=False)
    borrow_date = db.Column(db.DateTime, nullable=False, default=datetime.now)
    due_date = db.Column(db.DateTime, nullable=False)
    return_date = db.Column(db.DateTime, nullable=True)
    renew_count = db.Column(db.Integer, default=0)
    status = db.Column(db.Integer, default=1)  # 1: 借出, 0: 已归还
    remark = db.Column(db.String(255), nullable=True)
    created_at = db.Column(db.DateTime, nullable=False, default=datetime.now)
    updated_at = db.Column(db.DateTime, nullable=False, default=datetime.now)
    # 添加反向关系引用
    user = db.relationship('User', backref=db.backref('borrow_records', lazy='dynamic'))
    book = db.relationship('Book', backref=db.backref('borrow_records', lazy='dynamic'))
    # book 关系会在后面步骤添加
    def __repr__(self):
        return f''
================================================================================
File: ./app/models/announcement.py
================================================================================
from datetime import datetime
from app.models.user import db, User  # 从user模块导入db,而不是从app.models导入
class Announcement(db.Model):
    __tablename__ = 'announcements'
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(128), nullable=False)
    content = db.Column(db.Text, nullable=False)
    publisher_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    is_top = db.Column(db.Boolean, default=False)
    status = db.Column(db.Integer, default=1)  # 1-正常, 0-已下架
    created_at = db.Column(db.DateTime, nullable=False, default=datetime.now)
    updated_at = db.Column(db.DateTime, nullable=False, default=datetime.now, onupdate=datetime.now)
    # 关联关系
    publisher = db.relationship('User', backref='announcements')
    def to_dict(self):
        """将公告转换为字典"""
        return {
            'id': self.id,
            'title': self.title,
            'content': self.content,
            'publisher_id': self.publisher_id,
            'publisher_name': self.publisher.username if self.publisher else '',
            'is_top': self.is_top,
            'status': self.status,
            'created_at': self.created_at.strftime('%Y-%m-%d %H:%M:%S'),
            'updated_at': self.updated_at.strftime('%Y-%m-%d %H:%M:%S')
        }
    @staticmethod
    def get_active_announcements(limit=None):
        """获取活跃的公告"""
        query = Announcement.query.filter_by(status=1).order_by(
            Announcement.is_top.desc(),
            Announcement.created_at.desc()
        )
        if limit:
            query = query.limit(limit)
        return query.all()
    @staticmethod
    def get_announcement_by_id(announcement_id):
        """根据ID获取公告"""
        return Announcement.query.get(announcement_id)
    @staticmethod
    def create_announcement(title, content, publisher_id, is_top=False):
        """创建新公告"""
        announcement = Announcement(
            title=title,
            content=content,
            publisher_id=publisher_id,
            is_top=is_top
        )
        try:
            db.session.add(announcement)
            db.session.commit()
            return True, announcement
        except Exception as e:
            db.session.rollback()
            return False, str(e)
    @staticmethod
    def update_announcement(announcement_id, title, content, is_top=None):
        """更新公告内容"""
        announcement = Announcement.query.get(announcement_id)
        if not announcement:
            return False, "公告不存在"
        announcement.title = title
        announcement.content = content
        if is_top is not None:
            announcement.is_top = is_top
        try:
            db.session.commit()
            return True, announcement
        except Exception as e:
            db.session.rollback()
            return False, str(e)
    @staticmethod
    def change_status(announcement_id, status):
        """更改公告状态"""
        announcement = Announcement.query.get(announcement_id)
        if not announcement:
            return False, "公告不存在"
        announcement.status = status
        try:
            db.session.commit()
            return True, "状态已更新"
        except Exception as e:
            db.session.rollback()
            return False, str(e)
    @staticmethod
    def change_top_status(announcement_id, is_top):
        """更改置顶状态"""
        announcement = Announcement.query.get(announcement_id)
        if not announcement:
            return False, "公告不存在"
        announcement.is_top = is_top
        try:
            db.session.commit()
            return True, "置顶状态已更新"
        except Exception as e:
            db.session.rollback()
            return False, str(e)
================================================================================
File: ./app/models/inventory.py
================================================================================
from app.models.user import db
from datetime import datetime
class InventoryLog(db.Model):
    __tablename__ = 'inventory_logs'
    id = db.Column(db.Integer, primary_key=True)
    book_id = db.Column(db.Integer, db.ForeignKey('books.id'), nullable=False)
    change_type = db.Column(db.String(32), nullable=False)  # 'in' 入库, 'out' 出库
    change_amount = db.Column(db.Integer, nullable=False)
    after_stock = db.Column(db.Integer, nullable=False)
    operator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
    remark = db.Column(db.String(255), nullable=True)
    changed_at = db.Column(db.DateTime, nullable=False, default=datetime.now)
    # 添加反向关系引用
    operator = db.relationship('User', backref=db.backref('inventory_logs', lazy='dynamic'))
    # book 关系会在后面步骤添加
    def __repr__(self):
        return f''
================================================================================
File: ./app/static/css/log-detail.css
================================================================================
/* 日志详情样式 */
.content-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
}
.content-header h1 {
    margin: 0;
    font-size: 24px;
}
.log-info {
    padding: 10px;
}
.info-item {
    margin-bottom: 15px;
    display: flex;
}
.info-item .label {
    width: 100px;
    font-weight: 600;
    color: #495057;
}
.info-item .value {
    flex: 1;
}
.description {
    background-color: #f8f9fa;
    padding: 15px;
    border-radius: 5px;
    margin-top: 20px;
    display: block;
}
.description .label {
    display: block;
    width: 100%;
    margin-bottom: 10px;
}
.description .value {
    display: block;
    width: 100%;
    white-space: pre-wrap;
    word-break: break-word;
}
================================================================================
File: ./app/static/css/register.css
================================================================================
/* register.css - 注册页面专用样式 */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
}
:root {
    --primary-color: #4a89dc;
    --primary-hover: #3b78c4;
    --secondary-color: #5cb85c;
    --text-color: #333;
    --light-text: #666;
    --bg-color: #f5f7fa;
    --card-bg: #ffffff;
    --border-color: #ddd;
    --error-color: #e74c3c;
    --success-color: #2ecc71;
}
body.dark-mode {
    --primary-color: #5a9aed;
    --primary-hover: #4a89dc;
    --secondary-color: #6bc76b;
    --text-color: #f1f1f1;
    --light-text: #aaa;
    --bg-color: #1a1a1a;
    --card-bg: #2c2c2c;
    --border-color: #444;
}
body {
    background-color: var(--bg-color);
    background-image: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
    background-size: cover;
    background-position: center;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    color: var(--text-color);
    transition: all 0.3s ease;
}
.theme-toggle {
    position: absolute;
    top: 20px;
    right: 20px;
    z-index: 10;
    cursor: pointer;
    padding: 8px;
    border-radius: 50%;
    background-color: rgba(255, 255, 255, 0.2);
    backdrop-filter: blur(5px);
    border: 1px solid rgba(255, 255, 255, 0.1);
}
.overlay {
    background-color: rgba(0, 0, 0, 0.4);
    backdrop-filter: blur(5px);
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: -1;
}
.main-container {
    display: flex;
    justify-content: center;
    align-items: center;
    flex: 1;
    padding: 20px;
}
.login-container {
    background-color: var(--card-bg);
    border-radius: 12px;
    box-shadow: 0 5px 20px rgba(0, 0, 0, 0.15);
    width: 450px;
    padding: 35px;
    position: relative;
    overflow: hidden;
    animation: fadeIn 0.5s ease;
}
.register-container {
    width: 500px;
}
@keyframes fadeIn {
    from { opacity: 0; transform: translateY(20px); }
    to { opacity: 1; transform: translateY(0); }
}
.logo {
    text-align: center;
    margin-bottom: 25px;
    position: relative;
}
.logo img {
    width: 90px;
    height: 90px;
    border-radius: 12px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    padding: 5px;
    background-color: #fff;
    transition: transform 0.3s ease;
}
h1 {
    text-align: center;
    color: var(--text-color);
    margin-bottom: 10px;
    font-weight: 600;
    font-size: 28px;
}
.subtitle {
    text-align: center;
    color: var(--light-text);
    margin-bottom: 30px;
    font-size: 14px;
}
.form-group {
    margin-bottom: 22px;
    position: relative;
}
.form-group label {
    display: block;
    margin-bottom: 8px;
    color: var(--text-color);
    font-weight: 500;
    font-size: 14px;
}
.input-with-icon {
    position: relative;
}
.input-icon {
    position: absolute;
    left: 15px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--light-text);
}
.form-control {
    width: 100%;
    height: 48px;
    border: 1px solid var(--border-color);
    border-radius: 6px;
    padding: 0 15px 0 45px;
    font-size: 15px;
    transition: all 0.3s ease;
    background-color: var(--card-bg);
    color: var(--text-color);
}
.form-control:focus {
    border-color: var(--primary-color);
    box-shadow: 0 0 0 3px rgba(74, 137, 220, 0.2);
    outline: none;
}
.password-toggle {
    position: absolute;
    right: 15px;
    top: 50%;
    transform: translateY(-50%);
    cursor: pointer;
    color: var(--light-text);
}
.validation-message {
    margin-top: 6px;
    font-size: 12px;
    color: var(--error-color);
    display: none;
}
.validation-message.show {
    display: block;
    animation: shake 0.5s ease;
}
@keyframes shake {
    0%, 100% { transform: translateX(0); }
    10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
    20%, 40%, 60%, 80% { transform: translateX(5px); }
}
.btn-login {
    width: 100%;
    height: 48px;
    background-color: var(--primary-color);
    color: white;
    border: none;
    border-radius: 6px;
    font-size: 16px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.3s ease;
    position: relative;
    overflow: hidden;
}
.btn-login:hover {
    background-color: var(--primary-hover);
}
.btn-login:active {
    transform: scale(0.98);
}
.btn-login .loading {
    display: none;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}
.btn-login.loading-state {
    color: transparent;
}
.btn-login.loading-state .loading {
    display: block;
}
.signup {
    text-align: center;
    margin-top: 25px;
    font-size: 14px;
    color: var(--light-text);
}
.signup a {
    color: var(--primary-color);
    text-decoration: none;
    font-weight: 600;
    transition: color 0.3s ease;
}
.signup a:hover {
    color: var(--primary-hover);
    text-decoration: underline;
}
.alert {
    padding: 10px;
    margin-bottom: 15px;
    border-radius: 4px;
    color: #721c24;
    background-color: #f8d7da;
    border: 1px solid #f5c6cb;
}
.verification-code-container {
    display: flex;
    gap: 10px;
}
.verification-input {
    flex: 1;
    height: 48px;
    border: 1px solid var(--border-color);
    border-radius: 6px;
    padding: 0 15px;
    font-size: 15px;
    transition: all 0.3s ease;
    background-color: var(--card-bg);
    color: var(--text-color);
}
.send-code-btn {
    padding: 0 15px;
    background-color: var(--primary-color);
    color: white;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
    white-space: nowrap;
    transition: all 0.3s ease;
}
.send-code-btn:hover {
    background-color: var(--primary-hover);
}
.send-code-btn:disabled {
    background-color: #ccc;
    cursor: not-allowed;
}
footer {
    text-align: center;
    padding: 20px;
    color: rgba(255, 255, 255, 0.7);
    font-size: 12px;
}
footer a {
    color: rgba(255, 255, 255, 0.9);
    text-decoration: none;
}
@media (max-width: 576px) {
    .login-container, .register-container {
        width: 100%;
        padding: 25px;
        border-radius: 0;
    }
    .theme-toggle {
        top: 10px;
    }
    .logo img {
        width: 70px;
        height: 70px;
    }
    h1 {
        font-size: 22px;
    }
    .main-container {
        padding: 0;
    }
    .verification-code-container {
        flex-direction: column;
    }
}
================================================================================
File: ./app/static/css/inventory-book-logs.css
================================================================================
/* 冰雪奇缘主题库存日志页面样式 */
/* 基础背景与字体 */
body {
    font-family: 'Arial Rounded MT Bold', 'Helvetica Neue', Arial, sans-serif;
    background-color: #e6f2ff;
    color: #2c3e50;
}
/* 冰雪背景 */
.frozen-background {
    position: relative;
    min-height: 100vh;
    padding: 30px 0 50px;
    background: linear-gradient(135deg, #e4f1fe, #d4e6fb, #c9e0ff);
    overflow: hidden;
}
/* 雪花效果 */
.snowflakes {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    z-index: 1;
}
.snowflake {
    position: absolute;
    color: #fff;
    font-size: 1.5em;
    opacity: 0.8;
    top: -20px;
    animation: snowfall linear infinite;
}
.snowflake:nth-child(1) { left: 10%; animation-duration: 15s; animation-delay: 0s; }
.snowflake:nth-child(2) { left: 20%; animation-duration: 12s; animation-delay: 1s; }
.snowflake:nth-child(3) { left: 30%; animation-duration: 13s; animation-delay: 2s; }
.snowflake:nth-child(4) { left: 40%; animation-duration: 10s; animation-delay: 0s; }
.snowflake:nth-child(5) { left: 50%; animation-duration: 16s; animation-delay: 3s; }
.snowflake:nth-child(6) { left: 60%; animation-duration: 14s; animation-delay: 1s; }
.snowflake:nth-child(7) { left: 70%; animation-duration: 12s; animation-delay: 0s; }
.snowflake:nth-child(8) { left: 80%; animation-duration: 15s; animation-delay: 2s; }
.snowflake:nth-child(9) { left: 90%; animation-duration: 13s; animation-delay: 1s; }
.snowflake:nth-child(10) { left: 95%; animation-duration: 14s; animation-delay: 3s; }
@keyframes snowfall {
    0% {
        transform: translateY(0) rotate(0deg);
    }
    100% {
        transform: translateY(100vh) rotate(360deg);
    }
}
/* 冰雪主题卡片 */
.frozen-card {
    position: relative;
    background-color: rgba(255, 255, 255, 0.85);
    border-radius: 20px;
    box-shadow: 0 10px 30px rgba(79, 149, 255, 0.2);
    backdrop-filter: blur(10px);
    border: 2px solid #e1f0ff;
    margin-bottom: 40px;
    overflow: hidden;
    z-index: 2;
}
/* 城堡装饰 */
.castle-decoration {
    position: absolute;
    top: -40px;
    right: 30px;
    width: 120px;
    height: 120px;
    background-image: url('https://i.imgur.com/KkMfwWv.png');
    background-size: contain;
    background-repeat: no-repeat;
    opacity: 0.6;
    z-index: 1;
    transform: rotate(10deg);
    filter: hue-rotate(190deg);
}
/* 卡片标题栏 */
.card-header-frozen {
    background: linear-gradient(45deg, #7AB6FF, #94C5FF);
    color: #fff;
    padding: 1.5rem;
    border-radius: 18px 18px 0 0;
    text-align: center;
    position: relative;
    text-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
    display: flex;
    align-items: center;
    justify-content: center;
}
.card-header-frozen h4 {
    font-weight: 700;
    margin: 0;
    font-size: 1.6rem;
    z-index: 1;
}
.card-header-frozen i {
    margin-right: 10px;
}
/* 冰晶装饰 */
.ice-crystal {
    position: absolute;
    width: 50px;
    height: 50px;
    background-image: url('https://i.imgur.com/8vZuwlG.png');
    background-size: contain;
    background-repeat: no-repeat;
    filter: brightness(1.2) hue-rotate(190deg);
}
.ice-crystal.left {
    left: 20px;
    transform: rotate(-30deg) scale(0.8);
}
.ice-crystal.right {
    right: 20px;
    transform: rotate(30deg) scale(0.8);
}
/* 卡片内容区 */
.card-body-frozen {
    padding: 2.5rem;
    position: relative;
    z-index: 2;
}
/* 书籍基本信息区域 */
.book-info-row {
    background: linear-gradient(to right, rgba(232, 244, 255, 0.7), rgba(216, 234, 255, 0.4));
    border-radius: 15px;
    padding: 20px;
    margin-bottom: 30px !important;
    box-shadow: 0 5px 15px rgba(79, 149, 255, 0.1);
    position: relative;
    overflow: hidden;
}
/* 书籍封面 */
.book-cover-container {
    display: flex;
    justify-content: center;
    align-items: center;
}
.book-frame {
    position: relative;
    padding: 10px;
    background-color: white;
    border-radius: 10px;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
    transform: rotate(-3deg);
    transition: transform 0.5s ease;
    z-index: 1;
}
.book-frame:hover {
    transform: rotate(0deg) scale(1.05);
}
.book-cover {
    max-height: 250px;
    width: auto;
    object-fit: contain;
    border-radius: 5px;
    transform: rotate(3deg);
    transition: transform 0.5s ease;
}
.book-frame:hover .book-cover {
    transform: rotate(0deg);
}
.book-glow {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: radial-gradient(circle at 50% 50%, rgba(173, 216, 230, 0.4), rgba(173, 216, 230, 0) 70%);
    opacity: 0;
    transition: opacity 0.5s ease;
    pointer-events: none;
}
.book-frame:hover .book-glow {
    opacity: 1;
}
/* 书籍详情 */
.book-details {
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.book-title {
    color: #4169e1;
    font-weight: 700;
    margin-bottom: 20px;
    font-size: 1.8rem;
    position: relative;
    display: inline-block;
}
.book-title::after {
    content: "";
    position: absolute;
    bottom: -10px;
    left: 0;
    width: 100%;
    height: 3px;
    background: linear-gradient(to right, #7AB6FF, transparent);
    border-radius: 3px;
}
.book-info {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 15px;
}
.info-item {
    margin: 0;
    display: flex;
    align-items: center;
    font-size: 1.1rem;
    color: #34495e;
}
.info-item i {
    color: #7AB6FF;
    margin-right: 10px;
    font-size: 1.2rem;
    width: 24px;
    text-align: center;
}
/* 库存标签 */
.frozen-badge {
    display: inline-block;
    padding: 0.35em 0.9em;
    border-radius: 50px;
    font-weight: 600;
    margin-left: 8px;
    font-size: 0.95rem;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.high-stock {
    background: linear-gradient(45deg, #e0f7fa, #b3e5fc);
    color: #0277bd;
    border: 1px solid #81d4fa;
}
.low-stock {
    background: linear-gradient(45deg, #fff8e1, #ffecb3);
    color: #ff8f00;
    border: 1px solid #ffe082;
}
.out-stock {
    background: linear-gradient(45deg, #ffebee, #ffcdd2);
    color: #c62828;
    border: 1px solid #ef9a9a;
}
/* 历史记录区域 */
.history-section {
    position: relative;
    margin-top: 40px;
}
.section-title {
    color: #4169e1;
    font-weight: 700;
    font-size: 1.4rem;
    margin-bottom: 25px;
    position: relative;
    display: inline-block;
}
.section-title i {
    margin-right: 10px;
    color: #7AB6FF;
}
.magic-underline {
    position: absolute;
    bottom: -8px;
    left: 0;
    width: 100%;
    height: 3px;
    background: linear-gradient(to right, #7AB6FF, transparent);
    animation: sparkle 2s infinite;
}
@keyframes sparkle {
    0%, 100% { opacity: 0.5; }
    50% { opacity: 1; }
}
/* 自定义表格 */
.table-container {
    position: relative;
    margin-bottom: 30px;
    border-radius: 10px;
    overflow: hidden;
    box-shadow: 0 5px 15px rgba(79, 149, 255, 0.1);
}
.table-frozen {
    width: 100%;
    background-color: white;
    border-collapse: collapse;
}
.table-header-row {
    display: grid;
    grid-template-columns: 0.5fr 1fr 0.8fr 0.8fr 1fr 2fr 1.5fr;
    background: linear-gradient(45deg, #5e81ac, #81a1c1);
    color: white;
    font-weight: 600;
}
.th-frozen {
    padding: 15px;
    text-align: center;
    position: relative;
}
.th-frozen:not(:last-child)::after {
    content: "";
    position: absolute;
    right: 0;
    top: 20%;
    height: 60%;
    width: 1px;
    background-color: rgba(255, 255, 255, 0.3);
}
.table-body {
    max-height: 500px;
    overflow-y: auto;
}
.table-row {
    display: grid;
    grid-template-columns: 0.5fr 1fr 0.8fr 0.8fr 1fr 2fr 1.5fr;
    border-bottom: 1px solid #ecf0f1;
    transition: all 0.3s ease;
    cursor: pointer;
    position: relative;
    overflow: hidden;
}
.table-row:hover {
    background-color: #f0f8ff;
    transform: translateY(-2px);
    box-shadow: 0 5px 10px rgba(79, 149, 255, 0.1);
}
.table-row::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    width: 4px;
    background: linear-gradient(to bottom, #7AB6FF, #5e81ac);
    opacity: 0;
    transition: opacity 0.3s ease;
}
.table-row:hover::before {
    opacity: 1;
}
.td-frozen {
    padding: 15px;
    text-align: center;
    display: flex;
    align-items: center;
    justify-content: center;
}
.remark-cell {
    text-align: left;
    justify-content: flex-start;
    font-style: italic;
    color: #7f8c8d;
}
/* 表格中的徽章 */
.operation-badge {
    display: inline-flex;
    align-items: center;
    padding: 5px 12px;
    border-radius: 50px;
    font-weight: 600;
    font-size: 0.9rem;
}
.operation-badge i {
    margin-left: 5px;
}
.in-badge {
    background: linear-gradient(45deg, #e0f7fa, #b3e5fc);
    color: #0277bd;
    border: 1px solid #81d4fa;
}
.out-badge {
    background: linear-gradient(45deg, #fff8e1, #ffecb3);
    color: #ff8f00;
    border: 1px solid #ffe082;
}
/* 奥拉夫空状态 */
.empty-log {
    grid-template-columns: 1fr !important;
    height: 250px;
}
.empty-message {
    grid-column: span 7;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
}
.olaf-empty {
    text-align: center;
}
.olaf-image {
    width: 120px;
    height: 150px;
    background-image: url('https://i.imgur.com/lM0cLxb.png');
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
    margin: 0 auto 15px;
    animation: olaf-wave 3s infinite;
}
@keyframes olaf-wave {
    0%, 100% { transform: rotate(-5deg); }
    50% { transform: rotate(5deg); }
}
.olaf-empty p {
    font-size: 1.2rem;
    color: #7f8c8d;
    margin: 0;
}
/* 特殊的行样式 */
.log-entry[data-type="in"] {
    background-color: rgba(224, 247, 250, 0.2);
}
.log-entry[data-type="out"] {
    background-color: rgba(255, 248, 225, 0.2);
}
/* 分页容器 */
.pagination-container {
    margin-top: 30px;
    margin-bottom: 10px;
}
.frozen-pagination {
    display: flex;
    padding-left: 0;
    list-style: none;
    justify-content: center;
    gap: 5px;
}
.frozen-pagination .page-item {
    margin: 0 2px;
}
.frozen-pagination .page-link {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 8px 16px;
    color: #4169e1;
    background-color: white;
    border: 1px solid #e1f0ff;
    border-radius: 50px;
    text-decoration: none;
    transition: all 0.3s ease;
    min-width: 40px;
}
.frozen-pagination .page-link:hover {
    background-color: #e1f0ff;
    color: #2c3e50;
    transform: translateY(-2px);
    box-shadow: 0 5px 10px rgba(79, 149, 255, 0.1);
}
.frozen-pagination .page-item.active .page-link {
    background: linear-gradient(45deg, #7AB6FF, #5e81ac);
    color: white;
    border-color: #5e81ac;
}
.frozen-pagination .page-item.disabled .page-link {
    color: #95a5a6;
    background-color: #f8f9fa;
    cursor: not-allowed;
}
/* 页脚 */
.card-footer-frozen {
    background: linear-gradient(45deg, #ecf5ff, #d8e6ff);
    padding: 1.5rem;
    border-radius: 0 0 18px 18px;
    position: relative;
}
.footer-actions {
    display: flex;
    justify-content: space-between;
    position: relative;
    z-index: 2;
}
.footer-decoration {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 15px;
    background-image: url('https://i.imgur.com/KkMfwWv.png');
    background-size: 50px;
    background-repeat: repeat-x;
    opacity: 0.2;
    filter: hue-rotate(190deg);
}
/* 冰雪风格按钮 */
.frozen-btn {
    padding: 10px 20px;
    border-radius: 50px;
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: all 0.3s ease;
    position: relative;
    overflow: hidden;
    border: none;
    color: white;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.frozen-btn i {
    margin-right: 8px;
}
.return-btn {
    background: linear-gradient(45deg, #81a1c1, #5e81ac);
}
.return-btn:hover {
    background: linear-gradient(45deg, #5e81ac, #4c6f94);
    transform: translateY(-3px);
    box-shadow: 0 8px 15px rgba(94, 129, 172, 0.3);
    color: white;
}
.adjust-btn {
    background: linear-gradient(45deg, #7AB6FF, #5d91e5);
}
.adjust-btn:hover {
    background: linear-gradient(45deg, #5d91e5, #4169e1);
    transform: translateY(-3px);
    box-shadow: 0 8px 15px rgba(65, 105, 225, 0.3);
    color: white;
}
.frozen-btn::after {
    content: "";
    position: absolute;
    top: -50%;
    left: -50%;
    width: 200%;
    height: 200%;
    background: rgba(255, 255, 255, 0.1);
    transform: rotate(45deg);
    transition: all 0.3s ease;
    opacity: 0;
}
.frozen-btn:hover::after {
    opacity: 1;
    transform: rotate(45deg) translateY(-50%);
}
/* 动画类 */
.fade-in {
    animation: fadeIn 0.5s ease forwards;
    opacity: 0;
}
@keyframes fadeIn {
    from { opacity: 0; transform: translateY(20px); }
    to { opacity: 1; transform: translateY(0); }
}
.selected-row {
    background-color: #e3f2fd !important;
    position: relative;
    z-index: 1;
}
.selected-row::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(to right, rgba(122, 182, 255, 0.1), transparent);
    pointer-events: none;
}
/* 响应式调整 */
@media (max-width: 992px) {
    .table-header-row,
    .table-row {
        grid-template-columns: 0.5fr 1fr 0.8fr 0.8fr 1fr 1.2fr 1.2fr;
    }
    .book-info {
        grid-template-columns: 1fr;
    }
}
@media (max-width: 768px) {
    .book-cover-container {
        margin-bottom: 30px;
    }
    .book-frame {
        transform: rotate(0);
        max-width: 180px;
    }
    .book-cover {
        transform: rotate(0);
        max-height: 200px;
    }
    .book-title {
        text-align: center;
        font-size: 1.5rem;
    }
    .table-header-row,
    .table-row {
        display: flex;
        flex-direction: column;
    }
    .th-frozen:after {
        display: none;
    }
    .th-frozen {
        text-align: left;
        padding: 10px 15px;
        border-bottom: 1px solid rgba(255, 255, 255, 0.2);
    }
    .td-frozen {
        justify-content: flex-start;
        padding: 10px 15px;
        border-bottom: 1px solid #ecf0f1;
    }
    .td-frozen:before {
        content: attr(data-label);
        font-weight: 600;
        margin-right: 10px;
        color: #7f8c8d;
    }
    .footer-actions {
        flex-direction: column;
        gap: 15px;
    }
    .frozen-btn {
        width: 100%;
    }
}
================================================================================
File: ./app/static/css/user-list.css
================================================================================
/* 用户列表页面样式 */
.user-list-container {
    padding: 20px;
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
/* 页面标题和操作按钮 */
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 25px;
    padding-bottom: 15px;
    border-bottom: 1px solid #f0f0f0;
}
.page-header h1 {
    font-size: 1.8rem;
    color: #333;
    margin: 0;
}
.page-header .actions {
    display: flex;
    gap: 10px;
}
/* 搜索和筛选区域 */
.search-filter-container {
    margin-bottom: 20px;
    padding: 20px;
    background-color: #f9f9f9;
    border-radius: 6px;
}
.search-filter-form .form-row {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 15px;
}
.search-box {
    position: relative;
    flex: 1;
    min-width: 250px;
}
.search-box input {
    padding-right: 40px;
    border-radius: 4px;
    border: 1px solid #ddd;
}
.btn-search {
    position: absolute;
    right: 5px;
    top: 5px;
    background: none;
    border: none;
    color: #666;
}
.filter-box {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}
.filter-box select {
    min-width: 120px;
    border-radius: 4px;
    border: 1px solid #ddd;
    padding: 5px 10px;
}
.btn-filter, .btn-reset {
    padding: 6px 15px;
    border-radius: 4px;
}
.btn-filter {
    background-color: #4c84ff;
    color: white;
    border: none;
}
.btn-reset {
    background-color: #f8f9fa;
    color: #333;
    border: 1px solid #ddd;
}
/* 表格样式 */
.table {
    width: 100%;
    margin-bottom: 0;
    color: #333;
    border-collapse: collapse;
}
.table th {
    background-color: #f8f9fa;
    padding: 12px 15px;
    font-weight: 600;
    text-align: left;
    border-top: 1px solid #dee2e6;
    border-bottom: 1px solid #dee2e6;
}
.table td {
    padding: 12px 15px;
    vertical-align: middle;
    border-bottom: 1px solid #f0f0f0;
}
.table tr:hover {
    background-color: #f8f9fa;
}
/* 状态标签 */
.status-badge {
    display: inline-block;
    padding: 5px 10px;
    border-radius: 20px;
    font-size: 0.85rem;
    font-weight: 500;
}
.status-badge.active {
    background-color: #e8f5e9;
    color: #43a047;
}
.status-badge.inactive {
    background-color: #ffebee;
    color: #e53935;
}
/* 操作按钮 */
.actions {
    display: flex;
    gap: 5px;
    align-items: center;
}
.actions .btn {
    padding: 5px 8px;
    line-height: 1;
}
/* 分页控件 */
.pagination-container {
    margin-top: 20px;
    display: flex;
    justify-content: center;
}
.pagination {
    display: flex;
    padding-left: 0;
    list-style: none;
    border-radius: 0.25rem;
}
.page-item {
    margin: 0 2px;
}
.page-link {
    position: relative;
    display: block;
    padding: 0.5rem 0.75rem;
    margin-left: -1px;
    color: #4c84ff;
    background-color: #fff;
    border: 1px solid #dee2e6;
    text-decoration: none;
}
.page-item.active .page-link {
    z-index: 3;
    color: #fff;
    background-color: #4c84ff;
    border-color: #4c84ff;
}
.page-item.disabled .page-link {
    color: #aaa;
    pointer-events: none;
    background-color: #f8f9fa;
    border-color: #dee2e6;
}
/* 通知样式 */
.alert-box {
    position: fixed;
    top: 20px;
    right: 20px;
    z-index: 1050;
}
.alert-box .alert {
    margin-bottom: 10px;
    padding: 10px 15px;
    border-radius: 4px;
    opacity: 0;
    transition: opacity 0.3s ease-in-out;
}
.alert-box .fade-in {
    opacity: 1;
}
.alert-box .fade-out {
    opacity: 0;
}
/* 响应式调整 */
@media (max-width: 992px) {
    .search-filter-form .form-row {
        flex-direction: column;
    }
    .search-box, .filter-box {
        width: 100%;
    }
}
@media (max-width: 768px) {
    .table {
        display: block;
        overflow-x: auto;
    }
    .page-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 15px;
    }
}
================================================================================
File: ./app/static/css/book_ranking.css
================================================================================
/* app/static/css/book_ranking.css */
.table-container {
    margin-top: 30px;
}
.table-container h3 {
    text-align: center;
    margin-bottom: 20px;
    color: var(--accent-color);
    font-family: 'Ma Shan Zheng', cursive, Arial, sans-serif;
    font-size: 1.6em;
    position: relative;
    display: inline-block;
    left: 50%;
    transform: translateX(-50%);
}
.table-container h3:before,
.table-container h3:after {
    content: '';
    position: absolute;
    height: 2px;
    background: linear-gradient(to right, transparent, var(--primary-color), transparent);
    width: 120px;
    top: 50%;
}
.table-container h3:before {
    right: 100%;
    margin-right: 15px;
}
.table-container h3:after {
    left: 100%;
    margin-left: 15px;
}
.data-table img {
    width: 55px;
    height: 80px;
    object-fit: cover;
    border-radius: 8px;
    box-shadow: 0 3px 10px rgba(0,0,0,0.1);
    transition: transform 0.3s ease, box-shadow 0.3s ease;
    border: 2px solid white;
}
.data-table tr:hover img {
    transform: scale(1.08);
    box-shadow: 0 5px 15px rgba(0,0,0,0.15);
    border-color: var(--primary-color);
}
.data-table .rank {
    font-weight: 700;
    text-align: center;
    position: relative;
}
/* 前三名特殊样式 */
.data-table tr:nth-child(1) .rank:before {
    content: '👑';
    position: absolute;
    top: -15px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 18px;
}
.data-table tr:nth-child(2) .rank:before {
    content: '✨';
    position: absolute;
    top: -15px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 16px;
}
.data-table tr:nth-child(3) .rank:before {
    content: '🌟';
    position: absolute;
    top: -15px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 16px;
}
.data-table .book-title {
    font-weight: 500;
    color: var(--accent-color);
    transition: color 0.3s;
}
.data-table tr:hover .book-title {
    color: #d06b9c;
}
.data-table .author {
    font-style: italic;
    color: var(--light-text);
}
.data-table .borrow-count {
    font-weight: 600;
    color: var(--accent-color);
    position: relative;
    display: block; /* 修改为block以占据整个单元格 */
    text-align: center; /* 确保文本居中 */
}
.data-table .borrow-count:after {
    content: '❤️';
    font-size: 12px;
    margin-left: 5px;
    opacity: 0;
    transition: opacity 0.3s ease, transform 0.3s ease;
    transform: translateY(5px);
    display: inline-block;
}
.data-table tr:hover .borrow-count:after {
    opacity: 1;
    transform: translateY(0);
}
.no-data {
    text-align: center;
    padding: 40px;
    color: var(--light-text);
    background-color: var(--secondary-color);
    border-radius: 12px;
    font-style: italic;
    border: 1px dashed var(--border-color);
}
/* 书籍行动画 */
#ranking-table-body tr {
    transition: transform 0.3s ease, opacity 0.3s ease;
}
#ranking-table-body tr:hover {
    transform: translateX(5px);
}
/* 加载动画美化 */
.loading-row td {
    background-color: var(--secondary-color);
    color: var(--accent-color);
    font-size: 16px;
}
/* 书名悬停效果 */
.book-title {
    position: relative;
    text-decoration: none;
    display: inline-block;
}
.book-title:after {
    content: '';
    position: absolute;
    width: 100%;
    height: 2px;
    bottom: -2px;
    left: 0;
    background-color: var(--accent-color);
    transform: scaleX(0);
    transform-origin: bottom right;
    transition: transform 0.3s ease-out;
}
tr:hover .book-title:after {
    transform: scaleX(1);
    transform-origin: bottom left;
}
/* 特殊效果:波浪下划线 */
@keyframes wave {
    0%, 100% { background-position-x: 0%; }
    50% { background-position-x: 100%; }
}
.page-title:after {
    content: '';
    display: block;
    width: 100px;
    height: 5px;
    margin: 10px auto 0;
    background: linear-gradient(90deg, var(--primary-color), var(--accent-color), var(--primary-color));
    background-size: 200% 100%;
    border-radius: 5px;
    animation: wave 3s infinite linear;
}
.book-list-title {
    text-align: center;
    margin-bottom: 25px;
    color: var(--accent-color);
    font-family: 'Ma Shan Zheng', cursive, Arial, sans-serif;
    font-size: 1.6em;
    position: relative;
    display: inline-block;
    left: 50%;
    transform: translateX(-50%);
    padding: 0 15px;
}
.book-icon {
    font-size: 0.9em;
    margin: 0 8px;
    opacity: 0.85;
}
.column-icon {
    font-size: 0.9em;
    margin-right: 5px;
    opacity: 0.8;
}
.book-list-title:before,
.book-list-title:after {
    content: '';
    position: absolute;
    height: 2px;
    background: linear-gradient(to right, transparent, var(--primary-color), transparent);
    width: 80px;
    top: 50%;
}
.book-list-title:before {
    right: 100%;
    margin-right: 15px;
}
.book-list-title:after {
    left: 100%;
    margin-left: 15px;
}
/* 表格中的图标样式 */
.data-table .borrow-count:after {
    content: '📚';
    font-size: 12px;
    margin-left: 5px;
    opacity: 0;
    transition: opacity 0.3s ease, transform 0.3s ease;
    transform: translateY(5px);
    display: inline-block;
}
/* 前三名特殊样式 - 替换这部分代码 */
.data-table tr:nth-child(1) .rank:before,
.data-table tr:nth-child(2) .rank:before,
.data-table tr:nth-child(3) .rank:before {
    position: absolute;
    left: 10px; /* 调整到数字左侧 */
    top: 50%; /* 垂直居中 */
    transform: translateY(-50%); /* 保持垂直居中 */
    opacity: 0.9;
}
/* 分别设置每个奖牌的内容 */
.data-table tr:nth-child(1) .rank:before {
    content: '🏆';
    font-size: 18px;
}
.data-table tr:nth-child(2) .rank:before {
    content: '🥈';
    font-size: 16px;
}
.data-table tr:nth-child(3) .rank:before {
    content: '🥉';
    font-size: 16px;
}
/* 调整排名单元格的内边距,为图标留出空间 */
.data-table .rank {
    padding-left: 35px; /* 增加左内边距为图标腾出空间 */
    text-align: left; /* 使数字左对齐 */
}
/* 加载动画美化 */
.loading-animation {
    display: flex;
    align-items: center;
    justify-content: center;
}
.loading-animation:before {
    content: '📖';
    margin-right: 10px;
    animation: bookFlip 2s infinite;
    display: inline-block;
}
@keyframes bookFlip {
    0% { transform: rotateY(0deg); }
    50% { transform: rotateY(180deg); }
    100% { transform: rotateY(360deg); }
}
================================================================================
File: ./app/static/css/book-detail.css
================================================================================
/* 图书详情页样式 */
.book-detail-container {
    padding: 20px;
}
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
    padding-bottom: 15px;
    border-bottom: 1px solid #eee;
}
.actions {
    display: flex;
    gap: 10px;
}
.book-content {
    background-color: white;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.05);
    overflow: hidden;
}
.book-header {
    display: flex;
    padding: 25px;
    border-bottom: 1px solid #f0f0f0;
    background-color: #f9f9f9;
}
.book-cover-large {
    flex: 0 0 200px;
    height: 300px;
    background-color: #f0f0f0;
    border-radius: 5px;
    overflow: hidden;
    box-shadow: 0 4px 8px rgba(0,0,0,0.1);
    margin-right: 30px;
}
.book-cover-large img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.no-cover-large {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    color: #aaa;
}
.no-cover-large i {
    font-size: 48px;
    margin-bottom: 10px;
}
.book-main-info {
    flex: 1;
}
.book-title {
    font-size: 1.8rem;
    font-weight: 600;
    margin-bottom: 15px;
    color: #333;
}
.book-author {
    font-size: 1.1rem;
    color: #555;
    margin-bottom: 20px;
}
.book-meta-info {
    margin-bottom: 25px;
}
.meta-item {
    display: flex;
    align-items: center;
    margin-bottom: 12px;
    color: #666;
}
.meta-item i {
    width: 20px;
    margin-right: 10px;
    text-align: center;
    color: #555;
}
.meta-value {
    font-weight: 500;
    color: #444;
}
.tag {
    display: inline-block;
    background-color: #e9ecef;
    color: #495057;
    padding: 2px 8px;
    border-radius: 3px;
    margin-right: 5px;
    margin-bottom: 5px;
    font-size: 0.85rem;
}
.book-status-info {
    display: flex;
    align-items: center;
    gap: 20px;
    margin-top: 20px;
}
.status-badge {
    display: inline-block;
    padding: 8px 16px;
    border-radius: 4px;
    font-weight: 600;
    font-size: 0.9rem;
}
.status-badge.available {
    background-color: #d4edda;
    color: #155724;
}
.status-badge.unavailable {
    background-color: #f8d7da;
    color: #721c24;
}
.stock-info {
    font-size: 0.95rem;
    color: #555;
}
.book-details-section {
    padding: 25px;
}
.book-details-section h3 {
    font-size: 1.3rem;
    margin-bottom: 15px;
    padding-bottom: 10px;
    border-bottom: 1px solid #eee;
    color: #444;
}
.book-description {
    color: #555;
    line-height: 1.6;
}
.no-description {
    color: #888;
    font-style: italic;
}
.book-borrow-history {
    padding: 0 25px 25px;
}
.book-borrow-history h3 {
    font-size: 1.3rem;
    margin-bottom: 15px;
    padding-bottom: 10px;
    border-bottom: 1px solid #eee;
    color: #444;
}
.borrow-table {
    border: 1px solid #eee;
}
.no-records {
    color: #888;
    font-style: italic;
    text-align: center;
    padding: 20px;
    background-color: #f9f9f9;
    border-radius: 4px;
}
/* 响应式调整 */
@media (max-width: 768px) {
    .book-header {
        flex-direction: column;
    }
    .book-cover-large {
        margin-right: 0;
        margin-bottom: 20px;
        max-width: 200px;
        align-self: center;
    }
    .page-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 15px;
    }
    .actions {
        width: 100%;
    }
}
================================================================================
File: ./app/static/css/announcement-form.css
================================================================================
.announcement-form-container {
    padding: 20px;
    max-width: 900px;
    margin: 0 auto;
}
.page-header {
    margin-bottom: 25px;
    border-bottom: 1px solid #e3e3e3;
    padding-bottom: 10px;
}
.card {
    box-shadow: 0 2px 10px rgba(0,0,0,0.05);
    margin-bottom: 30px;
}
.form-group {
    margin-bottom: 1.5rem;
}
.form-group label {
    font-weight: 500;
    margin-bottom: 0.5rem;
    display: block;
}
.ql-container {
    min-height: 200px;
    font-size: 16px;
}
.form-check {
    margin-top: 20px;
    margin-bottom: 20px;
}
.form-buttons {
    display: flex;
    justify-content: flex-end;
    gap: 15px;
    margin-top: 30px;
}
.form-buttons .btn {
    min-width: 100px;
}
/* Quill编辑器样式重写 */
.ql-toolbar.ql-snow {
    border-top-left-radius: 4px;
    border-top-right-radius: 4px;
}
.ql-container.ql-snow {
    border-bottom-left-radius: 4px;
    border-bottom-right-radius: 4px;
}
================================================================================
File: ./app/static/css/book.css
================================================================================
/* 图书列表页面样式 - 女性友好版 */
/* 背景和泡泡动画 */
.book-list-container {
    padding: 24px;
    background-color: #ffeef2; /* 淡粉色背景 */
    min-height: calc(100vh - 60px);
    position: relative;
    overflow: hidden;
}
/* 泡泡动画 */
.book-list-container::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    pointer-events: none;
    z-index: 0;
}
@keyframes bubble {
    0% {
        transform: translateY(100%) scale(0);
        opacity: 0;
    }
    50% {
        opacity: 0.6;
    }
    100% {
        transform: translateY(-100vh) scale(1);
        opacity: 0;
    }
}
.bubble {
    position: absolute;
    bottom: -50px;
    background-color: rgba(255, 255, 255, 0.5);
    border-radius: 50%;
    z-index: 1;
    animation: bubble 15s infinite ease-in;
}
/* 为页面添加15个泡泡 */
.bubble:nth-child(1) { left: 5%; width: 30px; height: 30px; animation-duration: 20s; animation-delay: 0s; }
.bubble:nth-child(2) { left: 15%; width: 20px; height: 20px; animation-duration: 18s; animation-delay: 1s; }
.bubble:nth-child(3) { left: 25%; width: 25px; height: 25px; animation-duration: 16s; animation-delay: 2s; }
.bubble:nth-child(4) { left: 35%; width: 15px; height: 15px; animation-duration: 15s; animation-delay: 0.5s; }
.bubble:nth-child(5) { left: 45%; width: 30px; height: 30px; animation-duration: 14s; animation-delay: 3s; }
.bubble:nth-child(6) { left: 55%; width: 20px; height: 20px; animation-duration: 13s; animation-delay: 2.5s; }
.bubble:nth-child(7) { left: 65%; width: 25px; height: 25px; animation-duration: 12s; animation-delay: 1.5s; }
.bubble:nth-child(8) { left: 75%; width: 15px; height: 15px; animation-duration: 11s; animation-delay: 4s; }
.bubble:nth-child(9) { left: 85%; width: 30px; height: 30px; animation-duration: 10s; animation-delay: 3.5s; }
.bubble:nth-child(10) { left: 10%; width: 18px; height: 18px; animation-duration: 19s; animation-delay: 0.5s; }
.bubble:nth-child(11) { left: 20%; width: 22px; height: 22px; animation-duration: 17s; animation-delay: 2.5s; }
.bubble:nth-child(12) { left: 30%; width: 28px; height: 28px; animation-duration: 16s; animation-delay: 1.2s; }
.bubble:nth-child(13) { left: 40%; width: 17px; height: 17px; animation-duration: 15s; animation-delay: 3.7s; }
.bubble:nth-child(14) { left: 60%; width: 23px; height: 23px; animation-duration: 13s; animation-delay: 2.1s; }
.bubble:nth-child(15) { left: 80%; width: 19px; height: 19px; animation-duration: 12s; animation-delay: 1.7s; }
/* 页面标题部分 */
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 25px;
    padding-bottom: 15px;
    border-bottom: 1px solid rgba(233, 152, 174, 0.3);
    position: relative;
    z-index: 2;
}
.page-header h1 {
    color: #d23f6e;
    font-size: 1.9rem;
    font-weight: 600;
    margin: 0;
    text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.8);
}
/* 更漂亮的顶部按钮 */
.action-buttons {
    display: flex;
    gap: 12px;
    position: relative;
    z-index: 2;
}
.action-buttons .btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    border-radius: 50px;
    font-weight: 500;
    padding: 9px 18px;
    transition: all 0.3s ease;
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.06);
    border: none;
    font-size: 0.95rem;
    position: relative;
    overflow: hidden;
}
.action-buttons .btn::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: linear-gradient(to bottom, rgba(255, 255, 255, 0.2), transparent);
    pointer-events: none;
}
.action-buttons .btn:hover {
    transform: translateY(-3px);
    box-shadow: 0 6px 15px rgba(0, 0, 0, 0.12), 0 3px 6px rgba(0, 0, 0, 0.08);
}
.action-buttons .btn:active {
    transform: translateY(1px);
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
/* 按钮颜色 */
.btn-primary {
    background: linear-gradient(135deg, #5c88da, #4a73c7);
    color: white;
}
.btn-success {
    background: linear-gradient(135deg, #56c596, #41b384);
    color: white;
}
.btn-info {
    background: linear-gradient(135deg, #5bc0de, #46b8da);
    color: white;
}
.btn-secondary {
    background: linear-gradient(135deg, #f0ad4e, #ec971f);
    color: white;
}
.btn-danger {
    background: linear-gradient(135deg, #ff7676, #ff5252);
    color: white;
}
/* 过滤和搜索部分 */
.filter-section {
    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: 2;
    backdrop-filter: blur(5px);
}
.search-form {
    display: flex;
    flex-direction: column;
    gap: 16px;
}
.search-row {
    margin-bottom: 5px;
    width: 100%;
}
.search-group {
    display: flex;
    width: 100%;
    max-width: 800px;
}
.search-group .form-control {
    border: 1px solid #f9c0d0;
    border-right: none;
    border-radius: 25px 0 0 25px;
    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;
    flex: 1;
}
.search-group .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);
}
.search-group .btn {
    border-radius: 50%;
    width: 42px;
    height: 42px;
    min-width: 42px;
    padding: 0;
    background: linear-gradient(135deg, #e67e9f 60%, #ffd3e1 100%);
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-left: -1px;   /* 防止和输入框间有缝隙 */
    font-size: 1.1rem;
    box-shadow: 0 2px 6px rgba(230, 126, 159, 0.10);
    transition: background 0.2s, box-shadow 0.2s;
}
.search-group .btn:hover {
    background: linear-gradient(135deg, #d23f6e 80%, #efb6c6 100%);
    color: #fff;
    box-shadow: 0 4px 12px rgba(230, 126, 159, 0.14);
}
.filter-row {
    display: flex;
    flex-wrap: wrap;
    gap: 15px;
    width: 100%;
}
.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);
}
/* 图书网格布局 */
.books-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 24px;
    margin-bottom: 30px;
    position: relative;
    z-index: 2;
}
/* 图书卡片样式 */
.book-card {
    display: flex;
    flex-direction: column;
    border-radius: 16px;
    overflow: hidden;
    background-color: white;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.06);
    transition: all 0.3s ease;
    height: 100%;
    position: relative;
    border: 1px solid rgba(233, 152, 174, 0.2);
}
.book-card:hover {
    transform: translateY(-8px);
    box-shadow: 0 12px 25px rgba(0, 0, 0, 0.1);
}
.book-cover {
    width: 100%;
    height: 180px;
    background-color: #faf3f5;
    overflow: hidden;
    position: relative;
}
.book-cover::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: linear-gradient(to bottom, transparent 60%, rgba(249, 219, 227, 0.4));
    pointer-events: none;
}
.book-cover img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.5s ease;
}
.book-card:hover .book-cover img {
    transform: scale(1.05);
}
.no-cover {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background: linear-gradient(135deg, #ffeef2 0%, #ffd9e2 100%);
    color: #e67e9f;
    position: absolute;
    left: 0; right: 0; top: 0; bottom: 0;
    z-index: 1;
    pointer-events: none;
}
.no-cover i {
    font-size: 36px;
    margin-bottom: 10px;
}
.book-info {
    padding: 20px;
    display: flex;
    flex-direction: column;
    flex: 1;
}
.book-title {
    font-size: 1.1rem;
    font-weight: 600;
    margin: 0 0 10px;
    color: #d23f6e;
    line-height: 1.4;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
.book-author {
    font-size: 0.95rem;
    color: #888;
    margin-bottom: 15px;
}
.book-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 15px;
}
.book-category {
    padding: 4px 12px;
    border-radius: 20px;
    font-size: 0.8rem;
    background-color: #ffebf0;
    color: #e67e9f;
    font-weight: 500;
}
.book-status {
    padding: 4px 12px;
    border-radius: 20px;
    font-size: 0.8rem;
    font-weight: 500;
}
.book-status.available {
    background-color: #dffff6;
    color: #26a69a;
}
.book-status.unavailable {
    background-color: #ffeeee;
    color: #e57373;
}
.book-details {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 20px;
    font-size: 0.9rem;
    color: #777;
}
.book-details p {
    margin: 0;
    display: flex;
}
.book-details strong {
    min-width: 65px;
    color: #999;
    font-weight: 600;
}
/* 按钮组样式 */
.book-actions {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
    margin-top: auto;
}
.book-actions .btn {
    padding: 8px 0;
    font-size: 0.9rem;
    text-align: center;
    border-radius: 25px;
    transition: all 0.3s;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    border: none;
    font-weight: 500;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.book-actions .btn:hover {
    transform: translateY(-3px);
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.12);
}
.book-actions .btn i {
    font-size: 0.85rem;
}
/* 具体按钮颜色 */
.book-actions .btn-primary {
    background: linear-gradient(135deg, #5c88da, #4a73c7);
}
.book-actions .btn-info {
    background: linear-gradient(135deg, #5bc0de, #46b8da);
}
.book-actions .btn-success {
    background: linear-gradient(135deg, #56c596, #41b384);
}
.book-actions .btn-danger {
    background: linear-gradient(135deg, #ff7676, #ff5252);
}
/* 无图书状态 */
.no-books {
    grid-column: 1 / -1;
    padding: 50px 30px;
    text-align: center;
    background-color: white;
    border-radius: 16px;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
    position: relative;
    z-index: 2;
}
.no-books i {
    font-size: 60px;
    color: #f9c0d0;
    margin-bottom: 20px;
}
.no-books p {
    font-size: 1.1rem;
    color: #e67e9f;
    font-weight: 500;
}
/* 分页容器 */
.pagination-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-top: 30px;
    position: relative;
    z-index: 2;
}
.pagination {
    display: flex;
    list-style: none;
    padding: 0;
    margin: 0 0 15px 0;
    background-color: white;
    border-radius: 30px;
    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.08);
    overflow: hidden;
}
.pagination .page-item {
    margin: 0;
}
.pagination .page-link {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 40px;
    height: 40px;
    padding: 0 15px;
    border: none;
    color: #777;
    font-weight: 500;
    transition: all 0.2s;
    position: relative;
}
.pagination .page-link:hover {
    color: #e67e9f;
    background-color: #fff9fb;
}
.pagination .page-item.active .page-link {
    background-color: #e67e9f;
    color: white;
    box-shadow: none;
}
.pagination .page-item.disabled .page-link {
    color: #bbb;
    background-color: #f9f9f9;
}
.pagination-info {
    color: #999;
    font-size: 0.9rem;
}
/* 优化模态框样式 */
.modal-content {
    border-radius: 20px;
    border: none;
    box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1), 0 5px 15px rgba(0, 0, 0, 0.07);
    overflow: hidden;
}
.modal-header {
    padding: 20px 25px;
    background-color: #ffeef2;
    border-bottom: 1px solid #ffe0e9;
}
.modal-title {
    color: #d23f6e;
    font-size: 1.2rem;
    font-weight: 600;
}
.modal-body {
    padding: 25px;
}
.modal-footer {
    padding: 15px 25px;
    border-top: 1px solid #ffe0e9;
    background-color: #ffeef2;
}
.modal-body p {
    color: #666;
    font-size: 1rem;
    line-height: 1.6;
}
.modal-body p.text-danger {
    color: #ff5252 !important;
    font-weight: 500;
    display: flex;
    align-items: center;
    gap: 8px;
}
.modal-body p.text-danger::before {
    content: "\f06a";
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
}
.modal .close {
    font-size: 1.5rem;
    color: #e67e9f;
    opacity: 0.8;
    text-shadow: none;
    transition: all 0.2s;
}
.modal .close:hover {
    opacity: 1;
    color: #d23f6e;
}
.modal .btn {
    border-radius: 25px;
    padding: 8px 20px;
    font-weight: 500;
    box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1);
    border: none;
}
.modal .btn-secondary {
    background: linear-gradient(135deg, #a0a0a0, #808080);
    color: white;
}
.modal .btn-danger {
    background: linear-gradient(135deg, #ff7676, #ff5252);
    color: white;
}
/* 封面标题栏 */
.cover-title-bar {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    background: linear-gradient(0deg, rgba(233,152,174,0.92) 0%, rgba(255,255,255,0.08) 90%);
    color: #fff;
    font-size: 1rem;
    font-weight: bold;
    padding: 10px 14px 7px 14px;
    text-shadow: 0 2px 6px rgba(180,0,80,0.14);
    line-height: 1.3;
    width: 100%;
    box-sizing: border-box;
    display: flex;
    align-items: flex-end;
    min-height: 38px;
    z-index: 2;
}
.book-card:hover .cover-title-bar {
    background: linear-gradient(0deg, #d23f6e 0%, rgba(255,255,255,0.1) 100%);
    font-size: 1.07rem;
    letter-spacing: .5px;
}
/* 响应式调整 */
@media (max-width: 992px) {
    .filter-row {
        flex-wrap: wrap;
    }
    .filter-group {
        flex: 1 0 180px;
    }
}
@media (max-width: 768px) {
    .book-list-container {
        padding: 16px;
    }
    .page-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 15px;
    }
    .action-buttons {
        width: 100%;
        overflow-x: auto;
        padding-bottom: 8px;
        flex-wrap: nowrap;
        justify-content: flex-start;
    }
    .filter-section {
        padding: 15px;
    }
    .search-form {
        flex-direction: column;
        gap: 12px;
    }
    .search-group {
        max-width: 100%;
    }
    .filter-row {
        gap: 12px;
    }
    .books-grid {
        grid-template-columns: 1fr;
    }
    .book-actions {
        grid-template-columns: 1fr 1fr;
    }
}
@media (max-width: 600px) {
    .cover-title-bar {
        font-size: 0.95rem;
        min-height: 27px;
        padding: 8px 8px 5px 10px;
    }
    .book-actions {
        grid-template-columns: 1fr;
    }
}
================================================================================
File: ./app/static/css/login.css
================================================================================
/* login.css - 登录页面专用样式 */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
}
:root {
    --primary-color: #4a89dc;
    --primary-hover: #3b78c4;
    --secondary-color: #5cb85c;
    --text-color: #333;
    --light-text: #666;
    --bg-color: #f5f7fa;
    --card-bg: #ffffff;
    --border-color: #ddd;
    --error-color: #e74c3c;
    --success-color: #2ecc71;
}
body.dark-mode {
    --primary-color: #5a9aed;
    --primary-hover: #4a89dc;
    --secondary-color: #6bc76b;
    --text-color: #f1f1f1;
    --light-text: #aaa;
    --bg-color: #1a1a1a;
    --card-bg: #2c2c2c;
    --border-color: #444;
}
body {
    background-color: var(--bg-color);
    background-image: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
    background-size: cover;
    background-position: center;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    color: var(--text-color);
    transition: all 0.3s ease;
}
.theme-toggle {
    position: absolute;
    top: 20px;
    right: 20px;
    z-index: 10;
    cursor: pointer;
    padding: 8px;
    border-radius: 50%;
    background-color: rgba(255, 255, 255, 0.2);
    backdrop-filter: blur(5px);
    border: 1px solid rgba(255, 255, 255, 0.1);
}
.overlay {
    background-color: rgba(0, 0, 0, 0.4);
    backdrop-filter: blur(5px);
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: -1;
}
.main-container {
    display: flex;
    justify-content: center;
    align-items: center;
    flex: 1;
    padding: 20px;
}
.login-container {
    background-color: var(--card-bg);
    border-radius: 12px;
    box-shadow: 0 5px 20px rgba(0, 0, 0, 0.15);
    width: 450px;
    padding: 35px;
    position: relative;
    overflow: hidden;
    animation: fadeIn 0.5s ease;
}
@keyframes fadeIn {
    from { opacity: 0; transform: translateY(20px); }
    to { opacity: 1; transform: translateY(0); }
}
.logo {
    text-align: center;
    margin-bottom: 25px;
    position: relative;
}
.logo img {
    width: 90px;
    height: 90px;
    border-radius: 12px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    padding: 5px;
    background-color: #fff;
    transition: transform 0.3s ease;
}
h1 {
    text-align: center;
    color: var(--text-color);
    margin-bottom: 10px;
    font-weight: 600;
    font-size: 28px;
}
.subtitle {
    text-align: center;
    color: var(--light-text);
    margin-bottom: 30px;
    font-size: 14px;
}
.form-group {
    margin-bottom: 22px;
    position: relative;
}
.form-group label {
    display: block;
    margin-bottom: 8px;
    color: var(--text-color);
    font-weight: 500;
    font-size: 14px;
}
.input-with-icon {
    position: relative;
}
.input-icon {
    position: absolute;
    left: 15px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--light-text);
}
.form-control {
    width: 100%;
    height: 48px;
    border: 1px solid var(--border-color);
    border-radius: 6px;
    padding: 0 15px 0 45px;
    font-size: 15px;
    transition: all 0.3s ease;
    background-color: var(--card-bg);
    color: var(--text-color);
}
.form-control:focus {
    border-color: var(--primary-color);
    box-shadow: 0 0 0 3px rgba(74, 137, 220, 0.2);
    outline: none;
}
.password-toggle {
    position: absolute;
    right: 15px;
    top: 50%;
    transform: translateY(-50%);
    cursor: pointer;
    color: var(--light-text);
}
.validation-message {
    margin-top: 6px;
    font-size: 12px;
    color: var(--error-color);
    display: none;
}
.validation-message.show {
    display: block;
    animation: shake 0.5s ease;
}
@keyframes shake {
    0%, 100% { transform: translateX(0); }
    10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
    20%, 40%, 60%, 80% { transform: translateX(5px); }
}
.remember-forgot {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 25px;
}
.custom-checkbox {
    position: relative;
    padding-left: 30px;
    cursor: pointer;
    font-size: 14px;
    user-select: none;
    color: var(--light-text);
}
.custom-checkbox input {
    position: absolute;
    opacity: 0;
    cursor: pointer;
    height: 0;
    width: 0;
}
.checkmark {
    position: absolute;
    top: 0;
    left: 0;
    height: 18px;
    width: 18px;
    background-color: var(--card-bg);
    border: 1px solid var(--border-color);
    border-radius: 3px;
    transition: all 0.2s ease;
}
.custom-checkbox:hover input ~ .checkmark {
    border-color: var(--primary-color);
}
.custom-checkbox input:checked ~ .checkmark {
    background-color: var(--primary-color);
    border-color: var(--primary-color);
}
.checkmark:after {
    content: "";
    position: absolute;
    display: none;
}
.custom-checkbox input:checked ~ .checkmark:after {
    display: block;
}
.custom-checkbox .checkmark:after {
    left: 6px;
    top: 2px;
    width: 4px;
    height: 9px;
    border: solid white;
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}
.forgot-password a {
    color: var(--primary-color);
    text-decoration: none;
    font-size: 14px;
    transition: color 0.3s ease;
}
.forgot-password a:hover {
    color: var(--primary-hover);
    text-decoration: underline;
}
.btn-login {
    width: 100%;
    height: 48px;
    background-color: var(--primary-color);
    color: white;
    border: none;
    border-radius: 6px;
    font-size: 16px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.3s ease;
    position: relative;
    overflow: hidden;
}
.btn-login:hover {
    background-color: var(--primary-hover);
}
.btn-login:active {
    transform: scale(0.98);
}
.btn-login .loading {
    display: none;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}
.btn-login.loading-state {
    color: transparent;
}
.btn-login.loading-state .loading {
    display: block;
}
.signup {
    text-align: center;
    margin-top: 25px;
    font-size: 14px;
    color: var(--light-text);
}
.signup a {
    color: var(--primary-color);
    text-decoration: none;
    font-weight: 600;
    transition: color 0.3s ease;
}
.signup a:hover {
    color: var(--primary-hover);
    text-decoration: underline;
}
.features {
    display: flex;
    justify-content: center;
    margin-top: 25px;
    gap: 30px;
}
.feature-item {
    text-align: center;
    font-size: 12px;
    color: var(--light-text);
    display: flex;
    flex-direction: column;
    align-items: center;
}
.feature-icon {
    margin-bottom: 5px;
    font-size: 18px;
}
footer {
    text-align: center;
    padding: 20px;
    color: rgba(255, 255, 255, 0.7);
    font-size: 12px;
}
footer a {
    color: rgba(255, 255, 255, 0.9);
    text-decoration: none;
}
.alert {
    padding: 10px;
    margin-bottom: 15px;
    border-radius: 4px;
    color: #721c24;
    background-color: #f8d7da;
    border: 1px solid #f5c6cb;
}
@media (max-width: 576px) {
    .login-container {
        width: 100%;
        padding: 25px;
        border-radius: 0;
    }
    .theme-toggle {
        top: 10px;
    }
    .logo img {
        width: 70px;
        height: 70px;
    }
    h1 {
        font-size: 22px;
    }
    .main-container {
        padding: 0;
    }
}
================================================================================
File: ./app/static/css/book-edit.css
================================================================================
/* ========== 优雅粉色主题 - 图书编辑系统 ========== */
:root {
    --primary-pink: #FF85A2;
    --primary-pink-hover: #FF6D8E;
    --secondary-pink: #FFC0D3;
    --accent-pink: #FF4778;
    --background-pink: #FFF5F7;
    --border-pink: #FFD6E0;
    --soft-lavender: #E2D1F9;
    --mint-green: #D0F0C0;
    --dark-text: #5D4E60;
    --medium-text: #8A7B8F;
    --light-text: #BFB5C6;
    --white: #FFFFFF;
    --shadow-sm: 0 4px 6px rgba(255, 133, 162, 0.1);
    --shadow-md: 0 6px 12px rgba(255, 133, 162, 0.15);
    --shadow-lg: 0 15px 25px rgba(255, 133, 162, 0.2);
    --border-radius-sm: 8px;
    --border-radius-md: 12px;
    --border-radius-lg: 16px;
    --transition-fast: 0.2s ease;
    --transition-base: 0.3s ease;
    --font-primary: 'Poppins', 'Helvetica Neue', sans-serif;
    --font-secondary: 'Playfair Display', serif;
}
/* ========== 全局样式 ========== */
body {
    background-color: var(--background-pink);
    color: var(--dark-text);
    font-family: var(--font-primary);
    line-height: 1.6;
}
h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-secondary);
    color: var(--dark-text);
}
a {
    color: var(--accent-pink);
    transition: color var(--transition-fast);
}
a:hover {
    color: var(--primary-pink-hover);
    text-decoration: none;
}
.btn {
    border-radius: var(--border-radius-sm);
    font-weight: 500;
    transition: all var(--transition-base);
    box-shadow: var(--shadow-sm);
    padding: 0.5rem 1.25rem;
}
.btn:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
}
.btn-primary {
    background-color: var(--primary-pink);
    border-color: var(--primary-pink);
}
.btn-primary:hover, .btn-primary:focus {
    background-color: var(--primary-pink-hover);
    border-color: var(--primary-pink-hover);
}
.btn-info {
    background-color: var(--soft-lavender);
    border-color: var(--soft-lavender);
    color: var(--dark-text);
}
.btn-info:hover, .btn-info:focus {
    background-color: #D4BFF0;
    border-color: #D4BFF0;
    color: var(--dark-text);
}
.btn-secondary {
    background-color: var(--white);
    border-color: var(--border-pink);
    color: var(--medium-text);
}
.btn-secondary:hover, .btn-secondary:focus {
    background-color: var(--border-pink);
    border-color: var(--border-pink);
    color: var(--dark-text);
}
.btn i {
    margin-right: 8px;
}
/* ========== 表单容器 ========== */
.book-form-container {
    max-width: 1400px;
    margin: 2rem auto;
    padding: 2rem;
    background-color: var(--white);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-md);
    position: relative;
    overflow: hidden;
}
.book-form-container::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 8px;
    background: linear-gradient(to right, var(--primary-pink), var(--accent-pink), var(--soft-lavender));
}
/* ========== 页面标题区域 ========== */
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 2rem;
    padding-bottom: 1rem;
    border-bottom: 2px solid var(--secondary-pink);
}
.page-header h1 {
    font-size: 2.2rem;
    font-weight: 700;
    color: var(--primary-pink);
    margin: 0;
    position: relative;
    font-family: var(--font-secondary);
}
.flower-icon {
    color: var(--accent-pink);
    margin-right: 8px;
}
.actions {
    display: flex;
    gap: 1rem;
}
/* ========== 表单元素 ========== */
.form-row {
    margin-bottom: 1.5rem;
}
.form-group {
    margin-bottom: 1.5rem;
}
.form-group label {
    color: var(--dark-text);
    font-weight: 500;
    font-size: 0.95rem;
    margin-bottom: 0.5rem;
    display: block;
}
.form-control {
    border: 2px solid var(--border-pink);
    border-radius: var(--border-radius-sm);
    padding: 0.75rem 1rem;
    color: var(--dark-text);
    transition: all var(--transition-fast);
    font-size: 0.95rem;
}
.form-control:focus {
    border-color: var(--primary-pink);
    box-shadow: 0 0 0 0.2rem rgba(255, 133, 162, 0.25);
}
.form-control::placeholder {
    color: var(--light-text);
}
.required {
    color: var(--accent-pink);
}
select.form-control {
height: 42px; / 确保高度一致,内容不截断 */
line-height: 1.5;
padding: 8px 12px;
font-size: 0.95rem;
color: var(--dark-text);
background-color: var(--white);
border: 1px solid var(--border-pink);
border-radius: var(--border-radius-sm);
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%235D4E60' viewBox='0 0 24 24'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 0.75rem center;
background-size: 1rem;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
select.form-control:focus {
border-color: var(--primary-pink);
outline: none;
box-shadow: 0 0 0 0.2rem rgba(255, 133, 162, 0.2);
}
/* 状态选项 / 分类样式专属修复(可选项) */
#status, #category_id {
padding-top: 8px;
padding-bottom: 8px;
font-family: inherit;
}
/* iOS & Edge 下拉兼容优化 */
select.form-control::-ms-expand {
display: none;
}
/* 浏览器优雅过渡体验 */
select.form-control:hover {
border-color: var(--accent-pink);
}
select.form-control:disabled {
background-color: var(--background-pink);
color: var(--light-text);
cursor: not-allowed;
opacity: 0.7;
}
textarea.form-control {
    min-height: 150px;
    resize: vertical;
}
/* ========== 卡片样式 ========== */
.card {
    border: none;
    border-radius: var(--border-radius-md);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
    transition: all var(--transition-base);
    margin-bottom: 1.5rem;
    background-color: var(--white);
}
.card:hover {
    box-shadow: var(--shadow-md);
}
.card-header {
    background-color: var(--secondary-pink);
    border-bottom: none;
    padding: 1rem 1.5rem;
    font-family: var(--font-secondary);
    font-weight: 600;
    color: var(--dark-text);
    font-size: 1.1rem;
}
.card-body {
    padding: 1.5rem;
    background-color: var(--white);
}
/* ========== 封面图片区域 ========== */
.cover-preview-container {
    padding: 1rem;
    text-align: center;
}
.cover-preview {
    min-height: 300px;
    background-color: var(--background-pink);
    border: 2px dashed var(--secondary-pink);
    border-radius: var(--border-radius-sm);
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 1rem;
    position: relative;
    transition: all var(--transition-fast);
}
.cover-preview:hover {
    border-color: var(--primary-pink);
}
.cover-image {
    max-width: 100%;
    max-height: 300px;
    border-radius: var(--border-radius-sm);
    box-shadow: var(--shadow-sm);
}
.no-cover-placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    color: var(--light-text);
    padding: 2rem;
}
.no-cover-placeholder i {
    font-size: 3rem;
    margin-bottom: 1rem;
}
.upload-container {
    margin-top: 1rem;
}
.btn-outline-primary {
    color: var(--primary-pink);
    border-color: var(--primary-pink);
    background-color: transparent;
    transition: all var(--transition-base);
}
.btn-outline-primary:hover, .btn-outline-primary:focus {
    background-color: var(--primary-pink);
    color: white;
}
/* ========== 提交按钮区域 ========== */
.form-submit-container {
    margin-top: 2rem;
}
.btn-lg {
    padding: 1rem 1.5rem;
    font-size: 1.1rem;
}
.btn-block {
    width: 100%;
}
/* 输入组样式 */
.input-group-prepend .input-group-text {
    background-color: var(--secondary-pink);
    border-color: var(--border-pink);
    color: var(--dark-text);
    border-radius: var(--border-radius-sm) 0 0 var(--border-radius-sm);
}
/* 聚焦效果 */
.is-focused label {
    color: var(--primary-pink);
}
/* ========== 动画效果 ========== */
@keyframes fadeIn {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: translateY(0); }
}
.book-form-container {
    animation: fadeIn 0.5s ease;
}
/* ========== 响应式样式 ========== */
@media (max-width: 992px) {
    .book-form-container {
        padding: 1.5rem;
    }
    .page-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 1rem;
    }
    .actions {
        margin-top: 1rem;
    }
}
@media (max-width: 768px) {
    .book-form-container {
        padding: 1rem;
    }
    .card-header, .card-body {
        padding: 1rem;
    }
    .cover-preview {
        min-height: 250px;
    }
    .col-md-8, .col-md-4 {
        padding: 0 0.5rem;
    }
}
.is-invalid {
    border-color: #dc3545;
}
.is-valid {
    border-color: #28a745;
}
.invalid-feedback {
    display: none;
    color: #dc3545;
    font-size: 0.875rem;
}
.is-invalid ~ .invalid-feedback {
    display: block;
}
================================================================================
File: ./app/static/css/borrow_management.css
================================================================================
/* borrow_management.css - Optimized for literary female audience */
/* Main typography and colors */
body {
    font-family: 'Georgia', serif;
    color: #4a3728;
    background-color: #fcf8f3;
}
.page-title {
    margin-bottom: 1.5rem;
    color: #5d3511;
    border-bottom: 2px solid #d9c7b8;
    padding-bottom: 15px;
    font-family: 'Playfair Display', Georgia, serif;
    letter-spacing: 0.5px;
    position: relative;
}
.page-title:after {
    content: "❦";
    position: absolute;
    bottom: -12px;
    left: 50%;
    font-size: 18px;
    color: #8d6e63;
    background: #fcf8f3;
    padding: 0 10px;
    transform: translateX(-50%);
}
.container {
    background-color: #fff9f5;
    border-radius: 8px;
    box-shadow: 0 3px 15px rgba(113, 66, 20, 0.1);
    padding: 25px;
    margin-top: 20px;
    margin-bottom: 20px;
    border: 1px solid #e8d9cb;
}
/* Tabs styling */
.tabs {
    display: flex;
    border-bottom: 1px solid #d9c7b8;
    margin-bottom: 25px;
    position: relative;
}
.tabs:before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -3px;
    height: 2px;
    background: linear-gradient(to right, transparent, #8d6e63, transparent);
}
.tab {
    padding: 12px 22px;
    text-decoration: none;
    color: #5d3511;
    margin-right: 5px;
    border-radius: 8px 8px 0 0;
    position: relative;
    transition: all 0.3s ease;
    font-family: 'Georgia', serif;
}
.tab:hover {
    background-color: #f1e6dd;
    color: #704214;
    text-decoration: none;
}
.tab.active {
    background-color: #704214;
    color: #f8f0e5;
    font-weight: 500;
}
.tab.overdue-tab {
    background-color: #f9e8e8;
    color: #a15950;
}
.tab.overdue-tab:hover {
    background-color: #f4d3d3;
}
/* 修改 count 样式,避免与顶部导航冲突 */
.tabs .count {
    background-color: rgba(113, 66, 20, 0.15);
    border-radius: 12px;
    padding: 2px 10px;
    font-size: 0.8em;
    margin-left: 8px;
    font-family: 'Arial', sans-serif;
    display: inline-block;
    position: static;
    width: auto;
    height: auto;
}
.tab.active .count {
    background-color: rgba(255, 243, 224, 0.3);
}
.count.overdue-count {
    background-color: rgba(161, 89, 80, 0.2);
}
/* Search and filters */
.search-card {
    margin-bottom: 25px;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(113, 66, 20, 0.08);
    border: 1px solid #e8d9cb;
    background: linear-gradient(to bottom right, #fff, #fcf8f3);
}
.search-card .card-body {
    padding: 20px;
}
.search-form {
    margin-bottom: 0;
}
.search-card .form-control {
    border: 1px solid #d9c7b8;
    border-radius: 6px;
    color: #5d3511;
    background-color: #fff9f5;
    transition: all 0.3s ease;
    font-family: 'Georgia', serif;
}
.search-card .form-control:focus {
    border-color: #704214;
    box-shadow: 0 0 0 0.2rem rgba(113, 66, 20, 0.15);
    background-color: #fff;
}
.search-card .btn-outline-secondary {
    color: #704214;
    border-color: #d9c7b8;
    background-color: transparent;
}
.search-card .btn-outline-secondary:hover {
    color: #fff;
    background-color: #8d6e63;
    border-color: #8d6e63;
}
.clear-filters {
    display: block;
    width: 100%;
    text-align: center;
    font-style: italic;
}
/* Table styling */
.borrow-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    margin-bottom: 25px;
    box-shadow: 0 2px 10px rgba(113, 66, 20, 0.05);
    border-radius: 8px;
    overflow: hidden;
    border: 1px solid #e8d9cb;
}
.borrow-table th,
.borrow-table td {
    padding: 15px 18px;
    text-align: left;
    border-bottom: 1px solid #e8d9cb;
    vertical-align: middle;
}
/* 调整借阅用户列向左偏移15px */
.borrow-table th:nth-child(3),
.borrow-table td:nth-child(3) {
    padding-right: 3px;
}
.borrow-table th {
    background-color: #f1e6dd;
    color: #5d3511;
    font-weight: 600;
    letter-spacing: 0.5px;
}
/* 状态列调整 - 居中并确保内容显示 */
.borrow-table th:nth-child(6) {
    text-align: center;
}
.borrow-table td:nth-child(6) {
    text-align: center;
}
.borrow-item:hover {
    background-color: #f8f0e5;
}
.borrow-item:last-child td {
    border-bottom: none;
}
.book-cover img {
    width: 65px;
    height: 90px;
    object-fit: cover;
    border-radius: 6px;
    box-shadow: 0 3px 8px rgba(113, 66, 20, 0.15);
    border: 2px solid #fff;
    transition: transform 0.3s ease;
}
.book-cover img:hover {
    transform: scale(1.05);
}
.book-title {
    font-weight: 600;
    font-family: 'Georgia', serif;
}
.book-title a {
    color: #5d3511;
    text-decoration: none;
    transition: color 0.3s ease;
}
.book-title a:hover {
    color: #a66321;
    text-decoration: underline;
}
.book-author {
    color: #8d6e63;
    font-size: 0.9em;
    margin-top: 5px;
    font-style: italic;
}
/* 修改借阅用户显示方式 */
.borrow-table .user-info {
    text-align: center;
    display: table-cell;
    vertical-align: middle;
    height: 100%;
}
.borrow-table .user-info a {
    color: #5d3511;
    text-decoration: none;
    font-weight: 600;
    transition: color 0.3s ease;
    display: block;
    margin-bottom: 8px;
}
.borrow-table .user-info a:hover {
    color: #a66321;
    text-decoration: underline;
}
.user-nickname {
    color: #8d6e63;
    font-size: 0.9em;
    display: block;
    margin-top: 0;
}
/* Badges and status indicators - 修复与顶部导航栏冲突 */
.borrow-table .badge {
    padding: 5px 12px;
    border-radius: 20px;
    font-weight: 500;
    font-size: 0.85em;
    letter-spacing: 0.5px;
    display: inline-block;
    margin-bottom: 5px;
    position: static;
    width: auto;
    height: auto;
    top: auto;
    right: auto;
}
/* 给状态列的徽章额外的特异性 */
.borrow-table td .badge {
    position: static;
    width: auto;
    height: auto;
    display: inline-block;
    font-size: 0.85em;
    border-radius: 20px;
    padding: 5px 12px;
}
.borrow-table .badge-primary {
    background-color: #704214;
    color: white;
}
.borrow-table .badge-success {
    background-color: #5b8a72;
    color: white;
}
.borrow-table .badge-danger {
    background-color: #a15950;
    color: white;
}
.borrow-table .badge-info {
    background-color: #6a8da9;
    color: white;
}
.borrow-table .badge-warning {
    background-color: #d4a76a;
    color: #4a3728;
}
.return-date {
    color: #8d6e63;
    font-size: 0.9em;
    margin-top: 5px;
}
/* 确保状态显示正确 */
.borrow-item td:nth-child(6) span.badge {
    min-width: 80px;
}
/* 只为表格中的操作按钮设置样式 */
.actions .btn {
    margin-right: 5px;
    margin-bottom: 6px;
    border-radius: 20px;
    padding: 8px 16px;
    transition: all 0.3s ease;
    font-family: 'Georgia', serif;
    letter-spacing: 0.5px;
}
.actions .btn-success {
    background-color: #5b8a72;
    border-color: #5b8a72;
}
.actions .btn-success:hover,
.actions .btn-success:focus {
    background-color: #4a7561;
    border-color: #4a7561;
}
.actions .btn-warning {
    background-color: #d4a76a;
    border-color: #d4a76a;
    color: #4a3728;
}
.actions .btn-warning:hover,
.actions .btn-warning:focus {
    background-color: #c29355;
    border-color: #c29355;
    color: #4a3728;
}
.actions .btn-primary {
    background-color: #704214;
    border-color: #704214;
}
.actions .btn-primary:hover,
.actions .btn-primary:focus {
    background-color: #5d3511;
    border-color: #5d3511;
    box-shadow: 0 0 0 0.2rem rgba(113, 66, 20, 0.25);
}
.text-danger {
    color: #a15950 !important;
}
.overdue {
    background-color: rgba(161, 89, 80, 0.05);
}
/* Empty states */
.no-records {
    text-align: center;
    padding: 60px 20px;
    background-color: #f8f0e5;
    border-radius: 8px;
    margin: 25px 0;
    border: 1px dashed #d9c7b8;
}
.empty-icon {
    font-size: 4.5em;
    color: #d9c7b8;
    margin-bottom: 25px;
}
.empty-text {
    color: #8d6e63;
    margin-bottom: 25px;
    font-style: italic;
    font-size: 1.1em;
}
/* Pagination */
.pagination-container {
    display: flex;
    justify-content: center;
    margin-top: 25px;
}
.pagination .page-link {
    color: #5d3511;
    border-color: #e8d9cb;
    margin: 0 3px;
    border-radius: 4px;
}
.pagination .page-item.active .page-link {
    background-color: #704214;
    border-color: #704214;
}
.pagination .page-link:hover {
    background-color: #f1e6dd;
    color: #5d3511;
}
/* Modal customization */
.modal-content {
    border-radius: 8px;
    border: 1px solid #e8d9cb;
    box-shadow: 0 5px 20px rgba(113, 66, 20, 0.15);
    background-color: #fff9f5;
}
.modal-header {
    border-bottom: 1px solid #e8d9cb;
    background-color: #f1e6dd;
    border-radius: 8px 8px 0 0;
}
.modal-title {
    color: #5d3511;
    font-family: 'Georgia', serif;
}
.modal-footer {
    border-top: 1px solid #e8d9cb;
}
/* Decorative elements */
.container:before {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    width: 150px;
    height: 150px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Cpath fill='%23d9c7b8' fill-opacity='0.2' d='M11 18c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm48 25c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm-43-7c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm63 31c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM34 90c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm56-76c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM12 86c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm28-65c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm23-11c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-6 60c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm29 22c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zM32 63c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm57-13c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-9-21c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM60 91c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM35 41c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM12 60c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2z'/%3E%3C/svg%3E");
    opacity: 0.3;
    pointer-events: none;
    z-index: -1;
}
/* Responsive design */
@media (max-width: 992px) {
    .tabs {
        flex-wrap: wrap;
    }
    .tab {
        margin-bottom: 8px;
    }
}
@media (max-width: 768px) {
    .tabs {
        flex-direction: column;
        border-bottom: none;
    }
    .tab {
        border-radius: 8px;
        margin-right: 0;
        margin-bottom: 8px;
        border: 1px solid #d9c7b8;
    }
    .borrow-table {
        display: block;
        overflow-x: auto;
    }
    .book-cover img {
        width: 50px;
        height: 70px;
    }
    .search-card .row {
        margin-bottom: 15px;
    }
}
.container .btn-primary {
    background-color: #704214;
    border-color: #704214;
    color: white;
    border-radius: 20px;
}
.container .btn-primary:hover,
.container .btn-primary:focus {
    background-color: #5d3511;
    border-color: #5d3511;
    box-shadow: 0 0 0 0.2rem rgba(113, 66, 20, 0.25);
}
================================================================================
File: ./app/static/css/index.css
================================================================================
/* index.css - 仅用于图书管理系统首页/仪表板 */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
}
body {
    background-color: #f5f7fa;
    color: #333;
    font-size: 16px;
    line-height: 1.6;
}
a {
    text-decoration: none;
    color: #4a89dc;
}
ul {
    list-style: none;
}
/* 应用容器 */
.app-container {
    display: flex;
    min-height: 100vh;
}
/* 侧边导航栏 */
.sidebar {
    width: 250px;
    background-color: #2c3e50;
    color: #ecf0f1;
    padding: 20px 0;
    box-shadow: 0 0 10px rgba(0,0,0,0.1);
    position: fixed;
    height: 100vh;
    overflow-y: auto;
}
.logo-container {
    padding: 0 20px 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    margin-bottom: 20px;
    border-bottom: 1px solid rgba(255,255,255,0.1);
}
.logo {
    width: 60px;
    height: auto;
    margin-bottom: 10px;
}
.logo-container h2 {
    font-size: 1.2rem;
    margin: 10px 0;
    color: #ecf0f1;
    font-weight: 500;
}
.nav-links li {
    margin-bottom: 5px;
}
.nav-links li a {
    padding: 10px 20px;
    display: flex;
    align-items: center;
    color: #bdc3c7;
    transition: all 0.3s ease;
}
.nav-links li a i {
    margin-right: 10px;
    font-size: 1.1rem;
    width: 20px;
    text-align: center;
}
.nav-links li a:hover, .nav-links li.active a {
    background-color: #34495e;
    color: #ecf0f1;
    border-left: 3px solid #4a89dc;
}
.nav-category {
    padding: 10px 20px;
    font-size: 0.85rem;
    text-transform: uppercase;
    color: #7f8c8d;
    margin-top: 15px;
    margin-bottom: 5px;
}
/* 主内容区 */
.main-content {
    flex: 1;
    margin-left: 250px;
    padding: 20px;
}
/* 顶部导航栏 */
.top-bar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 15px 30px;
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.05);
    margin-bottom: 20px;
}
.search-container {
    position: relative;
    width: 300px;
}
.search-input {
    padding: 10px 15px 10px 40px;
    width: 100%;
    border: 1px solid #e1e4e8;
    border-radius: 20px;
    font-size: 14px;
    transition: all 0.3s ease;
}
.search-input:focus {
    border-color: #4a89dc;
    box-shadow: 0 0 0 3px rgba(74, 137, 220, 0.1);
    outline: none;
}
.search-icon {
    position: absolute;
    left: 15px;
    top: 50%;
    transform: translateY(-50%);
    color: #8492a6;
}
.user-menu {
    display: flex;
    align-items: center;
}
.notifications {
    margin-right: 20px;
    position: relative;
    cursor: pointer;
}
.notifications i {
    font-size: 1.2rem;
    color: #606266;
}
.badge {
    position: absolute;
    top: -8px;
    right: -8px;
    background-color: #f56c6c;
    color: white;
    font-size: 0.7rem;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}
.user-info {
    display: flex;
    align-items: center;
    position: relative;
    cursor: pointer;
}
.user-avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background-color: #4a89dc;
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
    margin-right: 10px;
    font-size: 1.2rem;
}
.user-details {
    display: flex;
    flex-direction: column;
}
.user-name {
    font-weight: 500;
    color: #333;
}
.user-role {
    font-size: 0.8rem;
    color: #8492a6;
}
.dropdown-menu {
    position: absolute;
    top: 100%;
    right: 0;
    background-color: white;
    border-radius: 4px;
    box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
    padding: 10px 0;
    min-width: 150px;
    display: none;
    z-index: 10;
}
.user-info.active .dropdown-menu {
    display: block;
}
.dropdown-menu a {
    display: block;
    padding: 8px 15px;
    color: #606266;
    transition: all 0.3s ease;
}
.dropdown-menu a:hover {
    background-color: #f5f7fa;
}
.dropdown-menu a i {
    margin-right: 8px;
    width: 16px;
    text-align: center;
}
/* 欢迎区域 */
.welcome-section {
    background: linear-gradient(to right, #4a89dc, #5d9cec);
    color: white;
    padding: 30px;
    border-radius: 8px;
    margin-bottom: 20px;
    box-shadow: 0 4px 6px rgba(0,0,0,0.05);
}
.welcome-section h1 {
    font-size: 1.8rem;
    margin-bottom: 5px;
}
.welcome-section p {
    font-size: 1rem;
    opacity: 0.9;
}
/* 统计卡片样式 */
.stats-container {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 20px;
    margin-bottom: 30px;
}
.stat-card {
    background-color: white;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.05);
    padding: 20px;
    display: flex;
    align-items: center;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.stat-card:hover {
    transform: translateY(-5px);
    box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.stat-icon {
    font-size: 2rem;
    color: #4a89dc;
    margin-right: 15px;
    width: 40px;
    text-align: center;
}
.stat-info h3 {
    font-size: 0.9rem;
    color: #606266;
    margin-bottom: 5px;
}
.stat-number {
    font-size: 1.8rem;
    font-weight: 600;
    color: #2c3e50;
}
/* 主要内容区域 */
.main-sections {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 20px;
    margin-bottom: 30px;
}
.content-section {
    background-color: white;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.05);
    padding: 20px;
}
.section-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
    padding-bottom: 10px;
    border-bottom: 1px solid #edf2f7;
}
.section-header h2 {
    font-size: 1.2rem;
    color: #2c3e50;
}
.view-all {
    font-size: 0.85rem;
    color: #4a89dc;
    display: flex;
    align-items: center;
}
.view-all i {
    margin-left: 5px;
    transition: transform 0.3s ease;
}
.view-all:hover i {
    transform: translateX(3px);
}
/* 图书卡片样式 */
.book-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 20px;
}
.book-card {
    display: flex;
    border: 1px solid #edf2f7;
    border-radius: 8px;
    overflow: hidden;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.book-card:hover {
    transform: translateY(-5px);
    box-shadow: 0 5px 15px rgba(0,0,0,0.05);
}
.book-cover {
    width: 100px;
    height: 140px;
    min-width: 100px;
    background-color: #f5f7fa;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}
.book-cover img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.book-info {
    padding: 15px;
    flex: 1;
    display: flex;
    flex-direction: column;
}
.book-title {
    font-size: 1rem;
    margin-bottom: 5px;
    color: #2c3e50;
    display: -webkit-box;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.book-author {
    font-size: 0.85rem;
    color: #606266;
    margin-bottom: 10px;
}
.book-meta {
    display: flex;
    justify-content: space-between;
    margin-bottom: 15px;
}
.book-category {
    background-color: #e5f1ff;
    color: #4a89dc;
    padding: 3px 8px;
    border-radius: 4px;
    font-size: 0.75rem;
}
.book-status {
    font-size: 0.75rem;
    font-weight: 500;
}
.book-status.available {
    color: #67c23a;
}
.book-status.borrowed {
    color: #e6a23c;
}
.borrow-btn {
    background-color: #4a89dc;
    color: white;
    border: none;
    padding: 6px 12px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.85rem;
    margin-top: auto;
    transition: background-color 0.3s ease;
}
.borrow-btn:hover {
    background-color: #357bc8;
}
/* 通知公告样式 */
.notice-item {
    display: flex;
    padding: 15px 0;
    border-bottom: 1px solid #edf2f7;
}
.notice-item:last-child {
    border-bottom: none;
}
.notice-icon {
    font-size: 1.5rem;
    color: #4a89dc;
    margin-right: 15px;
    display: flex;
    align-items: flex-start;
    padding-top: 5px;
}
.notice-content h3 {
    font-size: 1rem;
    color: #2c3e50;
    margin-bottom: 5px;
}
.notice-content p {
    font-size: 0.9rem;
    color: #606266;
    margin-bottom: 10px;
}
.notice-meta {
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.notice-time {
    font-size: 0.8rem;
    color: #8492a6;
}
.renew-btn {
    background-color: #ecf5ff;
    color: #4a89dc;
    border: 1px solid #d9ecff;
    padding: 5px 10px;
    border-radius: 4px;
    font-size: 0.8rem;
    cursor: pointer;
    transition: all 0.3s ease;
}
.renew-btn:hover {
    background-color: #4a89dc;
    color: white;
    border-color: #4a89dc;
}
/* 热门图书区域 */
.popular-section {
    margin-top: 20px;
}
.popular-books {
    display: flex;
    overflow-x: auto;
    gap: 15px;
    padding-bottom: 10px;
}
.popular-book-item {
    display: flex;
    background-color: #f8fafc;
    border-radius: 8px;
    padding: 20px 15px 15px;  /* 增加顶部内边距,为角标留出空间 */
    min-width: 280px;
    position: relative;
    margin-top: 10px;  /* 在顶部添加一些外边距 */
    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
    transition: transform 0.2s;
}
.popular-book-item:hover {
    transform: translateY(-3px);
    box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.rank-badge {
    position: absolute;
    top: -8px;  /* 略微调高一点 */
    left: 10px;
    background-color: #4a89dc;
    color: white;
    width: 28px;  /* 增加尺寸 */
    height: 28px; /* 增加尺寸 */
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    font-size: 0.85rem;
    font-weight: bold;
    z-index: 10;  /* 确保它位于其他元素之上 */
    box-shadow: 0 2px 4px rgba(0,0,0,0.2);  /* 添加阴影使其更突出 */
}
.book-cover.small {
    width: 60px;
    height: 90px;
    min-width: 60px;
    margin-right: 15px;
    border-radius: 4px;
    overflow: hidden;  /* 确保图片不会溢出容器 */
}
.book-cover.small img {
    width: 100%;
    height: 100%;
    object-fit: cover;  /* 确保图片正确填充容器 */
}
.book-details {
    flex: 1;
}
.book-stats {
    display: flex;
    flex-direction: column;
    gap: 5px;
    margin-top: 10px;
}
.book-stats span {
    font-size: 0.8rem;
    color: #8492a6;
}
.book-stats i {
    margin-right: 5px;
}
/* 响应式调整 */
@media (max-width: 1200px) {
    .stats-container {
        grid-template-columns: repeat(2, 1fr);
    }
    .main-sections {
        grid-template-columns: 1fr;
    }
}
@media (max-width: 768px) {
    .sidebar {
        width: 70px;
        overflow: hidden;
    }
    .logo-container {
        padding: 10px;
    }
    .logo-container h2 {
        display: none;
    }
    .nav-links li a span {
        display: none;
    }
    .nav-links li a i {
        margin-right: 0;
    }
    .nav-category {
        display: none;
    }
    .main-content {
        margin-left: 70px;
    }
    .search-container {
        width: 180px;
    }
    .book-grid {
        grid-template-columns: 1fr;
    }
}
@media (max-width: 576px) {
    .stats-container {
        grid-template-columns: 1fr;
    }
    .top-bar {
        flex-direction: column;
        gap: 15px;
    }
    .search-container {
        width: 100%;
    }
    .user-details {
        display: none;
    }
}
================================================================================
File: ./app/static/css/main.css
================================================================================
/* 基础样式 */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background-color: #f0f2f5;
    color: #333;
}
.app-container {
    display: flex;
    min-height: 100vh;
}
/* 侧边栏样式 */
.sidebar {
    width: 250px;
    background-color: #2c3e50;
    color: white;
    box-shadow: 2px 0 5px rgba(0,0,0,0.1);
    position: fixed;
    height: 100vh;
    overflow-y: auto;
    z-index: 1000;
}
.logo-container {
    display: flex;
    align-items: center;
    padding: 20px 15px;
    border-bottom: 1px solid rgba(255,255,255,0.1);
}
.logo {
    width: 40px;
    height: 40px;
    margin-right: 10px;
}
.logo-container h2 {
    font-size: 1.2rem;
    font-weight: 600;
}
.nav-links {
    list-style: none;
    padding: 15px 0;
}
.nav-category {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 1px;
    padding: 15px 20px 5px;
    color: #adb5bd;
}
.nav-links li {
    position: relative;
}
.nav-links li.active {
    background-color: rgba(255,255,255,0.1);
}
.nav-links li a {
    display: flex;
    align-items: center;
    padding: 12px 20px;
    color: #ecf0f1;
    text-decoration: none;
    transition: all 0.3s;
}
.nav-links li a:hover {
    background-color: rgba(255,255,255,0.05);
}
.nav-links li a i {
    margin-right: 10px;
    width: 20px;
    text-align: center;
}
/* 主内容区样式 */
.main-content {
    flex: 1;
    margin-left: 250px;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}
.top-bar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 15px 25px;
    background-color: white;
    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
    position: sticky;
    top: 0;
    z-index: 900;
}
.search-container {
    position: relative;
    width: 350px;
}
.search-icon {
    position: absolute;
    left: 10px;
    top: 50%;
    transform: translateY(-50%);
    color: #adb5bd;
}
.search-input {
    width: 100%;
    padding: 10px 10px 10px 35px;
    border: 1px solid #dee2e6;
    border-radius: 20px;
    font-size: 0.9rem;
}
.search-input:focus {
    outline: none;
    border-color: #4a6cf7;
}
.user-menu {
    display: flex;
    align-items: center;
}
/* 通知图标样式 */
.notifications {
    position: relative;
    margin-right: 20px;
}
.notification-icon {
    cursor: pointer;
    color: #495057;
    position: relative;
    display: block;
    padding: 5px;
}
.badge {
    position: absolute;
    top: -5px;
    right: -5px;
    background-color: #e74c3c;
    color: white;
    font-size: 0.7rem;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}
/* 通知下拉菜单 */
.notification-dropdown {
    position: absolute;
    top: 100%;
    right: -10px;
    width: 320px;
    background-color: white;
    border-radius: 5px;
    box-shadow: 0 3px 10px rgba(0,0,0,0.2);
    display: none;
    z-index: 1000;
    max-height: 400px;
    overflow-y: auto;
    margin-top: 10px;
}
.notification-dropdown.show {
    display: block;
}
.notification-dropdown::before {
    content: '';
    position: absolute;
    top: -8px;
    right: 15px;
    border-left: 8px solid transparent;
    border-right: 8px solid transparent;
    border-bottom: 8px solid white;
}
.notification-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 12px 15px;
    border-bottom: 1px solid #eaeaea;
}
.notification-header h6 {
    margin: 0;
    font-size: 0.9rem;
    font-weight: 600;
}
.mark-all-read {
    font-size: 0.8rem;
    color: #4a6cf7;
    text-decoration: none;
}
.mark-all-read:hover {
    text-decoration: underline;
}
.notification-items {
    max-height: 300px;
    overflow-y: auto;
}
.notification-item {
    display: block;
    padding: 12px 15px;
    border-bottom: 1px solid #f5f5f5;
    color: #333;
    text-decoration: none;
    transition: background-color 0.2s;
}
.notification-item:hover {
    background-color: #f8f9fa;
    text-decoration: none;
}
.notification-item.unread {
    background-color: #f0f7ff;
}
.notification-title {
    font-size: 0.9rem;
    margin-bottom: 5px;
    font-weight: 600;
}
.notification-text {
    font-size: 0.8rem;
    color: #666;
    margin-bottom: 5px;
}
.notification-time {
    font-size: 0.75rem;
    color: #999;
    display: block;
}
.no-notifications {
    padding: 25px;
    text-align: center;
    color: #999;
}
.dropdown-divider {
    height: 1px;
    background-color: #eaeaea;
    margin: 5px 0;
}
.view-all {
    display: block;
    text-align: center;
    padding: 10px;
    color: #4a6cf7;
    text-decoration: none;
    font-weight: 500;
    font-size: 0.9rem;
}
.view-all:hover {
    background-color: #f8f9fa;
    text-decoration: none;
}
/* 用户信息样式 */
.user-info {
    position: relative;
}
.user-info-toggle {
    display: flex;
    align-items: center;
    cursor: pointer;
    text-decoration: none;
    color: inherit;
}
.user-avatar {
    width: 40px;
    height: 40px;
    background-color: #4a6cf7;
    color: white;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
    margin-right: 10px;
}
.user-details {
    display: flex;
    flex-direction: column;
}
.user-name {
    font-weight: 600;
    font-size: 0.9rem;
}
.user-role {
    font-size: 0.8rem;
    color: #6c757d;
}
.user-dropdown {
    position: absolute;
    top: 100%;
    right: 0;
    background-color: white;
    box-shadow: 0 3px 10px rgba(0,0,0,0.2);
    border-radius: 5px;
    width: 200px;
    padding: 5px 0;
    display: none;
    z-index: 1000;
    margin-top: 10px;
}
.user-dropdown.show {
    display: block;
}
.user-dropdown::before {
    content: '';
    position: absolute;
    top: -8px;
    right: 15px;
    border-left: 8px solid transparent;
    border-right: 8px solid transparent;
    border-bottom: 8px solid white;
}
.user-dropdown a {
    display: block;
    padding: 10px 15px;
    color: #333;
    text-decoration: none;
    transition: background-color 0.2s;
}
.user-dropdown a:hover {
    background-color: #f8f9fa;
}
.user-dropdown a i {
    width: 20px;
    margin-right: 10px;
    text-align: center;
}
.login-link, .register-link {
    color: #4a6cf7;
    text-decoration: none;
    margin-left: 10px;
    font-weight: 500;
}
.login-link:hover, .register-link:hover {
    text-decoration: underline;
}
/* 内容区域 */
.content-wrapper {
    flex: 1;
    padding: 20px;
    background-color: #f0f2f5;
}
/* 响应式适配 */
@media (max-width: 768px) {
    .sidebar {
        width: 70px;
        overflow: visible;
    }
    .logo-container h2 {
        display: none;
    }
    .nav-links li a span {
        display: none;
    }
    .main-content {
        margin-left: 70px;
    }
    .user-details {
        display: none;
    }
    .search-container {
        width: 200px;
    }
    .notification-dropdown,
    .user-dropdown {
        position: fixed;
        right: 10px;
        width: calc(100% - 80px);
        max-width: 320px;
    }
}
================================================================================
File: ./app/static/css/log-list.css
================================================================================
/* 全局风格与颜色 */
:root {
    --primary-color: #9c88ff;
    --secondary-color: #f8a5c2;
    --accent-color: #78e08f;
    --light-pink: #ffeef8;
    --soft-blue: #e5f1ff;
    --soft-purple: #f3e5ff;
    --soft-pink: #ffeef5;
    --soft-red: #ffd8d8;
    --text-color: #4a4a4a;
    --light-text: #8a8a8a;
    --border-radius: 12px;
    --box-shadow: 0 6px 15px rgba(0,0,0,0.05);
    --transition: all 0.3s ease;
}
/* 整体容器 */
.content-container {
    padding: 20px;
    font-family: 'Montserrat', sans-serif;
    color: var(--text-color);
    background-image: linear-gradient(to bottom, var(--soft-blue) 0%, rgba(255,255,255,0.8) 20%, rgba(255,255,255,0.9) 100%);
    border-radius: var(--border-radius);
    box-shadow: var(--box-shadow);
    position: relative;
    overflow: hidden;
}
/* 头部样式 */
.content-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 25px;
    background: linear-gradient(120deg, var(--soft-purple), var(--soft-pink));
    padding: 15px 20px;
    border-radius: var(--border-radius);
    box-shadow: 0 4px 10px rgba(0,0,0,0.05);
}
.content-header h1 {
    margin: 0;
    font-size: 24px;
    font-weight: 500;
    color: #6a3093;
    letter-spacing: 0.5px;
}
.content-header .actions {
    display: flex;
    gap: 12px;
}
/* 闪烁星星效果 */
.sparkle {
    position: relative;
    display: inline-block;
    animation: sparkle 2s infinite;
}
@keyframes sparkle {
    0%, 100% { transform: scale(1); opacity: 1; }
    50% { transform: scale(1.1); opacity: 0.9; }
}
/* 按钮样式 */
.btn {
    padding: 8px 16px;
    border-radius: 20px;
    font-size: 14px;
    font-weight: 500;
    transition: var(--transition);
    border: none;
    display: flex;
    align-items: center;
    gap: 6px;
    box-shadow: 0 3px 8px rgba(0,0,0,0.05);
}
.btn-blossom {
    background: linear-gradient(45deg, #ffcee0, #b5c0ff);
    color: #634a7a;
}
.btn-primary-soft {
    background: linear-gradient(135deg, #a1c4fd, #c2e9fb);
    color: #4a4a4a;
}
.btn-secondary-soft {
    background: linear-gradient(135deg, #e2c9fa, #d3f9fb);
    color: #4a4a4a;
}
.btn-danger-soft {
    background: linear-gradient(135deg, #ffb8c6, #ffdfd3);
    color: #a55;
}
.btn:hover {
    transform: translateY(-2px);
    box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.btn-glow {
    animation: glow 1s ease-in-out infinite alternate;
}
@keyframes glow {
    from {
        box-shadow: 0 0 5px rgba(156, 136, 255, 0.3);
    }
    to {
        box-shadow: 0 0 15px rgba(156, 136, 255, 0.7);
    }
}
/* 筛选面板 */
.filter-panel {
    background: rgba(255, 255, 255, 0.9);
    border-radius: var(--border-radius);
    padding: 20px;
    margin-bottom: 25px;
    box-shadow: 0 5px 15px rgba(0,0,0,0.05);
    border: 1px solid rgba(248, 200, 220, 0.3);
}
.filter-panel-header {
    margin-bottom: 15px;
    text-align: center;
}
.filter-title {
    font-size: 18px;
    color: #9c88ff;
    font-weight: 500;
    font-family: 'Dancing Script', cursive;
    font-size: 24px;
}
.snowflake-divider {
    display: flex;
    justify-content: center;
    gap: 15px;
    margin: 8px 0;
    color: var(--primary-color);
    font-size: 14px;
    opacity: 0.7;
}
.filter-row {
    display: flex;
    flex-wrap: wrap;
    gap: 15px;
    margin-bottom: 15px;
}
.filter-item {
    flex: 1;
    min-width: 200px;
}
.filter-item label {
    display: block;
    margin-bottom: 8px;
    font-weight: 500;
    color: #7e6d94;
    font-size: 14px;
}
.elegant-select,
.elegant-input {
    width: 100%;
    padding: 10px;
    border: 1px solid #e0d0f0;
    border-radius: 8px;
    background-color: rgba(255, 255, 255, 0.8);
    color: var(--text-color);
    transition: var(--transition);
    box-shadow: inset 0 1px 3px rgba(0,0,0,0.05);
}
.elegant-select:focus,
.elegant-input:focus {
    outline: none;
    border-color: var(--primary-color);
    box-shadow: 0 0 0 3px rgba(156, 136, 255, 0.2);
}
.filter-actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 15px;
}
/* 日期范围样式 */
.date-range-inputs {
    padding-top: 15px;
    margin-top: 5px;
    border-top: 1px dashed #e0d0f0;
}
/* 卡片效果 */
.glass-card {
    background: rgba(255, 255, 255, 0.8);
    backdrop-filter: blur(10px);
    border-radius: var(--border-radius);
    box-shadow: 0 8px 20px rgba(0,0,0,0.05);
    border: 1px solid rgba(255, 255, 255, 0.5);
    overflow: hidden;
}
.card-body {
    padding: 20px;
}
/* 表格样式 */
.table-container {
    overflow-x: auto;
    border-radius: 8px;
}
.elegant-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    color: var(--text-color);
}
.elegant-table th {
    background: linear-gradient(to right, var(--soft-purple), var(--soft-pink));
    color: #6a4c93;
    font-weight: 500;
    text-align: left;
    padding: 12px 15px;
    font-size: 14px;
    border: none;
}
.elegant-table th:first-child {
    border-top-left-radius: 8px;
}
.elegant-table th:last-child {
    border-top-right-radius: 8px;
}
.elegant-table td {
    padding: 12px 15px;
    border-bottom: 1px solid rgba(224, 208, 240, 0.3);
    font-size: 14px;
    transition: var(--transition);
}
.elegant-table tr:last-child td {
    border-bottom: none;
}
.elegant-table tr:hover td {
    background-color: rgba(248, 239, 255, 0.6);
}
/* 用户徽章样式 */
.user-badge {
    background: linear-gradient(45deg, #a1c4fd, #c2e9fb);
    padding: 4px 10px;
    border-radius: 12px;
    font-size: 12px;
    color: #4a4a4a;
    display: inline-block;
}
/* 空数据提示 */
.empty-container {
    padding: 30px;
    text-align: center;
    color: var(--light-text);
}
.empty-container i {
    font-size: 40px;
    color: #d0c0e0;
    margin-bottom: 15px;
}
.empty-container p {
    margin: 0;
    font-size: 16px;
}
/* 分页样式 */
.pagination-wrapper {
    display: flex;
    justify-content: center;
    margin-top: 25px;
}
.pagination-container {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
    background: rgba(248, 239, 255, 0.5);
    padding: 15px 20px;
    border-radius: 25px;
}
.page-btn {
    padding: 6px 15px;
    border-radius: 20px;
    background: linear-gradient(45deg, #e2bbec, #b6cefd);
    color: #634a7a;
    border: none;
    transition: var(--transition);
    text-decoration: none;
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 13px;
}
.page-btn:hover {
    transform: translateY(-2px);
    box-shadow: 0 5px 10px rgba(0,0,0,0.1);
    text-decoration: none;
    color: #4a3a5a;
}
.page-info {
    color: var(--light-text);
    font-size: 14px;
}
/* 模态框样式 */
.modal-elegant {
    max-width: 400px;
}
.modal-content {
    border-radius: 15px;
    border: none;
    box-shadow: 0 10px 30px rgba(0,0,0,0.1);
    overflow: hidden;
    background: rgba(255, 255, 255, 0.95);
}
.modal-header {
    background: linear-gradient(135deg, #f8c8dc, #c8e7f8);
    border-bottom: none;
    padding: 15px 20px;
}
.modal-header .modal-title {
    color: #634a7a;
    font-weight: 500;
    display: flex;
    align-items: center;
    gap: 8px;
}
.modal-body {
    padding: 20px;
}
.modal-message {
    color: #7e6d94;
    margin-bottom: 15px;
}
.elegant-alert {
    background-color: rgba(255, 248, 225, 0.7);
    border: 1px solid #ffeeba;
    color: #856404;
    border-radius: 8px;
    padding: 12px 15px;
    display: flex;
    align-items: center;
    gap: 10px;
}
.modal-footer {
    background: rgba(248, 239, 255, 0.5);
    border-top: none;
    padding: 15px 20px;
}
/* 行动画效果 */
.fade-in-row {
    animation: fadeIn 0.5s ease-out forwards;
    opacity: 0;
}
@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(10px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}
/* 响应式调整 */
@media (max-width: 768px) {
    .filter-item {
        min-width: 100%;
    }
    .content-header {
        flex-direction: column;
        align-items: flex-start;
    }
    .content-header .actions {
        margin-top: 15px;
    }
    .pagination-container {
        flex-direction: column;
        gap: 15px;
    }
}
================================================================================
File: ./app/static/css/book-form.css
================================================================================
/* ========== 基础重置和变量 ========== */
:root {
    --primary-color: #3b82f6;
    --primary-hover: #2563eb;
    --primary-light: #eff6ff;
    --danger-color: #ef4444;
    --success-color: #10b981;
    --warning-color: #f59e0b;
    --info-color: #3b82f6;
    --text-dark: #1e293b;
    --text-medium: #475569;
    --text-light: #64748b;
    --text-muted: #94a3b8;
    --border-color: #e2e8f0;
    --border-focus: #bfdbfe;
    --bg-white: #ffffff;
    --bg-light: #f8fafc;
    --bg-lightest: #f1f5f9;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
    --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
    --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
    --radius-sm: 4px;
    --radius-md: 6px;
    --radius-lg: 8px;
    --radius-xl: 12px;
    --transition-fast: 0.15s ease;
    --transition-base: 0.3s ease;
    --transition-slow: 0.5s ease;
    --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
/* ========== 全局样式 ========== */
.book-form-container {
    padding: 24px;
    max-width: 1400px;
    margin: 0 auto;
    font-family: var(--font-sans);
    color: var(--text-dark);
}
/* ========== 页头样式 ========== */
.page-header-wrapper {
    margin-bottom: 24px;
    background-color: var(--bg-white);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 24px;
}
.header-title-section {
    display: flex;
    flex-direction: column;
}
.page-title {
    font-size: 1.5rem;
    font-weight: 600;
    color: var(--text-dark);
    margin: 0;
}
.subtitle {
    margin: 8px 0 0 0;
    color: var(--text-medium);
    font-size: 0.9rem;
}
.header-actions {
    display: flex;
    align-items: center;
    gap: 16px;
}
.btn-back {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--text-medium);
    background-color: var(--bg-lightest);
    border-radius: var(--radius-md);
    padding: 8px 16px;
    font-size: 0.875rem;
    font-weight: 500;
    transition: all var(--transition-fast);
    text-decoration: none;
    box-shadow: var(--shadow-sm);
}
.btn-back:hover {
    background-color: var(--border-color);
    color: var(--text-dark);
    text-decoration: none;
}
.btn-back i {
    font-size: 14px;
}
/* 进度条样式 */
.form-progress {
    min-width: 180px;
}
.progress-bar-container {
    height: 6px;
    background-color: var(--bg-lightest);
    border-radius: 3px;
    overflow: hidden;
}
.progress-bar {
    height: 100%;
    background-color: var(--primary-color);
    border-radius: 3px;
    transition: width var(--transition-base);
}
.progress-text {
    font-size: 0.75rem;
    color: var(--text-light);
    text-align: right;
    display: block;
    margin-top: 4px;
}
/* ========== 表单布局 ========== */
.form-grid {
    display: grid;
    grid-template-columns: 1fr 360px;
    gap: 24px;
}
.form-main-content {
    display: flex;
    flex-direction: column;
    gap: 24px;
}
.form-sidebar {
    display: flex;
    flex-direction: column;
    gap: 24px;
}
/* ========== 表单卡片样式 ========== */
.form-card {
    background-color: var(--bg-white);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
    transition: box-shadow var(--transition-base);
}
.form-card:hover {
    box-shadow: var(--shadow-md);
}
.card-header {
    padding: 16px 20px;
    background-color: var(--bg-white);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    align-items: center;
}
.card-title {
    font-weight: 600;
    color: var(--text-dark);
    font-size: 0.9375rem;
}
.card-body {
    padding: 20px;
}
.form-section {
    padding: 0;
}
/* ========== 表单元素样式 ========== */
.form-row {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 16px;
    margin-bottom: 16px;
}
.form-group {
    margin-bottom: 20px;
}
.form-group:last-child {
    margin-bottom: 0;
}
.form-label {
    display: block;
    font-weight: 500;
    color: var(--text-dark);
    margin-bottom: 8px;
    font-size: 0.9375rem;
}
.form-control {
    display: block;
    width: 100%;
    padding: 10px 14px;
    font-size: 0.9375rem;
    line-height: 1.5;
    color: var(--text-dark);
    background-color: var(--bg-white);
    background-clip: padding-box;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.form-control:focus {
    border-color: var(--border-focus);
    outline: 0;
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
}
.form-control::placeholder {
    color: var(--text-muted);
}
.form-control:disabled, .form-control[readonly] {
    background-color: var(--bg-lightest);
    opacity: 0.6;
}
.form-help {
    margin-top: 6px;
    font-size: 0.8125rem;
    color: var(--text-light);
}
.form-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 8px;
}
.char-counter {
    font-size: 0.8125rem;
    color: var(--text-muted);
}
/* 带按钮输入框 */
.input-with-button {
    display: flex;
    align-items: center;
}
.input-with-button .form-control {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
    flex-grow: 1;
}
.btn-append {
    height: 42px;
    padding: 0 14px;
    background-color: var(--bg-lightest);
    border: 1px solid var(--border-color);
    border-left: none;
    border-top-right-radius: var(--radius-md);
    border-bottom-right-radius: var(--radius-md);
    color: var(--text-medium);
    cursor: pointer;
    transition: background-color var(--transition-fast);
}
.btn-append:hover {
    background-color: var(--border-color);
    color: var(--text-dark);
}
/* 文本域 */
textarea.form-control {
    min-height: 150px;
    resize: vertical;
}
/* 数字输入控件 */
.number-control {
    display: flex;
    align-items: center;
    width: 100%;
    border-radius: var(--radius-md);
    overflow: hidden;
}
.number-btn {
    width: 42px;
    height: 42px;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--bg-lightest);
    border: 1px solid var(--border-color);
    color: var(--text-medium);
    cursor: pointer;
    transition: all var(--transition-fast);
    font-size: 1rem;
    user-select: none;
}
.number-btn:hover {
    background-color: var(--border-color);
    color: var(--text-dark);
}
.decrement {
    border-top-left-radius: var(--radius-md);
    border-bottom-left-radius: var(--radius-md);
}
.increment {
    border-top-right-radius: var(--radius-md);
    border-bottom-right-radius: var(--radius-md);
}
.number-control .form-control {
    flex: 1;
    border-radius: 0;
    border-left: none;
    border-right: none;
    text-align: center;
    padding: 10px 0;
}
/* 价格输入 */
.price-input {
    position: relative;
}
.currency-symbol {
    position: absolute;
    left: 14px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-medium);
}
.price-input .form-control {
    padding-left: 30px;
}
.price-slider {
    margin-top: 16px;
}
.range-slider {
    -webkit-appearance: none;
    width: 100%;
    height: 4px;
    border-radius: 2px;
    background-color: var(--border-color);
    outline: none;
    margin: 14px 0;
}
.range-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--primary-color);
    cursor: pointer;
    border: 2px solid var(--bg-white);
    box-shadow: var(--shadow-sm);
}
.slider-marks {
    display: flex;
    justify-content: space-between;
    font-size: 0.75rem;
    color: var(--text-light);
}
/* ========== 按钮样式 ========== */
.btn-primary {
    padding: 12px 16px;
    background-color: var(--primary-color);
    color: white;
    border: none;
    border-radius: var(--radius-md);
    font-weight: 500;
    font-size: 0.9375rem;
    cursor: pointer;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    transition: all var(--transition-fast);
    box-shadow: var(--shadow-sm);
}
.btn-primary:hover {
    background-color: var(--primary-hover);
    box-shadow: var(--shadow-md);
    transform: translateY(-1px);
}
.btn-primary:focus {
    outline: none;
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3);
}
.btn-primary:active {
    transform: translateY(1px);
}
.btn-secondary {
    padding: 10px 16px;
    background-color: var(--bg-white);
    color: var(--text-medium);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    font-weight: 500;
    font-size: 0.9375rem;
    cursor: pointer;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    transition: all var(--transition-fast);
}
.btn-secondary:hover {
    background-color: var(--bg-lightest);
    color: var(--text-dark);
}
.btn-secondary:focus {
    outline: none;
    box-shadow: 0 0 0 3px rgba(226, 232, 240, 0.5);
}
/* ========== 标签输入样式 ========== */
.tag-input-wrapper {
    display: flex;
    align-items: center;
    gap: 8px;
}
.tag-input-wrapper .form-control {
    flex-grow: 1;
}
.btn-tag-add {
    width: 42px;
    height: 42px;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--primary-color);
    border: none;
    border-radius: var(--radius-md);
    color: white;
    cursor: pointer;
    transition: all var(--transition-fast);
}
.btn-tag-add:hover {
    background-color: var(--primary-hover);
}
.tags-container {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 12px;
    min-height: 32px;
}
.tag {
    display: inline-flex;
    align-items: center;
    background-color: var(--primary-light);
    border-radius: 50px;
    padding: 6px 10px 6px 14px;
    font-size: 0.8125rem;
    color: var(--primary-color);
    transition: all var(--transition-fast);
}
.tag:hover {
    background-color: rgba(59, 130, 246, 0.2);
}
.tag-text {
    margin-right: 6px;
}
.tag-remove {
    background: none;
    border: none;
    color: var(--primary-color);
    cursor: pointer;
    padding: 0;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.75rem;
    transition: all var(--transition-fast);
}
.tag-remove:hover {
    background-color: rgba(59, 130, 246, 0.3);
    color: white;
}
/* ========== 封面上传区域 ========== */
.cover-preview-container {
    display: flex;
    flex-direction: column;
    gap: 16px;
}
.cover-preview {
    width: 100%;
    aspect-ratio: 5/7;
    background-color: var(--bg-lightest);
    border-radius: var(--radius-md);
    overflow: hidden;
    cursor: pointer;
    transition: all var(--transition-fast);
}
.cover-preview:hover {
    background-color: var(--bg-light);
}
.cover-preview.dragover {
    background-color: var(--primary-light);
}
.cover-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.no-cover-placeholder {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    color: var(--text-light);
    padding: 24px;
    text-align: center;
}
.no-cover-placeholder i {
    font-size: 48px;
    margin-bottom: 16px;
    color: var(--text-muted);
}
.placeholder-tip {
    font-size: 0.8125rem;
    margin-top: 8px;
    color: var(--text-muted);
}
.upload-options {
    display: flex;
    flex-direction: column;
    gap: 12px;
}
.upload-btn-group {
    display: flex;
    gap: 8px;
}
.btn-upload {
    flex-grow: 1;
    padding: 10px 16px;
    background-color: var(--primary-color);
    color: white;
    border: none;
    border-radius: var(--radius-md);
    font-weight: 500;
    font-size: 0.875rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    transition: all var(--transition-fast);
}
.btn-upload:hover {
    background-color: var(--primary-hover);
}
.btn-remove {
    width: 42px;
    height: 38px;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--bg-white);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    color: var(--text-medium);
    cursor: pointer;
    transition: all var(--transition-fast);
}
.btn-remove:hover {
    background-color: #fee2e2;
    border-color: #fca5a5;
    color: #ef4444;
}
.upload-tips {
    text-align: center;
    font-size: 0.75rem;
    color: var(--text-muted);
    line-height: 1.5;
}
/* ========== 表单提交区域 ========== */
.form-actions {
    display: flex;
    flex-direction: column;
    gap: 16px;
}
.secondary-actions {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 8px;
}
.form-tip {
    margin-top: 8px;
    font-size: 0.8125rem;
    color: var(--text-muted);
    text-align: center;
}
.form-tip i {
    color: var(--info-color);
    margin-right: 4px;
}
/* 必填项标记 */
.required {
    color: var(--danger-color);
    margin-left: 4px;
}
/* 无效输入状态 */
.is-invalid {
    border-color: var(--danger-color) !important;
}
.invalid-feedback {
    display: block;
    color: var(--danger-color);
    font-size: 0.8125rem;
    margin-top: 6px;
}
/* ========== Select2 定制 ========== */
.select2-container--classic .select2-selection--single {
    height: 42px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    background-color: var(--bg-white);
}
.select2-container--classic .select2-selection--single .select2-selection__rendered {
    line-height: 40px;
    color: var(--text-dark);
    padding-left: 14px;
}
.select2-container--classic .select2-selection--single .select2-selection__arrow {
    height: 40px;
    border-left: 1px solid var(--border-color);
}
.select2-container--classic .select2-selection--single:focus {
    border-color: var(--border-focus);
    outline: 0;
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
}
/* ========== 模态框样式 ========== */
.modal-content {
    border: none;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}
.modal-header {
    background-color: var(--bg-white);
    border-bottom: 1px solid var(--border-color);
    padding: 16px 20px;
}
.modal-title {
    font-weight: 600;
    color: var(--text-dark);
    font-size: 1.125rem;
}
.modal-body {
    padding: 20px;
}
.modal-footer {
    border-top: 1px solid var(--border-color);
    padding: 16px 20px;
}
.modal-btn {
    min-width: 100px;
}
/* 裁剪模态框 */
.img-container {
    max-height: 500px;
    overflow: hidden;
    margin-bottom: 20px;
}
#cropperImage {
    display: block;
    max-width: 100%;
}
.cropper-controls {
    display: flex;
    justify-content: center;
    gap: 20px;
    margin-top: 16px;
}
.control-group {
    display: flex;
    gap: 8px;
}
.control-btn {
    width: 40px;
    height: 40px;
    border-radius: var(--radius-md);
    background-color: var(--bg-lightest);
    border: 1px solid var(--border-color);
    color: var(--text-medium);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: all var(--transition-fast);
}
.control-btn:hover {
    background-color: var(--border-color);
    color: var(--text-dark);
}
/* 图书预览模态框 */
.preview-header {
    background-color: var(--bg-white);
    border-bottom: 1px solid var(--border-color);
}
.preview-body {
    padding: 0;
    background-color: var(--bg-lightest);
}
/* 添加到你的CSS文件中 */
.book-preview {
    display: flex;
    flex-direction: row;
    gap: 20px;
}
.preview-cover-section {
    flex: 0 0 200px;
}
.preview-details-section {
    flex: 1;
}
.book-preview-cover {
    height: 280px;
    width: 200px;
    overflow: hidden;
    border-radius: 4px;
    border: 1px solid #ddd;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #f8f9fa;
}
.preview-cover-img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}
.preview-tag {
    display: inline-block;
    background: #e9ecef;
    color: #495057;
    padding: 3px 8px;
    border-radius: 12px;
    font-size: 12px;
    margin-right: 5px;
    margin-bottom: 5px;
}
.book-tags-preview {
    margin: 15px 0;
}
.book-description-preview {
    margin-top: 20px;
}
.section-title {
    font-size: 16px;
    margin-bottom: 10px;
    color: #495057;
    border-bottom: 1px solid #dee2e6;
    padding-bottom: 5px;
}
.book-meta {
    margin-top: 10px;
    text-align: center;
}
.book-price {
    font-size: 18px;
    font-weight: bold;
    color: #dc3545;
}
.book-stock {
    font-size: 14px;
    color: #6c757d;
}
/* 响应式调整 */
@media (max-width: 768px) {
    .book-preview {
        flex-direction: column;
    }
    .preview-cover-section {
        margin: 0 auto;
    }
}
.preview-details-section {
    padding: 24px;
}
.book-title {
    font-size: 1.5rem;
    font-weight: 600;
    color: var(--text-dark);
    margin: 0 0 8px 0;
}
.book-author {
    color: var(--text-medium);
    font-size: 1rem;
    margin-bottom: 24px;
}
.book-info-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 16px;
    background-color: var(--bg-white);
    border-radius: var(--radius-md);
    padding: 16px;
    box-shadow: var(--shadow-sm);
    margin-bottom: 24px;
}
.info-item {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.info-label {
    font-size: 0.75rem;
    color: var(--text-light);
    text-transform: uppercase;
}
.info-value {
    font-weight: 500;
    color: var(--text-dark);
}
.book-tags-preview {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 24px;
}
.preview-tag {
    display: inline-block;
    background-color: var(--primary-light);
    color: var(--primary-color);
    padding: 4px 12px;
    border-radius: 50px;
    font-size: 0.8125rem;
}
.no-tags {
    font-size: 0.875rem;
    color: var(--text-muted);
}
.book-description-preview {
    background-color: var(--bg-white);
    border-radius: var(--radius-md);
    padding: 16px;
    box-shadow: var(--shadow-sm);
}
.section-title {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text-dark);
    margin: 0 0 12px 0;
}
.description-content {
    font-size: 0.9375rem;
    color: var(--text-medium);
    line-height: 1.6;
}
.placeholder-text {
    color: var(--text-muted);
    font-style: italic;
}
.preview-footer {
    background-color: var(--bg-white);
    display: flex;
    justify-content: flex-end;
    gap: 12px;
}
/* ========== 通知样式 ========== */
.notification-container {
    position: fixed;
    top: 20px;
    right: 20px;
    z-index: 9999;
    display: flex;
    flex-direction: column;
    gap: 12px;
    max-width: 320px;
}
.notification {
    background-color: var(--bg-white);
    border-radius: var(--radius-md);
    padding: 12px 16px;
    box-shadow: var(--shadow-md);
    display: flex;
    align-items: center;
    gap: 12px;
    animation-duration: 0.5s;
}
.success-notification {
    border-left: 4px solid var(--success-color);
}
.error-notification {
    border-left: 4px solid var(--danger-color);
}
.warning-notification {
    border-left: 4px solid var(--warning-color);
}
.info-notification {
    border-left: 4px solid var(--info-color);
}
.notification-icon {
    color: var(--text-light);
}
.success-notification .notification-icon {
    color: var(--success-color);
}
.error-notification .notification-icon {
    color: var(--danger-color);
}
.warning-notification .notification-icon {
    color: var(--warning-color);
}
.info-notification .notification-icon {
    color: var(--info-color);
}
.notification-content {
    flex-grow: 1;
}
.notification-content p {
    margin: 0;
    font-size: 0.875rem;
    color: var(--text-dark);
}
.notification-close {
    background: none;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    padding: 5px;
    transition: color var(--transition-fast);
}
.notification-close:hover {
    color: var(--text-medium);
}
/* ========== 动画效果 ========== */
@keyframes pulse {
    0% {
        box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);
    }
    70% {
        box-shadow: 0 0 0 8px rgba(59, 130, 246, 0);
    }
    100% {
        box-shadow: 0 0 0 0 rgba(59, 130, 246, 0);
    }
}
.pulse {
    animation: pulse 2s infinite;
}
/* ========== 响应式样式 ========== */
@media (max-width: 1200px) {
    .form-grid {
        grid-template-columns: 1fr 320px;
        gap: 20px;
    }
}
@media (max-width: 992px) {
    .page-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 16px;
    }
    .header-actions {
        width: 100%;
        justify-content: space-between;
    }
    .form-grid {
        grid-template-columns: 1fr;
    }
    .book-preview {
        grid-template-columns: 1fr;
    }
    .preview-cover-section {
        border-right: none;
        border-bottom: 1px solid var(--border-color);
        padding-bottom: 24px;
    }
    .book-preview-cover {
        max-width: 240px;
        margin: 0 auto;
    }
}
@media (max-width: 768px) {
    .book-form-container {
        padding: 16px 12px;
    }
    .page-header {
        padding: 20px;
    }
    .form-row {
        grid-template-columns: 1fr;
        gap: 12px;
    }
    .secondary-actions {
        grid-template-columns: 1fr;
    }
    .card-body {
        padding: 16px;
    }
    .book-info-grid {
        grid-template-columns: 1fr;
    }
}
.cover-preview {
    min-height: 250px;
    width: 100%;
    border: 1px dashed #ccc;
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    position: relative;
}
.cover-preview img.cover-image {
    max-width: 100%;
    max-height: 300px;
    object-fit: contain;
}
.img-container {
    max-height: 500px;
    overflow: auto;
}
#cropperImage {
    max-width: 100%;
    display: block;
}
================================================================================
File: ./app/static/css/borrow_statistics.css
================================================================================
/* app/static/css/borrow_statistics.css */
/* 确保与 statistics.css 兼容的样式 */
.stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 15px;
    margin-top: 15px;
}
.stats-item {
    background-color: var(--secondary-color);
    border-radius: 12px;
    padding: 20px 15px;
    text-align: center;
    transition: all 0.3s ease;
    border: 1px solid var(--border-color);
    box-shadow: 0 4px 12px var(--shadow-color);
    position: relative;
    overflow: hidden;
}
.stats-item:hover {
    transform: translateY(-5px);
    box-shadow: 0 8px 20px var(--shadow-color);
    background-color: white;
}
.stats-item::after {
    content: '';
    position: absolute;
    bottom: -15px;
    right: -15px;
    width: 50px;
    height: 50px;
    border-radius: 50%;
    background-color: var(--primary-color);
    opacity: 0.1;
    transition: all 0.3s ease;
}
.stats-item:hover::after {
    transform: scale(1.2);
    opacity: 0.2;
}
.stats-value {
    font-size: 26px;
    font-weight: 700;
    margin-bottom: 8px;
    color: var(--accent-color);
    display: flex;
    justify-content: center;
    align-items: center;
    height: 40px;
    position: relative;
}
.stats-value::before {
    content: '';
    position: absolute;
    bottom: -2px;
    left: 50%;
    transform: translateX(-50%);
    width: 40px;
    height: 2px;
    background-color: var(--primary-color);
    border-radius: 2px;
}
.stats-title {
    font-size: 14px;
    color: var(--light-text);
    font-weight: 500;
}
.loading {
    text-align: center;
    padding: 40px;
    color: var(--light-text);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}
.loader {
    border: 4px solid rgba(244, 188, 204, 0.3);
    border-top: 4px solid var(--accent-color);
    border-radius: 50%;
    width: 40px;
    height: 40px;
    animation: spin 1s linear infinite;
    margin-bottom: 15px;
}
@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}
/* 修复图表容器 */
.chart-container {
    margin-bottom: 30px;
}
.chart-wrapper {
    position: relative;
    height: 300px;
    width: 100%;
}
.trend-chart .chart-wrapper {
    height: 330px;
}
/* 确保图表正确渲染 */
canvas {
    max-width: 100%;
    height: auto !important;
}
/* 添加一些女性化的装饰元素 */
.chart-container::before {
    content: '';
    position: absolute;
    top: -15px;
    left: -15px;
    width: 60px;
    height: 60px;
    border-radius: 50%;
    background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
    opacity: 0.4;
    z-index: 0;
}
.chart-container::after {
    content: '';
    position: absolute;
    bottom: -15px;
    right: -15px;
    width: 60px;
    height: 60px;
    border-radius: 50%;
    background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
    opacity: 0.4;
    z-index: 0;
}
/* 新增部分 */
.intro-text {
    text-align: center;
    margin-bottom: 25px;
    font-size: 16px;
    font-weight: 300;
    color: var(--light-text);
    font-style: italic;
}
.insights-container {
    background-color: var(--secondary-color);
    border-radius: 15px;
    padding: 25px;
    margin-top: 30px;
    box-shadow: 0 5px 20px var(--shadow-color);
    border: 1px solid var(--border-color);
    position: relative;
    overflow: hidden;
}
.insights-container h3 {
    color: var(--accent-color);
    font-size: 1.3rem;
    margin-bottom: 15px;
    font-weight: 600;
    text-align: center;
    position: relative;
}
.insights-container h3::after {
    content: '';
    position: absolute;
    bottom: -5px;
    left: 50%;
    transform: translateX(-50%);
    width: 60px;
    height: 2px;
    background: linear-gradient(to right, var(--secondary-color), var(--accent-color), var(--secondary-color));
    border-radius: 3px;
}
.insights-content {
    line-height: 1.6;
    color: var(--text-color);
    text-align: center;
    position: relative;
    z-index: 1;
}
.insights-container::before {
    content: '';
    position: absolute;
    top: -30px;
    right: -30px;
    width: 100px;
    height: 100px;
    border-radius: 50%;
    background-color: var(--primary-color);
    opacity: 0.1;
}
/* 优雅的动画效果 */
@keyframes fadeInUp {
    from {
        opacity: 0;
        transform: translateY(20px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}
.animate-fadeInUp {
    animation: fadeInUp 0.8s ease forwards;
}
/* 确保响应式布局 */
@media (max-width: 768px) {
    .chart-row {
        flex-direction: column;
    }
    .half {
        width: 100%;
        min-width: 0;
    }
    .stats-grid {
        grid-template-columns: repeat(2, 1fr);
    }
    .chart-wrapper {
        height: 250px;
    }
}
================================================================================
File: ./app/static/css/categories.css
================================================================================
/* 分类管理页面样式 */
.categories-container {
    padding: 20px;
}
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
    padding-bottom: 15px;
    border-bottom: 1px solid #eee;
}
.card {
    margin-bottom: 20px;
    border: 1px solid rgba(0,0,0,0.125);
    border-radius: 0.25rem;
}
.card-header {
    padding: 0.75rem 1.25rem;
    background-color: rgba(0,0,0,0.03);
    border-bottom: 1px solid rgba(0,0,0,0.125);
    font-weight: 600;
}
.card-body {
    padding: 1.25rem;
}
.category-table {
    border: 1px solid #eee;
}
.category-table th {
    background-color: #f8f9fa;
}
.no-categories {
    text-align: center;
    padding: 30px;
    color: #888;
}
.no-categories i {
    font-size: 48px;
    color: #ddd;
    margin-bottom: 10px;
}
/* 通知弹窗 */
.notification-alert {
    position: fixed;
    top: 20px;
    right: 20px;
    min-width: 300px;
    z-index: 1050;
}
/* 响应式调整 */
@media (max-width: 768px) {
    .page-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 15px;
    }
}
================================================================================
File: ./app/static/css/user-edit.css
================================================================================
/* 用户编辑页面样式 */
.user-edit-container {
    padding: 20px;
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
/* 页面标题和操作按钮 */
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 25px;
    padding-bottom: 15px;
    border-bottom: 1px solid #f0f0f0;
}
.page-header h1 {
    font-size: 1.8rem;
    color: #333;
    margin: 0;
}
.page-header .actions {
    display: flex;
    gap: 10px;
}
/* 卡片样式 */
.card {
    border: none;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
    margin-bottom: 20px;
}
.card-body {
    padding: 25px;
}
/* 表单样式 */
.form-group {
    margin-bottom: 20px;
}
.form-group label {
    font-weight: 500;
    margin-bottom: 8px;
    color: #333;
    display: block;
}
.form-control {
    height: auto;
    padding: 10px 15px;
    border: 1px solid #ddd;
    border-radius: 4px;
    font-size: 0.95rem;
    transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.form-control:focus {
    border-color: #4c84ff;
    box-shadow: 0 0 0 0.2rem rgba(76, 132, 255, 0.25);
}
.form-control[readonly] {
    background-color: #f8f9fa;
    opacity: 0.7;
}
.form-text {
    font-size: 0.85rem;
    margin-top: 5px;
}
.form-row {
    margin-right: -15px;
    margin-left: -15px;
    display: flex;
    flex-wrap: wrap;
}
.col-md-6 {
    flex: 0 0 50%;
    max-width: 50%;
    padding-right: 15px;
    padding-left: 15px;
}
.col-md-12 {
    flex: 0 0 100%;
    max-width: 100%;
    padding-right: 15px;
    padding-left: 15px;
}
/* 用户信息框 */
.user-info-box {
    margin-top: 20px;
    margin-bottom: 20px;
    padding: 20px;
    background-color: #f8f9fa;
    border-radius: 4px;
    display: flex;
    flex-wrap: wrap;
}
.info-item {
    flex: 0 0 auto;
    margin-right: 30px;
    margin-bottom: 10px;
}
.info-label {
    font-weight: 500;
    color: #666;
    margin-right: 5px;
}
.info-value {
    color: #333;
}
/* 表单操作区域 */
.form-actions {
    display: flex;
    justify-content: flex-start;
    gap: 10px;
    margin-top: 30px;
    padding-top: 20px;
    border-top: 1px solid #f0f0f0;
}
.btn {
    padding: 8px 16px;
    border-radius: 4px;
    transition: all 0.2s ease;
}
.btn-primary {
    background-color: #4c84ff;
    border-color: #4c84ff;
}
.btn-primary:hover {
    background-color: #3a70e9;
    border-color: #3a70e9;
}
.btn-secondary {
    background-color: #f8f9fa;
    border-color: #ddd;
    color: #333;
}
.btn-secondary:hover {
    background-color: #e9ecef;
    border-color: #ccc;
}
.btn-outline-secondary {
    color: #6c757d;
    border-color: #6c757d;
}
.btn-outline-secondary:hover {
    color: #fff;
    background-color: #6c757d;
    border-color: #6c757d;
}
/* 表单分隔线 */
.form-divider {
    height: 1px;
    background-color: #f0f0f0;
    margin: 30px 0;
}
/* 警告和错误状态 */
.is-invalid {
    border-color: #dc3545 !important;
}
.invalid-feedback {
    display: block;
    width: 100%;
    margin-top: 5px;
    font-size: 0.85rem;
    color: #dc3545;
}
/* 成功消息样式 */
.alert-success {
    color: #155724;
    background-color: #d4edda;
    border-color: #c3e6cb;
    padding: 15px;
    margin-bottom: 20px;
    border-radius: 4px;
}
/* 错误消息样式 */
.alert-error, .alert-danger {
    color: #721c24;
    background-color: #f8d7da;
    border-color: #f5c6cb;
    padding: 15px;
    margin-bottom: 20px;
    border-radius: 4px;
}
/* 响应式调整 */
@media (max-width: 768px) {
    .form-row {
        flex-direction: column;
    }
    .col-md-6, .col-md-12 {
        flex: 0 0 100%;
        max-width: 100%;
    }
    .page-header {
        flex-direction: column;
        align-items: flex-start;
    }
    .page-header .actions {
        margin-top: 15px;
    }
    .user-info-box {
        flex-direction: column;
    }
    .info-item {
        margin-right: 0;
    }
}
================================================================================
File: ./app/static/css/user-profile.css
================================================================================
/* 用户个人中心页面样式 */
.profile-container {
    padding: 20px;
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
/* 页面标题 */
.page-header {
    margin-bottom: 25px;
    padding-bottom: 15px;
    border-bottom: 1px solid #f0f0f0;
}
.page-header h1 {
    font-size: 1.8rem;
    color: #333;
    margin: 0;
}
/* 个人中心内容布局 */
.profile-content {
    display: flex;
    gap: 30px;
}
/* 左侧边栏 */
.profile-sidebar {
    flex: 0 0 300px;
    background-color: #f8f9fa;
    border-radius: 8px;
    padding: 25px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
/* 右侧主要内容 */
.profile-main {
    flex: 1;
    min-width: 0; /* 防止内容溢出 */
}
/* 用户头像容器 */
.user-avatar-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-bottom: 25px;
    padding-bottom: 25px;
    border-bottom: 1px solid #e9ecef;
}
/* 大头像样式 */
.user-avatar.large {
    width: 120px;
    height: 120px;
    border-radius: 50%;
    background-color: #4c84ff;
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 3rem;
    margin-bottom: 15px;
    box-shadow: 0 4px 8px rgba(76, 132, 255, 0.2);
}
.user-name {
    font-size: 1.5rem;
    margin: 10px 0 5px;
    color: #333;
}
.user-role {
    font-size: 0.9rem;
    color: #6c757d;
    margin: 0;
}
/* 用户统计信息 */
.user-stats {
    display: flex;
    justify-content: space-between;
    margin-bottom: 25px;
    padding-bottom: 25px;
    border-bottom: 1px solid #e9ecef;
}
.stat-item {
    text-align: center;
    flex: 1;
}
.stat-value {
    font-size: 1.8rem;
    font-weight: 600;
    color: #4c84ff;
    line-height: 1;
    margin-bottom: 5px;
}
.stat-label {
    font-size: 0.85rem;
    color: #6c757d;
}
/* 账户信息样式 */
.account-info {
    margin-bottom: 10px;
}
.account-info .info-row {
    display: flex;
    justify-content: space-between;
    margin-bottom: 15px;
    font-size: 0.95rem;
}
.account-info .info-label {
    color: #6c757d;
    font-weight: 500;
}
.account-info .info-value {
    color: #333;
    text-align: right;
    word-break: break-all;
}
/* 选项卡导航样式 */
.nav-tabs {
    border-bottom: 1px solid #dee2e6;
    margin-bottom: 25px;
}
.nav-tabs .nav-link {
    border: none;
    color: #6c757d;
    padding: 12px 15px;
    margin-right: 5px;
    border-bottom: 2px solid transparent;
    transition: all 0.2s ease;
}
.nav-tabs .nav-link:hover {
    color: #4c84ff;
    border-bottom-color: #4c84ff;
}
.nav-tabs .nav-link.active {
    font-weight: 500;
    color: #4c84ff;
    border-bottom: 2px solid #4c84ff;
    background-color: transparent;
}
.nav-tabs .nav-link i {
    margin-right: 5px;
}
/* 表单区域 */
.form-section {
    padding: 20px;
    background-color: #f9f9fb;
    border-radius: 8px;
    margin-bottom: 20px;
}
.form-section h4 {
    margin-top: 0;
    margin-bottom: 20px;
    color: #333;
    font-size: 1.2rem;
    font-weight: 500;
}
.form-group {
    margin-bottom: 20px;
}
.form-group label {
    font-weight: 500;
    margin-bottom: 8px;
    color: #333;
    display: block;
}
.form-control {
    height: auto;
    padding: 10px 15px;
    border: 1px solid #ddd;
    border-radius: 4px;
    font-size: 0.95rem;
    transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.form-control:focus {
    border-color: #4c84ff;
    box-shadow: 0 0 0 0.2rem rgba(76, 132, 255, 0.25);
}
.form-text {
    font-size: 0.85rem;
    margin-top: 5px;
}
/* 表单操作区域 */
.form-actions {
    margin-top: 25px;
    display: flex;
    justify-content: flex-start;
}
.btn {
    padding: 10px 20px;
    border-radius: 4px;
    transition: all 0.2s ease;
}
.btn-primary {
    background-color: #4c84ff;
    border-color: #4c84ff;
}
.btn-primary:hover {
    background-color: #3a70e9;
    border-color: #3a70e9;
}
/* 活动记录选项卡 */
.activity-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
}
.activity-filter {
    display: flex;
    align-items: center;
    gap: 10px;
}
.activity-filter label {
    margin-bottom: 0;
}
.activity-filter select {
    width: auto;
}
/* 活动时间线 */
.activity-timeline {
    padding: 20px;
    background-color: #f9f9fb;
    border-radius: 8px;
    min-height: 300px;
    position: relative;
}
.timeline-loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 250px;
}
.timeline-loading p {
    margin-top: 15px;
    color: #6c757d;
}
.timeline-item {
    position: relative;
    padding-left: 30px;
    padding-bottom: 25px;
    border-left: 2px solid #dee2e6;
}
.timeline-item:last-child {
    border-left: none;
}
.timeline-icon {
    position: absolute;
    left: -10px;
    top: 0;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background-color: #4c84ff;
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    font-size: 10px;
}
.timeline-content {
    background-color: white;
    border-radius: 6px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
    padding: 15px;
}
.timeline-header {
    display: flex;
    justify-content: space-between;
    margin-bottom: 10px;
}
.timeline-title {
    font-weight: 500;
    color: #333;
    margin: 0;
}
.timeline-time {
    font-size: 0.85rem;
    color: #6c757d;
}
.timeline-details {
    color: #555;
    font-size: 0.95rem;
}
.timeline-type-login .timeline-icon {
    background-color: #4caf50;
}
.timeline-type-borrow .timeline-icon {
    background-color: #2196f3;
}
.timeline-type-return .timeline-icon {
    background-color: #ff9800;
}
/* 通知样式 */
.alert {
    padding: 15px;
    margin-bottom: 20px;
    border-radius: 4px;
}
.alert-success {
    color: #155724;
    background-color: #d4edda;
    border: 1px solid #c3e6cb;
}
.alert-error, .alert-danger {
    color: #721c24;
    background-color: #f8d7da;
    border: 1px solid #f5c6cb;
}
/* 响应式调整 */
@media (max-width: 992px) {
    .profile-content {
        flex-direction: column;
    }
    .profile-sidebar {
        flex: none;
        width: 100%;
        margin-bottom: 20px;
    }
    .user-stats {
        justify-content: space-around;
    }
}
================================================================================
File: ./app/static/css/browse.css
================================================================================
/* 图书浏览页面样式 */
/* 全局容器 */
.browse-container {
    padding: 24px;
    background-color: #f6f9fc;
    min-height: calc(100vh - 60px);
    position: relative;
    overflow: hidden;
}
/* 装饰气泡 */
.bubble {
    position: absolute;
    bottom: -50px;
    background-color: rgba(221, 236, 255, 0.4);
    border-radius: 50%;
    z-index: 1;
    animation: bubble 25s infinite ease-in;
}
@keyframes bubble {
    0% {
        transform: translateY(100%) scale(0);
        opacity: 0;
    }
    50% {
        opacity: 0.6;
    }
    100% {
        transform: translateY(-100vh) scale(1);
        opacity: 0;
    }
}
/* 为页面添加15个泡泡 */
.bubble:nth-child(1) { left: 5%; width: 30px; height: 30px; animation-duration: 20s; animation-delay: 0s; }
.bubble:nth-child(2) { left: 15%; width: 20px; height: 20px; animation-duration: 18s; animation-delay: 1s; }
.bubble:nth-child(3) { left: 25%; width: 25px; height: 25px; animation-duration: 16s; animation-delay: 2s; }
.bubble:nth-child(4) { left: 35%; width: 15px; height: 15px; animation-duration: 15s; animation-delay: 0.5s; }
.bubble:nth-child(5) { left: 45%; width: 30px; height: 30px; animation-duration: 14s; animation-delay: 3s; }
.bubble:nth-child(6) { left: 55%; width: 20px; height: 20px; animation-duration: 13s; animation-delay: 2.5s; }
.bubble:nth-child(7) { left: 65%; width: 25px; height: 25px; animation-duration: 12s; animation-delay: 1.5s; }
.bubble:nth-child(8) { left: 75%; width: 15px; height: 15px; animation-duration: 11s; animation-delay: 4s; }
.bubble:nth-child(9) { left: 85%; width: 30px; height: 30px; animation-duration: 10s; animation-delay: 3.5s; }
.bubble:nth-child(10) { left: 10%; width: 18px; height: 18px; animation-duration: 19s; animation-delay: 0.5s; }
.bubble:nth-child(11) { left: 20%; width: 22px; height: 22px; animation-duration: 17s; animation-delay: 2.5s; }
.bubble:nth-child(12) { left: 30%; width: 28px; height: 28px; animation-duration: 16s; animation-delay: 1.2s; }
.bubble:nth-child(13) { left: 40%; width: 17px; height: 17px; animation-duration: 15s; animation-delay: 3.7s; }
.bubble:nth-child(14) { left: 60%; width: 23px; height: 23px; animation-duration: 13s; animation-delay: 2.1s; }
.bubble:nth-child(15) { left: 80%; width: 19px; height: 19px; animation-duration: 12s; animation-delay: 1.7s; }
/* 页面标题部分 */
.page-header {
    margin-bottom: 25px;
    position: relative;
    z-index: 2;
    text-align: center;
}
.page-header h1 {
    color: #3c4858;
    font-size: 2.2rem;
    font-weight: 700;
    margin: 0;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
}
.welcome-text {
    margin-top: 10px;
    color: #8492a6;
    font-size: 1.1rem;
}
.welcome-text strong {
    color: #764ba2;
}
/* 过滤和搜索部分 */
.filter-section {
    margin-bottom: 25px;
    padding: 20px;
    background-color: #ffffff;
    border-radius: 12px;
    box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
    position: relative;
    z-index: 2;
}
.search-form {
    display: flex;
    flex-direction: column;
    gap: 16px;
}
.search-row {
    margin-bottom: 5px;
    width: 100%;
}
.search-group {
    display: flex;
    width: 100%;
    max-width: 800px;
    margin: 0 auto;
}
.search-group .form-control {
    border: 1px solid #e4e7eb;
    border-right: none;
    border-radius: 25px 0 0 25px;
    padding: 10px 20px;
    height: 46px;
    font-size: 1rem;
    background-color: #f7fafc;
    box-shadow: none;
    transition: all 0.3s;
    flex: 1;
}
.search-group .form-control:focus {
    outline: none;
    border-color: #a3bffa;
    background-color: #ffffff;
    box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.25);
}
.search-group .btn {
    border-radius: 0 25px 25px 0;
    width: 46px;
    height: 46px;
    min-width: 46px;
    padding: 0;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-left: -1px;
    font-size: 1.1rem;
    box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11);
    transition: all 0.3s;
    border: none;
}
.search-group .btn:hover {
    transform: translateY(-1px);
    box-shadow: 0 7px 14px rgba(50, 50, 93, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08);
}
.filter-row {
    display: flex;
    flex-wrap: wrap;
    gap: 15px;
    width: 100%;
    align-items: center;
}
.category-filters {
    position: relative;
    flex: 2;
    min-width: 180px;
}
.category-filter-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 20px;
    width: 100%;
    height: 42px;
    background: #f7fafc;
    border: 1px solid #e4e7eb;
    border-radius: 25px;
    cursor: pointer;
    font-size: 0.95rem;
    color: #3c4858;
    transition: all 0.3s;
}
.category-filter-toggle:hover {
    background: #edf2f7;
}
.category-filter-toggle i.fa-chevron-down {
    margin-left: 8px;
    transition: transform 0.3s;
}
.category-filter-toggle.active i.fa-chevron-down {
    transform: rotate(180deg);
}
.category-filter-dropdown {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    margin-top: 8px;
    background: white;
    border-radius: 12px;
    box-shadow: 0 7px 14px rgba(50, 50, 93, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08);
    padding: 10px;
    z-index: 100;
    display: none;
    max-height: 300px;
    overflow-y: auto;
}
.category-filter-dropdown.show {
    display: block;
    animation: fadeIn 0.2s;
}
@keyframes fadeIn {
    from { opacity: 0; transform: translateY(-10px); }
    to { opacity: 1; transform: translateY(0); }
}
.category-item {
    display: flex;
    align-items: center;
    padding: 10px 15px;
    color: #3c4858;
    border-radius: 6px;
    text-decoration: none;
    margin-bottom: 5px;
    transition: all 0.2s;
}
.category-item:hover {
    background: #f7fafc;
    color: #667eea;
}
.category-item.active {
    background: #ebf4ff;
    color: #667eea;
    font-weight: 500;
}
.category-item i {
    margin-right: 10px;
}
.filter-group {
    flex: 1;
    min-width: 130px;
}
.filter-section .form-control {
    border: 1px solid #e4e7eb;
    border-radius: 25px;
    height: 42px;
    padding: 10px 20px;
    background-color: #f7fafc;
    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='%23667eea' 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;
    width: 100%;
    transition: all 0.3s;
}
.filter-section .form-control:focus {
    outline: none;
    border-color: #a3bffa;
    background-color: #ffffff;
    box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.25);
}
/* 图书统计显示 */
.browse-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    margin-bottom: 25px;
    align-items: center;
}
.stat-item {
    display: flex;
    align-items: center;
    background: white;
    padding: 12px 20px;
    border-radius: 12px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
    flex: 1;
    min-width: 160px;
    max-width: 240px;
}
.stat-item i {
    font-size: 24px;
    color: #667eea;
    margin-right: 15px;
    background: #ebf4ff;
    width: 45px;
    height: 45px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 12px;
}
.stat-content {
    display: flex;
    flex-direction: column;
}
.stat-value {
    font-size: 1.25rem;
    font-weight: 700;
    color: #3c4858;
}
.stat-label {
    font-size: 0.875rem;
    color: #8492a6;
}
.search-results {
    flex: 2;
    padding: 12px 20px;
    background: #ebf4ff;
    border-radius: 12px;
    color: #667eea;
    font-weight: 500;
    text-align: center;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
}
/* 图书网格布局 */
.books-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 25px;
    margin-bottom: 40px;
    position: relative;
    z-index: 2;
}
/* 图书卡片样式 */
.book-card {
    border-radius: 10px;
    overflow: hidden;
    background-color: white;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
    transition: all 0.3s ease;
    position: relative;
    height: 100%;
    display: flex;
    flex-direction: column;
    opacity: 0;
    transform: translateY(20px);
    animation: fadeInUp 0.5s forwards;
}
@keyframes fadeInUp {
    to {
        opacity: 1;
        transform: translateY(0);
    }
}
.books-grid .book-card:nth-child(1) { animation-delay: 0.1s; }
.books-grid .book-card:nth-child(2) { animation-delay: 0.15s; }
.books-grid .book-card:nth-child(3) { animation-delay: 0.2s; }
.books-grid .book-card:nth-child(4) { animation-delay: 0.25s; }
.books-grid .book-card:nth-child(5) { animation-delay: 0.3s; }
.books-grid .book-card:nth-child(6) { animation-delay: 0.35s; }
.books-grid .book-card:nth-child(7) { animation-delay: 0.4s; }
.books-grid .book-card:nth-child(8) { animation-delay: 0.45s; }
.books-grid .book-card:nth-child(9) { animation-delay: 0.5s; }
.books-grid .book-card:nth-child(10) { animation-delay: 0.55s; }
.books-grid .book-card:nth-child(11) { animation-delay: 0.6s; }
.books-grid .book-card:nth-child(12) { animation-delay: 0.65s; }
.book-card:hover {
    transform: translateY(-8px);
    box-shadow: 0 15px 30px rgba(0, 0, 0, 0.12);
}
.book-cover {
    height: 240px;
    position: relative;
    overflow: hidden;
}
.book-cover img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.5s ease;
}
.book-card:hover .book-cover img {
    transform: scale(1.08);
}
.cover-overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: linear-gradient(to bottom, rgba(0,0,0,0) 50%, rgba(0,0,0,0.5) 100%);
    z-index: 1;
}
.book-ribbon {
    position: absolute;
    top: 10px;
    right: -30px;
    transform: rotate(45deg);
    width: 120px;
    text-align: center;
    z-index: 2;
}
.book-ribbon span {
    display: block;
    width: 100%;
    padding: 5px 0;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1px;
}
.book-ribbon .available {
    background-color: #4caf50;
    color: white;
}
.book-ribbon .unavailable {
    background-color: #f44336;
    color: white;
}
.no-cover {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100%;
    width: 100%;
    background: linear-gradient(135deg, #f6f9fc 0%, #e9ecef 100%);
    color: #8492a6;
}
.no-cover i {
    font-size: 40px;
    margin-bottom: 10px;
}
.book-info {
    padding: 20px;
    flex: 1;
    display: flex;
    flex-direction: column;
}
.book-title {
    font-size: 1.1rem;
    font-weight: 600;
    color: #3c4858;
    margin: 0 0 8px;
    line-height: 1.4;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
.book-author {
    font-size: 0.95rem;
    color: #8492a6;
    margin-bottom: 15px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.book-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 20px;
}
.book-category {
    padding: 5px 10px;
    background-color: #ebf4ff;
    color: #667eea;
    border-radius: 20px;
    font-size: 0.75rem;
}
.book-year {
    padding: 5px 10px;
    background-color: #f7fafc;
    color: #8492a6;
    border-radius: 20px;
    font-size: 0.75rem;
}
.book-actions {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
    margin-top: auto;
}
.book-actions a, .book-actions button {
    padding: 10px 12px;
    border-radius: 8px;
    text-align: center;
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: 500;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    transition: all 0.3s;
}
.btn-detail {
    background-color: #e9ecef;
    color: #3c4858;
}
.btn-detail:hover {
    background-color: #dee2e6;
    color: #2d3748;
}
.btn-borrow {
    background-color: #667eea;
    color: white;
}
.btn-borrow:hover {
    background-color: #5a67d8;
    color: white;
    transform: translateY(-2px);
    box-shadow: 0 5px 10px rgba(102, 126, 234, 0.4);
}
.btn-borrow.disabled {
    background-color: #cbd5e0;
    color: #718096;
    cursor: not-allowed;
}
/* 无图书状态 */
.no-books {
    grid-column: 1 / -1;
    padding: 50px 30px;
    text-align: center;
    background-color: white;
    border-radius: 16px;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
    position: relative;
    z-index: 2;
    display: flex;
    flex-direction: column;
    align-items: center;
}
.no-books-img {
    max-width: 200px;
    margin-bottom: 20px;
}
.no-books h3 {
    font-size: 1.25rem;
    color: #3c4858;
    margin: 0 0 10px;
}
.no-books p {
    font-size: 1rem;
    color: #8492a6;
    margin-bottom: 20px;
}
.btn-reset-search {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 20px;
    background-color: #667eea;
    color: white;
    border-radius: 8px;
    text-decoration: none;
    font-weight: 500;
    transition: all 0.3s;
}
.btn-reset-search:hover {
    background-color: #5a67d8;
    color: white;
    transform: translateY(-2px);
    box-shadow: 0 5px 10px rgba(102, 126, 234, 0.4);
}
/* 分页容器 */
.pagination-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-top: 30px;
    margin-bottom: 20px;
    position: relative;
    z-index: 2;
}
.pagination {
    display: flex;
    list-style: none;
    padding: 0;
    margin: 0 0 15px 0;
    background-color: white;
    border-radius: 30px;
    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.08);
    overflow: hidden;
}
.pagination .page-item {
    margin: 0;
}
.pagination .page-link {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 40px;
    height: 40px;
    padding: 0 15px;
    border: none;
    color: #3c4858;
    font-weight: 500;
    transition: all 0.2s;
    text-decoration: none;
}
.pagination .page-link:hover {
    color: #667eea;
    background-color: #f7fafc;
}
.pagination .page-item.active .page-link {
    background-color: #667eea;
    color: white;
    box-shadow: none;
}
.pagination .page-item.disabled .page-link {
    color: #cbd5e0;
    background-color: #f7fafc;
    cursor: not-allowed;
}
.pagination-info {
    color: #8492a6;
    font-size: 0.9rem;
}
/* 模态框样式优化 */
.modal-content {
    border-radius: 16px;
    border: none;
    box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1), 0 5px 15px rgba(0, 0, 0, 0.07);
    overflow: hidden;
}
.modal-header {
    padding: 20px 25px;
    background-color: #f7fafc;
    border-bottom: 1px solid #e2e8f0;
}
.modal-title {
    color: #3c4858;
    font-size: 1.2rem;
    font-weight: 600;
}
.modal-body {
    padding: 25px;
}
.modal-footer {
    padding: 15px 25px;
    border-top: 1px solid #e2e8f0;
    background-color: #f7fafc;
}
.modal-info {
    margin-top: 10px;
    padding: 12px 16px;
    background-color: #ebf8ff;
    border-left: 4px solid #4299e1;
    color: #2b6cb0;
    font-size: 0.9rem;
    border-radius: 4px;
}
.modal .close {
    font-size: 1.5rem;
    color: #a0aec0;
    opacity: 0.8;
    text-shadow: none;
    transition: all 0.2s;
}
.modal .close:hover {
    opacity: 1;
    color: #667eea;
}
.modal .btn {
    border-radius: 8px;
    padding: 10px 20px;
    font-weight: 500;
    transition: all 0.3s;
}
.modal .btn-secondary {
    background-color: #e2e8f0;
    color: #4a5568;
    border: none;
}
.modal .btn-secondary:hover {
    background-color: #cbd5e0;
    color: #2d3748;
}
.modal .btn-primary {
    background-color: #667eea;
    color: white;
    border: none;
}
.modal .btn-primary:hover {
    background-color: #5a67d8;
    box-shadow: 0 5px 10px rgba(102, 126, 234, 0.4);
}
/* 响应式调整 */
@media (max-width: 992px) {
    .filter-row {
        flex-wrap: wrap;
    }
    .category-filters {
        flex: 1 0 100%;
        margin-bottom: 10px;
    }
    .filter-group {
        flex: 1 0 180px;
    }
}
@media (max-width: 768px) {
    .browse-container {
        padding: 16px;
    }
    .page-header {
        text-align: left;
    }
    .filter-section {
        padding: 15px;
    }
    .search-form {
        flex-direction: column;
        gap: 12px;
    }
    .search-group {
        max-width: 100%;
    }
    .filter-row {
        gap: 12px;
    }
    .books-grid {
        grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
        gap: 16px;
    }
    .stat-item {
        min-width: 130px;
        padding: 10px;
    }
    .stat-item i {
        width: 35px;
        height: 35px;
        font-size: 18px;
    }
    .search-results {
        padding: 10px;
        font-size: 0.9rem;
    }
}
@media (max-width: 576px) {
    .books-grid {
        grid-template-columns: 1fr 1fr;
        gap: 12px;
    }
    .book-cover {
        height: 180px;
    }
    .book-info {
        padding: 12px;
    }
    .book-title {
        font-size: 0.9rem;
    }
    .book-author {
        font-size: 0.8rem;
    }
    .book-actions {
        grid-template-columns: 1fr;
        gap: 8px;
    }
    .browse-stats {
        flex-direction: column;
        align-items: stretch;
    }
    .stat-item {
        max-width: none;
    }
    .search-results {
        width: 100%;
    }
}
================================================================================
File: ./app/static/css/inventory-adjust.css
================================================================================
/* 迪士尼主题库存管理页面样式 */
/* 基础样式 */
body {
    background-color: #f9f7ff;
    font-family: 'Arial Rounded MT Bold', 'Helvetica Neue', Arial, sans-serif;
    color: #3d4c65;
}
/* 迪士尼风格卡片 */
.disney-inventory-card {
    border: none;
    border-radius: 20px;
    box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08);
    background-color: #ffffff;
    margin-bottom: 40px;
    position: relative;
    overflow: hidden;
    transition: all 0.3s ease;
    padding: 2px;
    border: 3px solid #f0e6fa;
}
.disney-inventory-card:hover {
    box-shadow: 0 15px 30px rgba(110, 125, 249, 0.2);
    transform: translateY(-5px);
}
/* 迪士尼装饰元素 */
.disney-decoration {
    position: absolute;
    width: 60px;
    height: 60px;
    background-size: contain;
    background-repeat: no-repeat;
    opacity: 0.8;
    z-index: 1;
}
.top-left {
    top: 10px;
    left: 10px;
    background-image: url('https://i.imgur.com/Vyo9IF4.png'); /* 替换为迪士尼星星图标URL */
    transform: rotate(-15deg);
}
.top-right {
    top: 10px;
    right: 10px;
    background-image: url('https://i.imgur.com/pLRUYhb.png'); /* 替换为迪士尼魔法棒图标URL */
    transform: rotate(15deg);
}
.bottom-left {
    bottom: 10px;
    left: 10px;
    background-image: url('https://i.imgur.com/KkMfwWv.png'); /* 替换为迪士尼城堡图标URL */
    transform: rotate(-5deg);
}
.bottom-right {
    bottom: 10px;
    right: 10px;
    background-image: url('https://i.imgur.com/TcA6PL2.png'); /* 替换为迪士尼皇冠图标URL */
    transform: rotate(5deg);
}
/* 米奇耳朵标题装饰 */
.card-header-disney {
    background: linear-gradient(45deg, #e4c1f9, #d4a5ff);
    color: #512b81;
    padding: 1.8rem 1.5rem 1.5rem;
    font-weight: 600;
    border-radius: 18px 18px 0 0;
    text-align: center;
    position: relative;
    z-index: 2;
}
.mickey-ears {
    position: absolute;
    top: -25px;
    left: 50%;
    transform: translateX(-50%);
    width: 80px;
    height: 40px;
    background-image: url('https://i.imgur.com/pCPQoZx.png'); /* 替换为米奇耳朵图标URL */
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
}
/* 卡片内容 */
.card-body-disney {
    padding: 2.5rem;
    background-color: #ffffff;
    border-radius: 0 0 18px 18px;
    position: relative;
    z-index: 2;
}
/* 书籍封面 */
.book-cover-container {
    position: relative;
    display: flex;
    justify-content: center;
    align-items: flex-start;
}
.book-cover {
    max-height: 300px;
    width: auto;
    object-fit: contain;
    border-radius: 12px;
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
    transition: transform 0.3s ease;
    position: relative;
    z-index: 2;
    border: 3px solid #f9f0ff;
}
.book-cover:hover {
    transform: scale(1.03);
}
.disney-sparkles {
    position: absolute;
    width: 100%;
    height: 100%;
    background-image: url('https://i.imgur.com/8vZuwlG.png'); /* 替换为迪士尼闪光效果URL */
    background-size: 200px;
    background-repeat: no-repeat;
    background-position: center;
    opacity: 0;
    transition: opacity 0.5s ease;
    pointer-events: none;
}
.book-cover:hover + .disney-sparkles {
    opacity: 0.7;
}
/* 书籍详情 */
.book-details {
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.book-title {
    color: #5e35b1;
    font-weight: 700;
    margin-bottom: 1.8rem;
    font-size: 1.8rem;
    border-bottom: 3px dotted #e1bee7;
    padding-bottom: 1rem;
}
.book-info {
    font-size: 1.05rem;
    color: #424242;
}
.book-info p {
    margin-bottom: 1rem;
    display: flex;
    align-items: center;
}
/* 迪士尼图标 */
.disney-icon {
    display: inline-block;
    width: 28px;
    height: 28px;
    background-size: contain;
    background-position: center;
    background-repeat: no-repeat;
    margin-right: 10px;
    flex-shrink: 0;
}
.author-icon {
    background-image: url('https://i.imgur.com/2K5qpgQ.png'); /* 替换为米妮图标URL */
}
.publisher-icon {
    background-image: url('https://i.imgur.com/YKhKVT7.png'); /* 替换为唐老鸭图标URL */
}
.isbn-icon {
    background-image: url('https://i.imgur.com/ioaQTBM.png'); /* 替换为高飞图标URL */
}
.inventory-icon {
    background-image: url('https://i.imgur.com/D0jRTKX.png'); /* 替换为奇奇蒂蒂图标URL */
}
.type-icon {
    background-image: url('https://i.imgur.com/xgQriQn.png'); /* 替换为米奇图标URL */
}
.amount-icon {
    background-image: url('https://i.imgur.com/ioaQTBM.png'); /* 替换为高飞图标URL */
}
.remark-icon {
    background-image: url('https://i.imgur.com/2K5qpgQ.png'); /* 替换为米妮图标URL */
}
/* 库存状态标签 */
.stock-badge {
    display: inline-block;
    padding: 0.35em 0.9em;
    border-radius: 50px;
    font-weight: 600;
    margin-left: 8px;
    font-size: 0.9rem;
}
.high-stock {
    background-color: #e0f7fa;
    color: #0097a7;
    border: 2px solid #80deea;
}
.low-stock {
    background-color: #fff8e1;
    color: #ff8f00;
    border: 2px solid #ffe082;
}
.out-stock {
    background-color: #ffebee;
    color: #c62828;
    border: 2px solid #ef9a9a;
}
/* 表单容器 */
.form-container {
    background-color: #f8f4ff;
    padding: 2rem;
    border-radius: 15px;
    margin-top: 2rem;
    border: 2px dashed #d1c4e9;
    position: relative;
}
.form-group {
    position: relative;
}
/* 表单标签 */
.disney-label {
    color: #5e35b1;
    font-weight: 600;
    margin-bottom: 0.8rem;
    display: flex;
    align-items: center;
    font-size: 1.1rem;
}
/* 自定义表单控件 */
.disney-select,
.disney-input,
.disney-textarea {
    display: block;
    width: 100%;
    padding: 0.8rem 1rem;
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.5;
    color: #495057;
    background-color: #fff;
    background-clip: padding-box;
    border: 2px solid #d1c4e9;
    border-radius: 12px;
    transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.disney-select:focus,
.disney-input:focus,
.disney-textarea:focus {
    border-color: #9575cd;
    outline: 0;
    box-shadow: 0 0 0 0.2rem rgba(149, 117, 205, 0.25);
}
/* 确保下拉菜单选项可见 */
.disney-select option {
    background-color: #fff;
    color: #495057;
    padding: 8px;
}
/* 库存提示 */
.stock-hint {
    color: #757575;
    font-size: 0.95rem;
    margin-top: 0.6rem;
    font-weight: 500;
}
.stock-hint.warning {
    color: #ff8f00;
    font-weight: bold;
}
.stock-hint.danger {
    color: #c62828;
    font-weight: bold;
}
/* 按钮样式 */
.button-group {
    display: flex;
    justify-content: flex-end;
    gap: 15px;
    margin-top: 2rem;
}
.btn {
    padding: 0.7rem 2rem;
    border-radius: 50px;
    font-weight: 600;
    font-size: 1rem;
    letter-spacing: 0.5px;
    display: inline-block;
    text-align: center;
    vertical-align: middle;
    transition: all 0.3s ease;
    position: relative;
    overflow: hidden;
}
.disney-cancel-btn {
    background-color: #f3e5f5;
    color: #6a1b9a;
    border: 2px solid #ce93d8;
}
.disney-cancel-btn:hover {
    background-color: #e1bee7;
    color: #4a148c;
    transform: translateY(-3px);
}
.disney-confirm-btn {
    background: linear-gradient(45deg, #7e57c2, #5e35b1);
    color: white;
    border: none;
}
.disney-confirm-btn:hover {
    background: linear-gradient(45deg, #673ab7, #4527a0);
    transform: translateY(-3px);
    box-shadow: 0 7px 15px rgba(103, 58, 183, 0.3);
}
.disney-confirm-btn:before {
    content: "";
    position: absolute;
    top: -10px;
    left: -20px;
    width: 40px;
    height: 40px;
    background-image: url('https://i.imgur.com/8vZuwlG.png'); /* 替换为迪士尼魔法效果URL */
    background-size: contain;
    background-repeat: no-repeat;
    opacity: 0;
    transition: all 0.5s ease;
    transform: scale(0.5);
}
.disney-confirm-btn:hover:before {
    opacity: 0.8;
    transform: scale(1) rotate(45deg);
    top: -5px;
    left: 10px;
}
/* 响应式调整 */
@media (max-width: 768px) {
    .book-cover-container {
        margin-bottom: 30px;
    }
    .book-cover {
        max-height: 250px;
    }
    .book-title {
        text-align: center;
        font-size: 1.5rem;
    }
    .disney-decoration {
        width: 40px;
        height: 40px;
    }
    .button-group {
        flex-direction: column;
    }
    .btn {
        width: 100%;
        margin-bottom: 10px;
    }
    .card-header-disney,
    .card-body-disney {
        padding: 1.5rem;
    }
}
/* 表单元素聚焦效果 */
.form-group.focused {
    transform: translateY(-3px);
}
.form-group.focused .disney-label {
    color: #7e57c2;
}
/* 提交动画 */
.disney-inventory-card.submitting {
    animation: submitPulse 1s ease;
}
@keyframes submitPulse {
    0% { transform: scale(1); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08); }
    50% { transform: scale(1.02); box-shadow: 0 15px 35px rgba(126, 87, 194, 0.3); }
    100% { transform: scale(1); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08); }
}
/* 确认按钮动画 */
.disney-confirm-btn.active {
    animation: btnPulse 0.3s ease;
}
@keyframes btnPulse {
    0% { transform: scale(1); }
    50% { transform: scale(1.05); }
    100% { transform: scale(1); }
}
/* 表单过渡效果 */
.form-group {
    transition: transform 0.3s ease;
}
.disney-select,
.disney-input,
.disney-textarea {
    transition: all 0.3s ease;
}
/* 闪光效果持续时间 */
.disney-sparkles {
    transition: opacity 0.8s ease;
}
================================================================================
File: ./app/static/css/overdue.css
================================================================================
/* overdue.css - 适合文艺少女的深棕色调设计 */
body {
    font-family: 'Georgia', 'Times New Roman', serif;
    color: #4a3728;
    background-color: #fcf8f3;
}
.container {
    background-color: #fff9f5;
    border-radius: 8px;
    box-shadow: 0 3px 15px rgba(113, 66, 20, 0.1);
    padding: 25px;
    margin-top: 20px;
    margin-bottom: 20px;
    border: 1px solid #e8d9cb;
    position: relative;
}
.page-title {
    margin-bottom: 0;
    color: #5d3511;
    font-family: 'Playfair Display', Georgia, 'Times New Roman', serif;
    font-weight: 600;
    letter-spacing: 0.5px;
}
.d-flex {
    position: relative;
}
.d-flex:after {
    content: "";
    display: block;
    height: 2px;
    width: 100%;
    background: linear-gradient(to right, #d9c7b8, #8d6e63, #d9c7b8);
    margin-top: 15px;
    margin-bottom: 20px;
}
.alert-warning {
    background-color: #f9e8d0;
    border: 1px solid #ebd6ba;
    color: #8a6d3b;
    border-radius: 8px;
    padding: 15px;
    margin-bottom: 25px;
    box-shadow: 0 2px 5px rgba(138, 109, 59, 0.1);
    position: relative;
    overflow: hidden;
}
.alert-warning:before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    background: linear-gradient(to right, #d4a76a, transparent);
}
/* 表格样式 */
.overdue-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    margin-bottom: 25px;
    box-shadow: 0 2px 10px rgba(113, 66, 20, 0.05);
    border-radius: 8px;
    overflow: hidden;
    border: 1px solid #e8d9cb;
}
.overdue-table th,
.overdue-table td {
    padding: 15px 18px;
    text-align: left;
    border-bottom: 1px solid #e8d9cb;
}
.overdue-table th {
    background-color: #f1e6dd;
    color: #5d3511;
    font-weight: 600;
    letter-spacing: 0.5px;
}
.overdue-item:hover {
    background-color: #f8f0e5;
}
.overdue-item:last-child td {
    border-bottom: none;
}
.book-cover img {
    width: 65px;
    height: 90px;
    object-fit: cover;
    border-radius: 6px;
    box-shadow: 0 3px 8px rgba(113, 66, 20, 0.15);
    border: 2px solid #fff;
    transition: transform 0.3s ease;
}
.book-cover img:hover {
    transform: scale(1.05);
}
.book-title {
    font-weight: 600;
    font-family: 'Georgia', 'Times New Roman', serif;
}
.book-title a {
    color: #5d3511;
    text-decoration: none;
    transition: color 0.3s ease;
}
.book-title a:hover {
    color: #a66321;
    text-decoration: underline;
}
.book-author {
    color: #8d6e63;
    font-size: 0.9em;
    margin-top: 5px;
    font-style: italic;
}
.user-info a {
    color: #5d3511;
    text-decoration: none;
    font-weight: 600;
    transition: color 0.3s ease;
}
.user-info a:hover {
    color: #a66321;
    text-decoration: underline;
}
.user-nickname {
    color: #8d6e63;
    font-size: 0.9em;
    margin-top: 3px;
}
.user-contact {
    margin-top: 8px;
}
.user-contact a {
    color: #8d6e63;
    margin-right: 10px;
    text-decoration: none;
}
.user-contact a:hover {
    color: #704214;
}
.email-link, .phone-link {
    display: inline-block;
    padding: 4px 10px;
    font-size: 0.85em;
    background-color: #f1e6dd;
    border-radius: 15px;
    border: 1px solid #e8d9cb;
    transition: all 0.3s ease;
}
.email-link:hover, .phone-link:hover {
    background-color: #e8d9cb;
}
.text-danger {
    color: #a15950 !important;
}
.overdue-days {
    font-weight: 600;
}
/* 徽章 */
.badge {
    padding: 5px 12px;
    border-radius: 20px;
    font-weight: 500;
    font-size: 0.85em;
    letter-spacing: 0.5px;
}
.badge-danger {
    background-color: #a15950;
    color: white;
}
.badge-warning {
    background-color: #d4a76a;
    color: #4a3728;
}
.badge-info {
    background-color: #6a8da9;
    color: white;
}
/* 按钮 */
.btn {
    border-radius: 20px;
    padding: 8px 16px;
    transition: all 0.3s ease;
    letter-spacing: 0.3px;
}
.btn-outline-secondary {
    color: #704214;
    border-color: #d9c7b8;
    background-color: transparent;
}
.btn-outline-secondary:hover {
    color: #fff;
    background-color: #8d6e63;
    border-color: #8d6e63;
}
.btn-success {
    background-color: #5b8a72;
    border-color: #5b8a72;
}
.btn-success:hover, .btn-success:focus {
    background-color: #4a7561;
    border-color: #4a7561;
}
.btn-warning {
    background-color: #d4a76a;
    border-color: #d4a76a;
    color: #4a3728;
}
.btn-warning:hover, .btn-warning:focus {
    background-color: #c29355;
    border-color: #c29355;
    color: #4a3728;
}
.btn-primary {
    background-color: #704214;
    border-color: #704214;
}
.btn-primary:hover, .btn-primary:focus {
    background-color: #5d3511;
    border-color: #5d3511;
}
.actions .btn {
    margin-right: 5px;
    margin-bottom: 6px;
}
/* 空状态 */
.no-records {
    text-align: center;
    padding: 60px 20px;
    background-color: #f8f0e5;
    border-radius: 8px;
    margin: 25px 0;
    border: 1px dashed #d9c7b8;
    position: relative;
}
.no-records:before, .no-records:after {
    content: "❦";
    position: absolute;
    color: #d9c7b8;
    font-size: 24px;
}
.no-records:before {
    top: 20px;
    left: 20px;
}
.no-records:after {
    bottom: 20px;
    right: 20px;
}
.empty-icon {
    font-size: 4.5em;
    color: #5b8a72;
    margin-bottom: 25px;
}
.empty-text {
    color: #5b8a72;
    margin-bottom: 25px;
    font-style: italic;
    font-size: 1.1em;
}
/* 分页 */
.pagination-container {
    display: flex;
    justify-content: center;
    margin-top: 25px;
}
.pagination .page-link {
    color: #5d3511;
    border-color: #e8d9cb;
    margin: 0 3px;
    border-radius: 4px;
}
.pagination .page-item.active .page-link {
    background-color: #704214;
    border-color: #704214;
}
.pagination .page-link:hover {
    background-color: #f1e6dd;
    color: #5d3511;
}
/* 模态框定制 */
.modal-content {
    border-radius: 8px;
    border: 1px solid #e8d9cb;
    box-shadow: 0 5px 20px rgba(113, 66, 20, 0.15);
    background-color: #fff9f5;
}
.modal-header {
    border-bottom: 1px solid #e8d9cb;
    background-color: #f1e6dd;
    border-radius: 8px 8px 0 0;
}
.modal-title {
    color: #5d3511;
    font-family: 'Georgia', 'Times New Roman', serif;
    font-weight: 600;
}
.modal-footer {
    border-top: 1px solid #e8d9cb;
}
/* 装饰元素 */
.container:before {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    width: 150px;
    height: 150px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Cpath fill='%23d9c7b8' fill-opacity='0.2' d='M11 18c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm48 25c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm-43-7c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm63 31c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM34 90c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm56-76c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM12 86c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm28-65c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm23-11c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-6 60c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm29 22c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zM32 63c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm57-13c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-9-21c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM60 91c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM35 41c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM12 60c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2z'/%3E%3C/svg%3E");
    opacity: 0.3;
    pointer-events: none;
    z-index: -1;
}
/* 响应式设计 */
@media (max-width: 992px) {
    .actions .btn {
        display: block;
        width: 100%;
        margin-bottom: 8px;
    }
    .no-records:before, .no-records:after {
        display: none;
    }
}
@media (max-width: 768px) {
    .overdue-table {
        display: block;
        overflow-x: auto;
    }
    .book-cover img {
        width: 50px;
        height: 70px;
    }
}
================================================================================
File: ./app/static/css/announcement-manage.css
================================================================================
.announcement-manage-container {
    padding: 20px;
}
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 25px;
    border-bottom: 1px solid #e3e3e3;
    padding-bottom: 15px;
}
.filter-container {
    margin-bottom: 25px;
}
.filter-form {
    display: flex;
    gap: 15px;
    align-items: center;
    flex-wrap: wrap;
}
.filter-form .form-group {
    margin-bottom: 0;
    min-width: 200px;
}
.announcement-table {
    background-color: #fff;
    box-shadow: 0 2px 10px rgba(0,0,0,0.05);
    border-radius: 8px;
}
.announcement-table th {
    background-color: #f8f9fa;
    white-space: nowrap;
}
.announcement-title {
    font-weight: 500;
    color: #333;
    text-decoration: none;
    display: block;
    max-width: 300px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.announcement-title:hover {
    color: #007bff;
    text-decoration: underline;
}
.btn-group {
    display: flex;
    gap: 5px;
}
.pagination-container {
    margin-top: 30px;
    display: flex;
    justify-content: center;
}
.no-records {
    text-align: center;
    padding: 50px 20px;
    background-color: #f8f9fa;
    border-radius: 8px;
    color: #6c757d;
}
.no-records i {
    font-size: 3rem;
    margin-bottom: 15px;
}
.no-records p {
    font-size: 1.2rem;
}
================================================================================
File: ./app/static/css/user_activity.css
================================================================================
/* app/static/css/user_activity.css */
.data-table .rank {
    font-weight: 700;
    text-align: center;
}
.data-table .borrow-count {
    font-weight: 600;
    color: #007bff;
}
================================================================================
File: ./app/static/css/user-form.css
================================================================================
/* 用户表单样式 - 甜美风格 */
.user-form-container {
    max-width: 850px;
    margin: 25px auto;
    padding: 0 20px;
}
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 25px;
    padding-bottom: 15px;
    border-bottom: 2px solid #f8e6e8;
    animation: slideInDown 0.6s ease-out;
}
.page-header h1 {
    margin: 0;
    font-size: 28px;
    color: #e75480; /* 粉红色调 */
    font-weight: 600;
    letter-spacing: 0.5px;
}
.form-card {
    background-color: #fff;
    border-radius: 12px;
    box-shadow: 0 5px 20px rgba(231, 84, 128, 0.08);
    padding: 30px;
    border: 1px solid #f8e6e8;
    position: relative;
    overflow: visible;
    animation: fadeIn 0.7s ease-out;
}
.form-group {
    margin-bottom: 22px;
    animation: slideInRight 0.4s ease-out;
    animation-fill-mode: both;
}
/* 为每个表单组添加延迟,创造波浪效果 */
.form-group:nth-child(1) { animation-delay: 0.1s; }
.form-group:nth-child(2) { animation-delay: 0.2s; }
.form-group:nth-child(3) { animation-delay: 0.3s; }
.form-group:nth-child(4) { animation-delay: 0.4s; }
.form-group:nth-child(5) { animation-delay: 0.5s; }
.form-group:nth-child(6) { animation-delay: 0.6s; }
.form-group:nth-child(7) { animation-delay: 0.7s; }
.form-group:nth-child(8) { animation-delay: 0.8s; }
.form-group:nth-child(9) { animation-delay: 0.9s; }
.form-group:nth-child(10) { animation-delay: 1.0s; }
.form-group.required label:after {
    content: " *";
    color: #ff6b8b;
}
.form-group label {
    display: block;
    margin-bottom: 8px;
    font-weight: 500;
    color: #5d5d5d;
    font-size: 15px;
    transition: all 0.3s ease;
}
.form-group:hover label {
    color: #e75480;
    transform: translateX(3px);
}
.form-control {
    display: block;
    width: 100%;
    padding: 12px 15px;
    font-size: 15px;
    line-height: 1.5;
    color: #555;
    background-color: #fff;
    background-clip: padding-box;
    border: 1.5px solid #ffd1dc; /* 淡粉色边框 */
    border-radius: 8px;
    transition: all 0.3s ease;
}
.form-control:focus {
    border-color: #ff8da1;
    outline: 0;
    box-shadow: 0 0 0 3px rgba(255, 141, 161, 0.25);
    transform: translateY(-2px);
}
.form-control::placeholder {
    color: #bbb;
    font-style: italic;
}
.password-field {
    position: relative;
}
.toggle-password {
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    cursor: pointer;
    color: #ff8da1;
    transition: all 0.3s ease;
    z-index: 2;
}
.toggle-password:hover {
    color: #e75480;
    transform: translateY(-50%) scale(1.2);
}
.input-with-button {
    display: flex;
    gap: 12px;
}
.input-with-button .form-control {
    flex: 1;
}
.input-with-button .btn {
    white-space: nowrap;
}
.form-text {
    display: block;
    margin-top: 6px;
    font-size: 13.5px;
    color: #888;
    font-style: italic;
    transition: all 0.3s ease;
}
.form-text.text-danger {
    color: #ff5c77;
    font-style: normal;
    animation: shake 0.5s cubic-bezier(.36,.07,.19,.97) both;
}
.form-text.text-success {
    color: #7ac98f;
    font-style: normal;
    animation: pulse 0.5s ease;
}
.form-actions {
    display: flex;
    gap: 15px;
    margin-top: 35px;
    justify-content: center;
    animation: fadeInUp 0.8s ease-out;
    animation-delay: 1.2s;
    animation-fill-mode: both;
}
.btn {
    display: inline-block;
    font-weight: 500;
    text-align: center;
    white-space: nowrap;
    vertical-align: middle;
    user-select: none;
    border: 1.5px solid transparent;
    padding: 10px 22px;
    font-size: 15px;
    line-height: 1.5;
    border-radius: 25px; /* 圆润按钮 */
    transition: all 0.3s ease;
    cursor: pointer;
    letter-spacing: 0.3px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
    position: relative;
    overflow: hidden;
}
/* 按钮波纹效果 */
.btn:after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 5px;
    height: 5px;
    background: rgba(255, 255, 255, 0.5);
    opacity: 0;
    border-radius: 100%;
    transform: scale(1, 1) translate(-50%);
    transform-origin: 50% 50%;
}
.btn:focus:not(:active)::after {
    animation: ripple 1s ease-out;
}
@keyframes ripple {
    0% {
        transform: scale(0, 0);
        opacity: 0.5;
    }
    20% {
        transform: scale(25, 25);
        opacity: 0.3;
    }
    100% {
        transform: scale(50, 50);
        opacity: 0;
    }
}
.btn-primary {
    color: #fff;
    background-color: #ff8da1;
    border-color: #ff8da1;
}
.btn-primary:hover {
    color: #fff;
    background-color: #ff7389;
    border-color: #ff7389;
    box-shadow: 0 4px 8px rgba(255, 141, 161, 0.3);
    transform: translateY(-3px);
}
.btn-primary:active {
    transform: translateY(-1px);
}
.btn-secondary {
    color: #777;
    background-color: #f8f9fa;
    border-color: #e6e6e6;
}
.btn-secondary:hover {
    color: #555;
    background-color: #f1f1f1;
    border-color: #d9d9d9;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
    transform: translateY(-3px);
}
.btn-outline-primary {
    color: #ff8da1;
    background-color: transparent;
    border-color: #ff8da1;
}
.btn-outline-primary:hover {
    color: #fff;
    background-color: #ff8da1;
    border-color: #ff8da1;
    box-shadow: 0 4px 8px rgba(255, 141, 161, 0.2);
    transform: translateY(-2px);
}
.btn i {
    margin-right: 6px;
    transition: transform 0.3s ease;
}
.btn:hover i {
    transform: translateX(-3px);
}
/* 禁用状态 */
.btn:disabled,
.btn.disabled {
    opacity: 0.65;
    cursor: not-allowed;
    transform: none !important;
    box-shadow: none !important;
}
/* 提示信息 */
.alert {
    position: relative;
    padding: 14px 20px;
    margin-bottom: 25px;
    border: 1px solid transparent;
    border-radius: 8px;
    animation: shake 0.5s cubic-bezier(.36,.07,.19,.97) both;
}
.alert-danger {
    color: #ff5c77;
    background-color: #fff0f3;
    border-color: #ffe0e5;
}
/* 装饰元素 */
.form-card::before {
    content: "";
    position: absolute;
    top: -15px;
    right: 30px;
    width: 40px;
    height: 40px;
    background-color: #ffeaef;
    border-radius: 50%;
    z-index: -1;
    opacity: 0.8;
    animation: float 6s ease-in-out infinite;
}
.form-card::after {
    content: "";
    position: absolute;
    bottom: -20px;
    left: 50px;
    width: 60px;
    height: 60px;
    background-color: #ffeaef;
    border-radius: 50%;
    z-index: -1;
    opacity: 0.6;
    animation: float 7s ease-in-out infinite reverse;
}
/* 修复选择框问题 */
s/* 专门修复下拉框文字显示问题 */
select.form-control {
    /* 保持一致的外观 */
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23ff8da1' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 16px;
    /* 修正文字显示问题 */
    padding: 12px 40px 12px 15px; /* 增加右侧内边距,确保文字不被箭头遮挡 */
    text-overflow: ellipsis; /* 如果文字太长会显示省略号 */
    white-space: nowrap; /* 防止文本换行 */
    color: #555 !important; /* 强制文本颜色 */
    font-weight: normal;
    line-height: 1.5;
    position: relative;
    z-index: 1;
}
/* 确保选定的选项能被完整显示 */
select.form-control option {
    padding: 10px 15px;
    color: #555;
    background-color: #fff;
    font-size: 15px;
    line-height: 1.5;
}
/* 针对特定浏览器的修复 */
@-moz-document url-prefix() {
    select.form-control {
        color: #555;
        text-indent: 0;
        text-overflow: clip;
    }
}
/* 针对Safari的修复 */
@media screen and (-webkit-min-device-pixel-ratio: 0) {
    select.form-control {
        text-indent: 1px;
        text-overflow: clip;
    }
}
/* 设置选中文本的样式 */
select.form-control:focus option:checked {
    background: #ffeaef;
    color: #555;
}
/* 修复IE特定问题 */
select::-ms-expand {
    display: none;
}
/* 确保选项在下拉框中正确展示 */
select.form-control option {
    font-weight: normal;
}
/* 解决Chrome中的问题 */
@media screen and (-webkit-min-device-pixel-ratio: 0) {
    select.form-control {
        border-radius: 8px;
    }
}
/* 更明确地设置选择状态的样式 */
select.form-control {
    border: 1.5px solid #ffd1dc;
    background-color: #fff;
}
select.form-control:focus {
    border-color: #ff8da1;
    outline: 0;
    box-shadow: 0 0 0 3px rgba(255, 141, 161, 0.25);
}
/* 尝试不同的方式设置下拉箭头 */
.select-wrapper {
    position: relative;
    display: block;
    width: 100%;
}
.select-wrapper::after {
    content: '⌄';
    font-size: 24px;
    color: #ff8da1;
    position: absolute;
    right: 15px;
    top: 50%;
    transform: translateY(-50%);
    pointer-events: none;
}
/* 移除自定义背景图,改用伪元素作为箭头 */
select.form-control {
    background-image: none;
}
/* 美化表单分组 */
.form-card {
    position: relative;
    overflow: hidden;
}
.form-group {
    position: relative;
    z-index: 1;
    transition: transform 0.3s ease;
}
.form-group:hover {
    transform: translateX(5px);
}
/* 甜美风格的表单组分隔线 */
.form-group:not(:last-child):after {
    content: "";
    display: block;
    height: 1px;
    width: 0;
    background: linear-gradient(to right, transparent, #ffe0e8, transparent);
    margin-top: 22px;
    transition: width 0.5s ease;
}
.form-group:not(:last-child):hover:after {
    width: 100%;
}
/* 必填项标记美化 */
.form-group.required label {
    position: relative;
}
.form-group.required label:after {
    content: " *";
    color: #ff6b8b;
    font-size: 18px;
    line-height: 0;
    position: relative;
    top: 5px;
    transition: all 0.3s ease;
}
.form-group.required:hover label:after {
    color: #ff3958;
    transform: scale(1.2);
}
/* 美化滚动条 */
::-webkit-scrollbar {
    width: 8px;
}
::-webkit-scrollbar-track {
    background: #fff;
}
::-webkit-scrollbar-thumb {
    background-color: #ffc0cb;
    border-radius: 20px;
    border: 2px solid #fff;
}
::-webkit-scrollbar-thumb:hover {
    background-color: #ff8da1;
}
/* 添加动画 */
@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}
@keyframes slideInRight {
    from { opacity: 0; transform: translateX(20px); }
    to { opacity: 1; transform: translateX(0); }
}
@keyframes slideInDown {
    from { opacity: 0; transform: translateY(-20px); }
    to { opacity: 1; transform: translateY(0); }
}
@keyframes fadeInUp {
    from { opacity: 0; transform: translateY(20px); }
    to { opacity: 1; transform: translateY(0); }
}
@keyframes float {
    0% {
        transform: translateY(0px);
    }
    50% {
        transform: translateY(-15px);
    }
    100% {
        transform: translateY(0px);
    }
}
@keyframes pulse {
    0% {
        transform: scale(1);
    }
    50% {
        transform: scale(1.05);
    }
    100% {
        transform: scale(1);
    }
}
@keyframes shake {
    10%, 90% {
        transform: translateX(-1px);
    }
    20%, 80% {
        transform: translateX(2px);
    }
    30%, 50%, 70% {
        transform: translateX(-3px);
    }
    40%, 60% {
        transform: translateX(3px);
    }
}
/* 响应式设计 */
@media (max-width: 768px) {
    .form-actions {
        flex-direction: column;
        align-items: center;
    }
    .input-with-button {
        flex-direction: column;
    }
    .page-header {
        flex-direction: column;
        align-items: flex-start;
    }
    .page-header .actions {
        margin-top: 12px;
    }
    .btn {
        width: 100%;
    }
}
/* 表单光影效果 */
.form-card {
    position: relative;
    overflow: hidden;
}
.form-card:before, .form-card:after {
    content: "";
    position: absolute;
    z-index: -1;
}
/* 移入表单时添加光晕效果 */
.form-card:hover:before {
    content: "";
    position: absolute;
    top: -50%;
    left: -50%;
    width: 200%;
    height: 200%;
    background: radial-gradient(circle, rgba(255,232,238,0.3) 0%, rgba(255,255,255,0) 70%);
    animation: glowEffect 2s infinite linear;
}
@keyframes glowEffect {
    0% {
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg);
    }
}
/* 输入焦点时的动画 */
.form-control:focus {
    animation: focusPulse 1s infinite alternate;
}
@keyframes focusPulse {
    from {
        box-shadow: 0 0 0 3px rgba(255, 141, 161, 0.25);
    }
    to {
        box-shadow: 0 0 0 5px rgba(255, 141, 161, 0.15);
    }
}
================================================================================
File: ./app/static/css/book-import.css
================================================================================
/* 图书批量导入页面样式 - 女性风格优化版 */
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500&family=Playfair+Display:wght@400;700&display=swap');
:root {
    --primary-color: #e083b8;
    --primary-light: #f8d7e9;
    --secondary-color: #89c2d9;
    --accent-color: #a76eb8;
    --text-color: #555;
    --light-text: #888;
    --dark-text: #333;
    --border-radius: 12px;
    --box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);
}
body {
    background-color: #fff6f9;
    font-family: 'Montserrat', sans-serif;
    color: var(--text-color);
}
.import-container {
    padding: 30px;
    position: relative;
    overflow: hidden;
}
/* 页眉样式 */
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 30px;
    padding-bottom: 20px;
    border-bottom: 1px solid #f0d3e6;
}
.fancy-title {
    font-family: 'Playfair Display', serif;
    font-size: 2.5rem;
    color: var(--accent-color);
    text-shadow: 1px 1px 2px rgba(167, 110, 184, 0.2);
    letter-spacing: 1px;
    margin: 0;
    position: relative;
}
.fancy-title::after {
    content: "";
    position: absolute;
    bottom: -10px;
    left: 0;
    width: 60px;
    height: 3px;
    background: linear-gradient(to right, var(--primary-color), var(--secondary-color));
    border-radius: 3px;
}
.subtitle {
    font-size: 1.5rem;
    font-weight: 300;
    color: var(--light-text);
    margin-left: 10px;
}
.btn-return {
    padding: 8px 20px;
    background-color: transparent;
    color: var(--accent-color);
    border: 2px solid var(--primary-light);
    border-radius: 25px;
    transition: all 0.3s ease;
    font-weight: 500;
    box-shadow: 0 3px 8px rgba(167, 110, 184, 0.1);
}
.btn-return:hover {
    background-color: var(--primary-light);
    color: var(--accent-color);
    transform: translateY(-3px);
    box-shadow: 0 5px 12px rgba(167, 110, 184, 0.2);
}
/* 卡片样式 */
.card {
    border: none;
    border-radius: var(--border-radius);
    box-shadow: var(--box-shadow);
    overflow: hidden;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
    background-color: #ffffff;
    margin-bottom: 30px;
}
.card:hover {
    transform: translateY(-5px);
    box-shadow: 0 10px 25px rgba(0, 0, 0, 0.12);
}
.card-header {
    background: linear-gradient(135deg, #f9f1f7, #fcf6fa);
    padding: 20px 25px;
    border-bottom: 1px solid #f0e1ea;
}
.card-header h4 {
    font-family: 'Playfair Display', serif;
    color: var(--accent-color);
    margin: 0;
    font-size: 1.5rem;
}
.sparkle {
    color: var(--primary-color);
    margin-right: 8px;
    animation: sparkle 2s infinite;
}
@keyframes sparkle {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.5; }
}
.card-body {
    padding: 30px;
}
/* 表单样式 */
.elegant-label {
    font-weight: 500;
    color: var(--dark-text);
    margin-bottom: 12px;
    font-size: 1.1rem;
    display: block;
}
.custom-file {
    position: relative;
    display: inline-block;
    width: 100%;
    margin-bottom: 15px;
}
.custom-file-input {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
    z-index: 2;
}
.custom-file-label {
    padding: 15px 20px;
    background-color: #f9f2f7;
    color: var(--light-text);
    border: 2px dashed #e9d6e5;
    border-radius: var(--border-radius);
    text-align: center;
    transition: all 0.3s ease;
    cursor: pointer;
}
.custom-file-label:hover {
    background-color: #f4e8f0;
    border-color: var(--primary-color);
}
.has-file .custom-file-label {
    background-color: #e6f3ff;
    border-color: var(--secondary-color);
    color: var(--secondary-color);
    font-weight: 500;
}
.import-btn {
    background: linear-gradient(45deg, var(--primary-color), var(--accent-color));
    border: none;
    padding: 15px 30px;
    color: white;
    font-size: 1.1rem;
    font-weight: 500;
    border-radius: 30px;
    margin-top: 15px;
    transition: all 0.3s ease;
    box-shadow: 0 8px 15px rgba(167, 110, 184, 0.3);
}
.import-btn:hover {
    transform: translateY(-3px);
    box-shadow: 0 12px 20px rgba(167, 110, 184, 0.4);
    background: linear-gradient(45deg, var(--accent-color), var(--primary-color));
}
/* 分隔线 */
.divider {
    display: flex;
    align-items: center;
    margin: 30px 0;
    color: var(--light-text);
}
.divider:before,
.divider:after {
    content: "";
    flex: 1;
    border-bottom: 1px solid #f0d3e6;
}
.divider-content {
    padding: 0 10px;
    color: var(--primary-color);
    font-size: 1.2rem;
}
/* 导入说明样式 */
.import-instructions {
    margin-top: 10px;
    padding: 25px;
    background: linear-gradient(to bottom right, #fff, #fafafa);
    border-radius: var(--border-radius);
    box-shadow: 0 6px 15px rgba(0, 0, 0, 0.03);
}
.instruction-title {
    font-family: 'Playfair Display', serif;
    color: var(--accent-color);
    margin-bottom: 20px;
    font-size: 1.4rem;
    border-bottom: 2px solid var(--primary-light);
    padding-bottom: 10px;
    display: inline-block;
}
.instruction-content {
    color: var(--text-color);
    line-height: 1.6;
}
.elegant-list {
    list-style-type: none;
    padding-left: 5px;
    margin-top: 15px;
}
.elegant-list li {
    margin-bottom: 12px;
    position: relative;
    padding-left: 25px;
    line-height: 1.5;
}
.elegant-list li:before {
    content: "\f054";
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    color: var(--primary-color);
    position: absolute;
    left: 0;
    top: 2px;
    font-size: 12px;
}
.field-name {
    font-family: 'Courier New', monospace;
    background-color: #f6f6f6;
    padding: 2px 8px;
    border-radius: 4px;
    color: #9c5bb5;
    font-weight: 600;
    font-size: 0.9rem;
}
.required-field {
    color: var(--dark-text);
}
.required-badge {
    background-color: #fce1e9;
    color: #e25a86;
    font-size: 0.7rem;
    padding: 2px 8px;
    border-radius: 12px;
    margin-left: 5px;
    vertical-align: middle;
    font-weight: 600;
}
/* 模板下载样式 */
.template-download {
    margin-top: 30px;
    text-align: center;
    padding: 20px;
    background: linear-gradient(135deg, #f0f9ff, #f5f0ff);
    border-radius: var(--border-radius);
    border: 1px solid #e0f0ff;
}
.template-download p {
    color: var(--dark-text);
    margin-bottom: 15px;
    font-weight: 500;
}
.download-btn {
    background-color: white;
    color: var(--accent-color);
    border: 2px solid var(--primary-light);
    padding: 10px 25px;
    border-radius: 25px;
    font-weight: 500;
    transition: all 0.3s ease;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
}
.download-btn:hover {
    background-color: var(--accent-color);
    color: white;
    border-color: var(--accent-color);
    transform: translateY(-3px);
    box-shadow: 0 8px 20px rgba(167, 110, 184, 0.2);
}
/* 悬浮元素 - 冰雪奇缘和天空之城风格 */
.floating-elements {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    overflow: hidden;
    z-index: -1;
}
.snowflake {
    position: absolute;
    opacity: 0.7;
    border-radius: 50%;
    background: radial-gradient(circle at center, rgba(255,255,255,0.8) 0%, rgba(230,240,255,0.3) 70%, transparent 100%);
    animation: float 20s linear infinite;
}
.snowflake-1 {
    width: 20px;
    height: 20px;
    top: 10%;
    left: 10%;
}
.snowflake-2 {
    width: 15px;
    height: 15px;
    top: 20%;
    right: 20%;
}
.snowflake-3 {
    width: 25px;
    height: 25px;
    bottom: 30%;
    left: 30%;
}
.snowflake-4 {
    width: 18px;
    height: 18px;
    bottom: 15%;
    right: 15%;
}
.flower {
    position: absolute;
    width: 30px;
    height: 30px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath fill='%23e083b8' d='M50 15c-5 0-10 5-10 10s5 10 10 10 10-5 10-10-5-10-10-10zm-25 25c-5 0-10 5-10 10s5 10 10 10 10-5 10-10-5-10-10-10zm50 0c-5 0-10 5-10 10s5 10 10 10 10-5 10-10-5-10-10-10zm-25 25c-5 0-10 5-10 10s5 10 10 10 10-5 10-10-5-10-10-10z'/%3E%3Ccircle fill='%23f8d7e9' cx='50' cy='50' r='10'/%3E%3C/svg%3E");
    background-size: contain;
    background-repeat: no-repeat;
    opacity: 0.5;
    animation: rotate 25s linear infinite, float 20s ease-in-out infinite;
}
.flower-1 {
    top: 70%;
    left: 5%;
}
.flower-2 {
    top: 15%;
    right: 5%;
}
@keyframes float {
    0% {
        transform: translateY(0) translateX(0);
    }
    25% {
        transform: translateY(30px) translateX(15px);
    }
    50% {
        transform: translateY(50px) translateX(-15px);
    }
    75% {
        transform: translateY(20px) translateX(25px);
    }
    100% {
        transform: translateY(0) translateX(0);
    }
}
@keyframes rotate {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(360deg);
    }
}
/* 响应式调整 */
@media (max-width: 992px) {
    .import-container {
        padding: 20px 15px;
    }
    .fancy-title {
        font-size: 2rem;
    }
    .subtitle {
        font-size: 1.2rem;
    }
}
@media (max-width: 768px) {
    .page-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 15px;
    }
    .card-body {
        padding: 20px 15px;
    }
    .import-instructions {
        padding: 15px;
    }
    .fancy-title {
        font-size: 1.8rem;
    }
    .subtitle {
        font-size: 1rem;
        display: block;
        margin-left: 0;
        margin-top: 5px;
    }
}
/* 添加到book-import.css文件末尾 */
/* 导入消息样式 */
.import-message {
    margin-top: 15px;
}
.import-message .alert {
    border-radius: var(--border-radius);
    padding: 15px;
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);
    border: none;
}
.import-message .alert-success {
    background-color: #e6f7ee;
    color: #28a745;
}
.import-message .alert-warning {
    background-color: #fff8e6;
    color: #ffc107;
}
.import-message .alert-danger {
    background-color: #feecf0;
    color: #dc3545;
}
.import-message .alert-info {
    background-color: #e6f3f8;
    color: #17a2b8;
}
.import-message .alert i {
    margin-right: 8px;
}
/* 导入过程中的飘落元素 */
.falling-element {
    position: absolute;
    z-index: 1000;
    pointer-events: none;
    opacity: 0.8;
}
.falling-flower {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath fill='%23e083b8' d='M50 15c-5 0-10 5-10 10s5 10 10 10 10-5 10-10-5-10-10-10zm-25 25c-5 0-10 5-10 10s5 10 10 10 10-5 10-10-5-10-10-10zm50 0c-5 0-10 5-10 10s5 10 10 10 10-5 10-10-5-10-10-10zm-25 25c-5 0-10 5-10 10s5 10 10 10 10-5 10-10-5-10-10-10z'/%3E%3Ccircle fill='%23f8d7e9' cx='50' cy='50' r='10'/%3E%3C/svg%3E");
    background-size: contain;
    background-repeat: no-repeat;
    animation: fallAndSpin 5s linear forwards;
}
.falling-snowflake {
    background: radial-gradient(circle at center, rgba(255,255,255,0.8) 0%, rgba(230,240,255,0.3) 70%, transparent 100%);
    border-radius: 50%;
    animation: fall 5s linear forwards;
}
@keyframes fall {
    0% {
        transform: translateY(-50px) rotate(0deg);
        opacity: 0;
    }
    10% {
        opacity: 1;
    }
    100% {
        transform: translateY(calc(100vh - 100px)) rotate(359deg);
        opacity: 0;
    }
}
@keyframes fallAndSpin {
    0% {
        transform: translateY(-50px) rotate(0deg);
        opacity: 0;
    }
    10% {
        opacity: 1;
    }
    100% {
        transform: translateY(calc(100vh - 100px)) rotate(720deg);
        opacity: 0;
    }
}
/* 导入过程中按钮样式 */
.import-btn:disabled {
    background: linear-gradient(45deg, #f089b7, #b989d9);
    opacity: 0.7;
    cursor: not-allowed;
}
.import-btn:disabled .fa-spinner {
    margin-right: 10px;
}
/* 文件上传成功状态样式 */
.has-file .custom-file-label {
    background-color: #e6f7ee;
    border-color: #28a745;
    color: #28a745;
}
/* 添加文件类型图标 */
.has-file .custom-file-label::before {
    content: "\f56f";  /* Excel文件图标 */
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    margin-right: 8px;
}
================================================================================
File: ./app/static/css/statistics.css
================================================================================
/* app/static/css/statistics.css */
:root {
    /* Soft & Elegant Palette */
    --color-primary-light-pink: #FCE4EC; /* 淡粉色 */
    --color-primary-milk-white: #FFF8F0; /* 奶白色 */
    --color-primary-apricot: #FFDAB9;    /* 浅杏色 */
    --color-aux-rose-gold: #B76E79;     /* 玫瑰金 */
    --color-aux-light-purple: #E6E6FA;  /* 淡紫色 */
    --color-aux-soft-gray: #D3D3D3;     /* 柔和的灰色 */
    --color-accent-berry-red: #8C2D5A;   /* 深一点的浆果红 */
    --font-serif-elegant: 'Playfair Display', serif;
    --font-serif-lora: 'Lora', serif;
    --font-sans-clean: 'Open Sans', sans-serif;
    --font-script-delicate: 'Sacramento', cursive;
    --font-serif-garamond: 'EB Garamond', serif;
    /* Derived/General Usage */
    --background-main: var(--color-primary-milk-white);
    --background-container: #FFFFFF;
    --text-main: #5D5053; /* A darker, softer, slightly desaturated rose-brown */
    --text-soft: #8A797C;
    --text-heading: var(--color-aux-rose-gold);
    --text-accent: var(--color-accent-berry-red);
    --border-soft: var(--color-aux-soft-gray);
    --border-decorative: var(--color-primary-light-pink);
    --shadow-soft: rgba(183, 110, 121, 0.1); /* Soft shadow based on rose gold */
    --shadow-subtle: rgba(0, 0, 0, 0.05);
    /* Fallback for old variables - some might still be used by unchanged CSS */
    --primary-color: var(--color-primary-light-pink);
    --secondary-color: var(--color-primary-apricot); /* Or #FFF8F0 for a lighter secondary */
    --accent-color: var(--color-aux-rose-gold);
    --text-color: var(--text-main);
    --light-text: var(--text-soft);
    --border-color: var(--border-soft);
    --shadow-color: var(--shadow-soft);
    --hover-color: #F8E0E6; /* Lighter pink for hover */
}
body {
    background-color: var(--background-main);
    color: var(--text-main);
    font-family: var(--font-sans-clean);
    font-weight: 300; /* Lighter default font weight */
    line-height: 1.7; /* Increased line height */
}
.statistics-container {
    padding: 40px 30px; /* Increased padding */
    max-width: 1100px; /* Slightly adjusted max-width */
    margin: 40px auto; /* More margin for breathing room */
    background-color: var(--background-container);
    border-radius: 16px; /* Softer, larger border-radius */
    box-shadow: 0 8px 25px var(--shadow-soft); /* Softer shadow */
    position: relative;
    overflow: hidden;
}
.page-title {
    color: var(--text-heading);
    margin-bottom: 35px;
    padding-bottom: 15px;
    border-bottom: 1px solid var(--border-decorative); /* Thinner, delicate line */
    text-align: center;
    font-family: var(--font-serif-elegant);
    font-size: 2.8em; /* Larger, more prominent */
    font-weight: 700;
    letter-spacing: 0.5px;
}
/* Simplified page title decoration */
.page-title:after {
    content: '';
    display: block;
    width: 80px; /* Shorter line */
    height: 2px; /* Thinner line */
    margin: 12px auto 0;
    background: var(--color-aux-rose-gold); /* Solid accent color */
    border-radius: 2px;
    /* animation: wave 3s infinite linear; Removed wave animation for elegance */
}
/* @keyframes wave {
    0%, 100% { background-position-x: 0%; }
    50% { background-position-x: 100%; }
} */
/* Quote Banner - Styled for elegance */
.quote-banner {
    background-color: var(--color-primary-light-pink); /* Soft pink background */
    border-radius: 12px; /* Softer radius */
    padding: 25px 35px; /* Ample padding */
    margin: 0 auto 40px; /* Increased bottom margin */
    max-width: 75%;
    text-align: center;
    box-shadow: 0 4px 15px rgba(183, 110, 121, 0.08); /* Very subtle shadow */
    border-left: 3px solid var(--color-aux-rose-gold);
    border-right: 3px solid var(--color-aux-rose-gold);
    position: relative;
}
.quote-banner p {
    font-family: var(--font-serif-garamond), serif; /* Elegant serif for quote */
    font-style: italic;
    color: var(--color-accent-berry-red); /* Berry red for emphasis */
    font-size: 1.1em; /* Slightly larger */
    margin: 0;
    letter-spacing: 0.2px;
    line-height: 1.6;
}
.quote-banner:before,
.quote-banner:after {
    content: """; /* Using """ for opening */
    font-family: var(--font-serif-elegant), serif; /* Consistent elegant font */
    font-size: 50px; /* Adjusted size */
    color: var(--color-aux-rose-gold); /* Rose gold for quotes */
    opacity: 0.4; /* Softer opacity */
    position: absolute;
    top: 0px;
}
.quote-banner:before {
    left: 15px;
}
.quote-banner:after {
    content: """; /* Using """ for closing */
    right: 15px;
    top: auto; /* Adjust position for closing quote mark */
    bottom: -20px;
}
/* Stats Grid - main navigation cards container */
.stats-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    grid-gap: 30px; /* Increased gap for more whitespace */
    margin: 40px auto; /* Adjusted margin */
    max-width: 900px; /* Adjusted max-width */
}
.stats-grid .stats-card {
    position: relative;
    background-color: var(--background-container);
    border-radius: 12px; /* Softer radius */
    overflow: hidden;
    box-shadow: 0 6px 18px var(--shadow-subtle); /* More subtle shadow */
    transition: transform 0.35s cubic-bezier(0.25, 0.8, 0.25, 1), box-shadow 0.35s cubic-bezier(0.25, 0.8, 0.25, 1);
    text-decoration: none;
    color: var(--text-main);
    border: 1px solid #F0E8E9; /* Very light, almost invisible border */
    min-height: 260px; /* Ensure cards have enough height */
    padding: 0;
}
.card-inner { /* This class is directly inside stats-card links */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 25px; /* Ample padding */
    height: 100%;
    position: relative;
    z-index: 2;
    background: transparent; /* Make it transparent to show card background */
    transition: background-color 0.3s ease;
}
.stats-grid .stats-card:hover {
    transform: translateY(-6px); /* Slightly less aggressive transform */
    box-shadow: 0 10px 25px var(--shadow-soft); /* Enhanced shadow on hover */
    border-color: var(--color-primary-light-pink);
}
.stats-grid .stats-card:hover .card-inner {
    /* background: rgba(255, 248, 240, 0.5); */ /* Optional: very subtle hover background on inner part */
}
.stats-grid .card-icon {
    font-size: 36px; /* Slightly smaller icon */
    margin-bottom: 18px;
    color: var(--color-aux-rose-gold);
    background-color: var(--color-primary-milk-white); /* Milk white for icon background */
    width: 70px; /* Adjusted size */
    height: 70px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    box-shadow: 0 3px 8px rgba(183, 110, 121, 0.15); /* Subtle shadow for icon */
    transition: transform 0.3s ease, color 0.3s ease;
}
.stats-grid .stats-card:hover .card-icon {
    transform: scale(1.08) rotate(3deg);
    color: var(--color-accent-berry-red); /* Icon color change on hover */
}
.stats-grid .card-title {
    font-family: var(--font-serif-lora);
    font-size: 1.45em; /* Adjusted size */
    font-weight: 600;
    margin-bottom: 12px;
    color: var(--text-heading);
    position: relative;
    display: inline-block;
}
.stats-grid .card-title:after { /* Decorative line under card title */
    content: '';
    position: absolute;
    bottom: -6px; /* Positioned slightly below */
    left: 50%;
    transform: translateX(-50%) scaleX(0); /* Start scaled to 0 */
    width: 60%; /* Line width relative to title */
    height: 1.5px;
    background-color: var(--color-primary-light-pink); /* Light pink line */
    transition: transform 0.35s ease-out;
    transform-origin: center;
}
.stats-grid .stats-card:hover .card-title:after {
    transform: translateX(-50%) scaleX(1); /* Scale to full on hover */
}
.stats-grid .card-description {
    font-family: var(--font-sans-clean);
    font-size: 0.9em;
    color: var(--text-soft);
    line-height: 1.5;
    max-width: 90%; /* Prevent text from touching edges */
}
/* Card Decoration - Subtle background elements */
.card-decoration {
    position: absolute;
    bottom: -40px; /* Adjusted position */
    right: -40px;
    width: 120px; /* Smaller decoration */
    height: 120px;
    border-radius: 50%;
    background-color: var(--color-primary-light-pink); /* Light pink base */
    opacity: 0.15; /* More subtle opacity */
    transition: all 0.5s ease;
    z-index: 1;
}
.stats-card:hover .card-decoration { /* Use stats-card hover for decoration */
    transform: scale(1.4);
    opacity: 0.25;
}
/* Specific card decorations with more subtle emoji styling */
.card-decoration:before { /* General style for emoji if used */
    position: absolute;
    font-size: 24px; /* Smaller emoji */
    top: 50%; /* Centered better */
    left: 50%;
    transform: translate(-50%, -50%);
    opacity: 0.3; /* Very subtle */
    color: var(--color-aux-rose-gold); /* Themed color */
}
.book-decoration:before    { content: '📚'; }
.trend-decoration:before   { content: '📈'; }
.user-decoration:before    { content: '👥'; }
.overdue-decoration:before { content: '⏰'; }
/* Page Decoration - Floating elements */
.page-decoration {
    position: absolute;
    width: 180px; /* Slightly smaller */
    height: 180px;
    border-radius: 50%;
    background: linear-gradient(45deg, var(--color-primary-apricot), var(--color-aux-light-purple), var(--color-primary-light-pink)); /* New gradient */
    opacity: 0.15; /* More subtle */
    z-index: -1; /* Ensure it's behind content */
}
.page-decoration.left {
    top: -80px; /* Adjusted position */
    left: -80px;
    animation: floatLeft 18s ease-in-out infinite;
}
.page-decoration.right {
    bottom: -80px;
    right: -80px;
    animation: floatRight 20s ease-in-out infinite;
}
@keyframes floatLeft {
    0%, 100% { transform: translate(0, 0) rotate(0deg) scale(1); }
    25% { transform: translate(15px, 20px) rotate(8deg) scale(1.05); }
    50% { transform: translate(5px, 35px) rotate(15deg) scale(1); }
    75% { transform: translate(25px, 10px) rotate(5deg) scale(1.05); }
}
@keyframes floatRight {
    0%, 100% { transform: translate(0, 0) rotate(0deg) scale(1); }
    25% { transform: translate(-15px, -18px) rotate(-7deg) scale(1.05); }
    50% { transform: translate(-10px, -30px) rotate(-12deg) scale(1); }
    75% { transform: translate(-22px, -12px) rotate(-6deg) scale(1.05); }
}
/* --- Unchanged CSS from this point onwards as per request for elements not in index.html --- */
/* --- (Or elements whose styling should largely be preserved unless overridden by above general styles) --- */
.breadcrumb {
    margin-bottom: 20px;
    font-size: 14px;
    color: var(--light-text); /* Will use new --light-text */
}
.breadcrumb a {
    color: var(--accent-color); /* Will use new --accent-color */
    text-decoration: none;
    transition: all 0.3s ease;
}
.breadcrumb a:hover {
    text-decoration: underline;
    color: var(--color-accent-berry-red); /* More specific hover */
}
.breadcrumb .current-page {
    color: var(--text-color); /* Will use new --text-color */
    font-weight: 500;
}
/* 原始卡片菜单 - Unchanged as it's not used in the provided HTML */
.stats-menu {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 20px;
    margin-top: 30px;
}
/* 原始卡片样式 - This .stats-card is different from .stats-grid .stats-card. Keeping for other pages. */
/* However, some properties might be inherited if not specific enough. */
/* Adding a more specific selector to avoid conflict if this old style is needed elsewhere */
.stats-menu > .stats-card {
    background-color: var(--secondary-color);
    border-radius: 12px;
    padding: 25px;
    box-shadow: 0 4px 12px var(--shadow-color);
    transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275), box-shadow 0.4s;
    text-decoration: none;
    color: var(--text-color);
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    border: 1px solid var(--border-color);
}
.stats-menu > .stats-card:hover {
    transform: translateY(-8px) scale(1.02);
    box-shadow: 0 8px 20px var(--shadow-color);
    border-color: var(--primary-color);
}
/* Card icon/title/description for .stats-menu > .stats-card */
.stats-menu > .stats-card .card-icon {
    font-size: 40px;
    margin-bottom: 15px;
    color: var(--accent-color);
    /* Resetting some properties from .stats-grid .card-icon if they conflict */
    background-color: transparent;
    width: auto;
    height: auto;
    box-shadow: none;
}
.stats-menu > .stats-card .card-title {
    font-size: 18px;
    font-weight: 600;
    margin-bottom: 10px;
    font-family: var(--font-sans-clean); /* Keep it simple for this version */
    color: var(--text-color); /* Default text color for these */
}
.stats-menu > .stats-card .card-title:after {
    display: none; /* No line for this version */
}
.stats-menu > .stats-card .card-description {
    font-size: 14px;
    color: var(--light-text);
    font-family: var(--font-sans-clean);
}
.filter-section {
    margin-bottom: 25px;
    display: flex;
    align-items: center;
    background-color: var(--color-primary-milk-white); /* Updated bg */
    padding: 12px 18px;
    border-radius: 10px;
    border: 1px dashed var(--border-decorative); /* Updated border */
}
.filter-label {
    font-weight: 500;
    margin-right: 10px;
    color: var(--text-main); /* Updated text */
}
.filter-select {
    padding: 8px 15px;
    border: 1px solid var(--border-soft); /* Updated border */
    border-radius: 8px; /* Softer radius */
    background-color: white;
    color: var(--text-main);
    font-size: 0.95em;
    font-family: var(--font-sans-clean);
    transition: border-color 0.3s, box-shadow 0.3s;
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23B76E79' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); /* Updated arrow color */
    background-repeat: no-repeat;
    background-position: right 10px center;
    padding-right: 30px;
}
.filter-select:focus {
    outline: none;
    border-color: var(--color-aux-rose-gold); /* Updated focus color */
    box-shadow: 0 0 0 3px rgba(183, 110, 121, 0.2); /* Updated focus shadow */
}
.ml-20 {
    margin-left: 20px;
}
.chart-container {
    background-color: white;
    border-radius: 12px; /* Softer radius */
    padding: 25px;
    box-shadow: 0 4px 15px var(--shadow-soft); /* Updated shadow */
    margin-bottom: 35px;
    position: relative;
    height: 400px;
    border: 1px solid var(--border-decorative); /* Updated border */
    overflow: hidden;
}
.chart-container canvas {
    max-height: 100%;
    z-index: 1;
    position: relative;
}
.chart-decoration { /* These are for charts, distinct from page/card decorations */
    position: absolute;
    width: 60px;
    height: 60px;
    border-radius: 50%;
    background: linear-gradient(45deg, var(--color-primary-light-pink), var(--color-primary-apricot)); /* Updated gradient */
    opacity: 0.4; /* Softer opacity */
    z-index: 0;
}
.chart-decoration.left {
    top: -15px;
    left: -15px;
}
.chart-decoration.right {
    bottom: -15px;
    right: -15px;
}
.floating {
    animation: floating 6s ease-in-out infinite;
}
@keyframes floating {
    0% { transform: translate(0, 0) scale(1); }
    50% { transform: translate(8px, 8px) scale(1.05); } /* Softer float */
    100% { transform: translate(0, 0) scale(1); }
}
.chart-container.half {
    height: auto;
    min-height: 400px;
    padding-bottom: 40px;
}
.chart-container.half .chart-wrapper {
    height: 340px;
    padding-bottom: 20px;
}
canvas#category-chart {
    max-height: 100%;
    margin-bottom: 20px;
    padding-bottom: 20px;
    position: relative;
}
.chart-container.half::before,
.chart-container.half::after {
    width: 40px;
    height: 40px;
    opacity: 0.2; /* Softer opacity */
}
.chart-container.half .chart-wrapper {
    position: relative;
}
.chart-row {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    margin-bottom: 30px;
}
.half {
    flex: 1 1 calc(50% - 10px);
    min-width: 300px;
}
/* 表格容器样式 */
.table-container {
    margin-bottom: 30px;
    position: relative;
    overflow: hidden;
    border-radius: 12px;
    box-shadow: 0 4px 20px var(--shadow-subtle);
}
.data-table {
    width: 100%;
    border-collapse: collapse; /* 修改为collapse以解决边框问题 */
    border-spacing: 0;
    border-radius: 10px; /* 保持圆角 */
    overflow: hidden;
    box-shadow: 0 2px 10px var(--shadow-subtle); /* 保持阴影 */
    font-family: var(--font-sans-clean); /* 确保一致字体 */
}
.data-table th, .data-table td {
    padding: 14px 18px;
    text-align: left;
    border-bottom: 1px solid var(--border-decorative); /* 保持底部边框 */
    vertical-align: middle; /* 确保所有内容垂直居中 */
    box-sizing: border-box; /* 确保边框计算在单元格尺寸内 */
}
.data-table td {
    font-size: 0.95em;
}
.data-table th {
    background-color: var(--color-primary-light-pink); /* Lighter pink for header */
    font-weight: 600; /* Was 600, can be 400 for softer look */
    color: var(--text-heading); /* Rose gold text for header */
    letter-spacing: 0.5px;
    font-size: 1em;
    border-bottom: 2px solid var(--color-aux-rose-gold);
}
.data-table tr {
    transition: background-color 0.3s;
}
.data-table tr:nth-child(even) {
    background-color: var(--color-primary-milk-white); /* Milk white for even rows */
}
.data-table tr:nth-child(odd) {
    background-color: white;
}
.data-table tr:last-child td {
    border-bottom: none;
}
.data-table tr:hover {
    background-color: #FEF6F8; /* Very light pink on hover */
}
/* 表格特定列的样式 */
.data-table th:first-child,
.data-table td:first-child {
    text-align: center; /* 排名居中 */
    position: relative; /* 确保相对定位 */
}
.data-table th:nth-child(2),
.data-table td:nth-child(2) {
    text-align: center; /* 封面图片居中 */
}
.data-table th:last-child,
.data-table td:last-child {
    text-align: center; /* 借阅次数居中显示 */
}
.loading-row td {
    text-align: center;
    padding: 30px;
    color: var(--text-soft); /* Updated text color */
}
.loading-animation {
    display: flex;
    align-items: center;
    justify-content: center;
}
.loading-animation:before {
    content: '📖';
    margin-right: 10px;
    animation: bookFlip 2s infinite;
    display: inline-block;
    color: var(--color-aux-rose-gold); /* Themed color */
}
@keyframes bookFlip {
    0% { transform: rotateY(0deg); }
    50% { transform: rotateY(180deg); }
    100% { transform: rotateY(360deg); }
}
.dot-animation {
    display: inline-block;
    animation: dotAnimation 1.5s infinite;
}
@keyframes dotAnimation {
    0% { opacity: 0.3; }
    50% { opacity: 1; }
    100% { opacity: 0.3; }
}
.stats-cards { /* This is for the small summary cards, different from .stats-grid */
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 15px;
    margin-bottom: 30px;
}
/* Style for .stats-cards > .stats-card if they exist */
.stats-cards > .stats-card {
    background-color: var(--background-container);
    border: 1px solid var(--border-decorative);
    padding: 20px;
    border-radius: 10px;
    box-shadow: 0 3px 10px var(--shadow-subtle);
    text-align: center;
}
.stats-cards .stats-card .card-value { /* Assuming .card-value is inside these cards */
    font-size: 2em; /* Adjusted size */
    font-weight: 700;
    margin-bottom: 8px;
    color: var(--color-accent-berry-red); /* Berry red for value */
    font-family: var(--font-serif-elegant);
}
.stats-cards .stats-card .card-title { /* Title within these small cards */
    font-family: var(--font-sans-clean);
    font-size: 0.95em;
    color: var(--text-soft);
    font-weight: 400;
}
.stats-cards .stats-card .card-icon { /* Icon within these small cards */
    font-size: 1.8em;
    color: var(--color-aux-rose-gold);
    margin-bottom: 10px;
}
/* Quote Container - Appears distinct from .quote-banner, kept for other pages */
.quote-container {
    text-align: center;
    margin: 40px auto 20px;
    max-width: 600px;
    font-style: italic;
    color: var(--text-main); /* Updated text */
    padding: 20px;
    background-color: var(--color-primary-apricot); /* Apricot background */
    border-radius: 12px; /* Softer radius */
    position: relative;
    font-family: var(--font-serif-garamond);
    box-shadow: 0 3px 10px var(--shadow-subtle);
}
.quote-container:before,
.quote-container:after {
    content: """;
    font-size: 50px;
    font-family: var(--font-serif-elegant);
    position: absolute;
    color: var(--color-aux-rose-gold); /* Rose gold quotes */
    opacity: 0.3; /* Softer */
}
.quote-container:before {
    top: -5px;
    left: 10px;
}
.quote-container:after {
    content: """;
    bottom: -25px;
    right: 10px;
}
.quote-container p {
    position: relative;
    z-index: 1;
    margin-bottom: 10px;
    font-size: 1.05em; /* Adjusted */
    line-height: 1.6;
}
.quote-author {
    display: block;
    font-size: 0.9em;
    font-style: normal;
    text-align: right;
    color: var(--text-soft); /* Updated text */
    font-family: var(--font-sans-clean);
}
/* Book list title - for table pages */
.book-list-title {
    text-align: center;
    margin-bottom: 25px;
    color: var(--text-heading); /* Rose gold */
    font-family: var(--font-serif-lora); /* Lora for this title */
    font-size: 1.8em; /* Adjusted */
    position: relative;
    display: inline-block;
    left: 50%;
    transform: translateX(-50%);
    padding: 0 20px;
}
.book-icon { /* General book icon if used with this title */
    font-size: 0.9em;
    margin: 0 8px;
    opacity: 0.85;
    color: var(--color-aux-rose-gold);
}
.column-icon {
    font-size: 0.9em;
    margin-right: 5px;
    opacity: 0.8;
    color: var(--color-aux-rose-gold);
}
.book-list-title:before,
.book-list-title:after {
    content: '';
    position: absolute;
    height: 1.5px; /* Thinner line */
    background: linear-gradient(to right, transparent, var(--color-primary-light-pink), transparent); /* Softer gradient */
    width: 70px;
    top: 50%;
}
.book-list-title:before {
    right: 100%;
    margin-right: 15px;
}
.book-list-title:after {
    left: 100%;
    margin-left: 15px;
}
/* 表格中的图标样式 */
.data-table .borrow-count {
    font-weight: 600;
    color: var(--text-heading);
    position: relative;
    display: block; /* 修改为block以占据整个单元格 */
    text-align: center; /* 确保文本居中 */
    font-size: 1em;
}
.data-table .borrow-count:after {
    content: '📚';
    font-size: 12px;
    margin-left: 5px;
    opacity: 0;
    transition: opacity 0.3s ease, transform 0.3s ease;
    transform: translateY(5px);
    display: inline-block;
    color: var(--color-aux-rose-gold);
}
.data-table tr:hover .borrow-count:after {
    opacity: 0.7; /* Softer opacity */
    transform: translateY(0);
}
/* 排名列样式 */
.data-table .rank {
    font-weight: 700;
    text-align: center;
    position: relative;
    font-size: 1.1em;
    color: var(--text-heading);
    font-family: var(--font-serif-lora);
    padding: 5px 15px; /* 基本内边距 */
}
/* 前三名奖牌样式 */
.data-table tr:nth-child(1) .rank:before,
.data-table tr:nth-child(2) .rank:before,
.data-table tr:nth-child(3) .rank:before {
    position: absolute;
    font-size: 1.2em;
    left: 5px; /* 左侧位置 */
    top: 50%;
    transform: translateY(-50%);
    opacity: 0.85;
}
/* 分别设置每个奖牌的内容 */
.data-table tr:nth-child(1) .rank:before {
    content: '🏆';
}
.data-table tr:nth-child(2) .rank:before {
    content: '🥈';
}
.data-table tr:nth-child(3) .rank:before {
    content: '🥉';
}
/* 确保所有排名单元格的对齐一致 */
.data-table td:first-child {
    text-align: center;
}
.book-title { /* In data tables */
    position: relative;
    text-decoration: none;
    display: inline-block;
    font-weight: 600; /* Bolder for emphasis */
    color: var(--text-accent); /* Berry red for book titles */
    transition: color 0.3s;
}
.data-table tr:hover .book-title {
    color: var(--color-aux-rose-gold); /* Rose gold on hover */
}
.book-title:after { /* Underline effect for book titles in tables */
    content: '';
    position: absolute;
    width: 100%;
    height: 1.5px;
    bottom: -3px;
    left: 0;
    background-color: var(--color-primary-light-pink); /* Light pink underline */
    transform: scaleX(0);
    transform-origin: bottom right;
    transition: transform 0.3s ease-out;
}
tr:hover .book-title:after {
    transform: scaleX(1);
    transform-origin: bottom left;
}
/* Data table image styling */
.data-table img {
    width: 50px; /* Slightly smaller */
    height: 75px;
    object-fit: cover;
    border-radius: 6px; /* Softer radius */
    box-shadow: 0 2px 6px rgba(0,0,0,0.08); /* Softer shadow */
    transition: transform 0.3s ease, box-shadow 0.3s ease;
    border: 2px solid white;
}
.data-table tr:hover img {
    transform: scale(1.1); /* Slightly more pop */
    box-shadow: 0 4px 10px rgba(0,0,0,0.12);
    border-color: var(--color-primary-light-pink); /* Pink border on hover */
}
.data-table .author {
    font-style: italic;
    color: var(--text-soft); /* Softer text for author */
    font-size: 0.9em;
}
.no-data {
    text-align: center;
    padding: 40px;
    color: var(--text-soft);
    background-color: var(--color-primary-milk-white); /* Milk white background */
    border-radius: 12px;
    font-style: italic;
    border: 1px dashed var(--border-decorative); /* Decorative dashed border */
    font-family: var(--font-serif-garamond);
}
/* 书籍行动画 */
#ranking-table-body tr {
    transition: transform 0.3s ease, opacity 0.3s ease, background-color 0.3s ease; /* Added background-color */
}
#ranking-table-body tr:hover {
    transform: translateX(3px); /* Subtle shift */
}
/* Animation shared */
.fade-in { /* This is a custom class, not from animate.css */
    animation: customFadeIn 0.6s ease forwards; /* Renamed to avoid conflict */
    opacity: 0;
    transform: translateY(15px); /* Slightly more travel */
}
@keyframes customFadeIn { /* Renamed */
    to {
        opacity: 1;
        transform: translateY(0);
    }
}
/* Responsive adjustments */
@media (max-width: 992px) { /* Adjusted breakpoint */
    .stats-grid {
        max-width: 95%;
        gap: 20px; /* Smaller gap on medium screens */
    }
    .stats-grid .stats-card {
        min-height: 240px;
    }
    .page-title {
        font-size: 2.4em;
    }
    .quote-banner {
        max-width: 85%;
    }
}
@media (max-width: 768px) {
    .statistics-container {
        padding: 30px 20px;
        margin: 20px auto;
    }
    .page-title {
        font-size: 2em;
    }
    .quote-banner {
        max-width: 90%;
        padding: 20px;
    }
    .quote-banner:before,
    .quote-banner:after {
        font-size: 35px;
    }
     .quote-banner:after {
        bottom: -15px;
    }
    .stats-grid {
        grid-template-columns: 1fr; /* Single column for cards */
        gap: 25px;
        max-width: 450px; /* Max width for single column */
    }
    .stats-grid .stats-card {
        min-height: auto; /* Auto height for single column */
        height: auto; /* Ensure this is not fixed */
        padding-bottom: 20px; /* Ensure padding for content */
    }
     .stats-grid .card-inner {
        padding: 20px;
    }
    .chart-row {
        flex-direction: column;
    }
    .half {
        width: 100%;
        flex-basis: 100%; /* Ensure it takes full width */
    }
    .stats-cards { /* Small summary cards */
        grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    }
    .filter-section {
        flex-wrap: wrap;
        padding: 10px 15px;
    }
    .filter-select {
        width: 100%;
    }
    .ml-20 {
        margin-left: 0;
        margin-top: 10px;
    }
    .page-decoration { /* Make page decorations smaller or hide on mobile */
        width: 120px;
        height: 120px;
        opacity: 0.1;
    }
    .page-decoration.left {
        top: -60px;
        left: -60px;
    }
    .page-decoration.right {
        bottom: -60px;
        right: -60px;
    }
    .data-table th, .data-table td {
        padding: 10px 12px;
        font-size: 0.9em;
    }
    .data-table img {
        width: 40px;
        height: 60px;
    }
}
@media (max-width: 480px) {
    .page-title {
        font-size: 1.8em;
    }
    .quote-banner p {
        font-size: 1em;
    }
    .stats-grid .card-title {
        font-size: 1.3em;
    }
    .stats-grid .card-description {
        font-size: 0.85em;
    }
    .stats-grid .card-icon {
        width: 60px;
        height: 60px;
        font-size: 30px;
    }
    .statistics-container {
        margin: 15px auto;
        padding: 20px 15px;
    }
    .page-decoration {
        display: none; /* Hide complex decorations on very small screens */
    }
    /* 移动端表格调整 */
    .data-table .rank:before {
        left: -5px; /* 小屏幕上减少偏移量 */
        font-size: 1.2em;
    }
    .data-table .rank {
        padding: 5px 8px; /* 减少内边距 */
    }
}
================================================================================
File: ./app/static/css/inventory-list.css
================================================================================
/* 全局变量设置 */
:root {
    --primary-color: #f2a3b3;
    --primary-light: #ffd6e0;
    --primary-dark: #e57f9a;
    --secondary-color: #a9d1f7;
    --text-color: #4a4a4a;
    --light-text: #6e6e6e;
    --success-color: #77dd77;
    --warning-color: #fdfd96;
    --danger-color: #ff9e9e;
    --background-color: #fff9fb;
    --card-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
    --transition: all 0.3s ease;
    --border-radius: 12px;
    --card-padding: 20px;
}
/* 基础样式 */
body {
    background-color: var(--background-color);
    color: var(--text-color);
    font-family: 'Helvetica Neue', Arial, sans-serif;
    line-height: 1.6;
}
.inventory-container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
}
/* 页面标题 */
.page-header {
    background: linear-gradient(135deg, var(--primary-light), var(--secondary-color));
    border-radius: var(--border-radius);
    margin-bottom: 30px;
    padding: 40px 30px;
    text-align: center;
    box-shadow: var(--card-shadow);
    position: relative;
    overflow: hidden;
}
.page-header::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: url('data:image/svg+xml;utf8,') repeat;
    background-size: 80px 80px;
    opacity: 0.4;
}
.header-content {
    position: relative;
    z-index: 2;
}
.page-header h1 {
    color: #fff;
    margin: 0;
    font-size: 2.5rem;
    font-weight: 300;
    letter-spacing: 1px;
    text-shadow: 1px 1px 3px rgba(0,0,0,0.1);
}
.header-icon {
    margin-right: 15px;
    color: #fff;
}
.subtitle {
    color: #fff;
    margin-top: 10px;
    font-size: 1.1rem;
    font-weight: 300;
    opacity: 0.9;
}
/* 搜索框样式 */
.search-card {
    background: #fff;
    border-radius: var(--border-radius);
    padding: var(--card-padding);
    margin-bottom: 30px;
    box-shadow: var(--card-shadow);
    border-top: 4px solid var(--primary-color);
}
.search-form {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 15px;
}
.search-input-group {
    display: flex;
    flex: 1;
    min-width: 300px;
}
.search-input-container {
    position: relative;
    flex: 1;
}
.search-icon {
    position: absolute;
    left: 15px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--light-text);
}
.search-input {
    width: 100%;
    padding: 12px 15px 12px 40px;
    border: 1px solid #e3e3e3;
    border-radius: var(--border-radius) 0 0 var(--border-radius);
    font-size: 1rem;
    transition: var(--transition);
    outline: none;
}
.search-input:focus {
    border-color: var(--primary-color);
    box-shadow: 0 0 0 3px var(--primary-light);
}
.search-button {
    background-color: var(--primary-color);
    color: white;
    border: none;
    padding: 12px 25px;
    font-size: 1rem;
    border-radius: 0 var(--border-radius) var(--border-radius) 0;
    cursor: pointer;
    transition: var(--transition);
}
.search-button:hover {
    background-color: var(--primary-dark);
}
.log-button {
    background-color: #fff;
    color: var(--primary-color);
    border: 1px solid var(--primary-color);
    padding: 11px 20px;
    border-radius: var(--border-radius);
    text-decoration: none;
    font-size: 0.95rem;
    transition: var(--transition);
    display: inline-flex;
    align-items: center;
    gap: 8px;
}
.log-button:hover {
    background-color: var(--primary-light);
    color: var(--primary-dark);
}
/* 表格样式 */
.table-container {
    background: #fff;
    border-radius: var(--border-radius);
    padding: var(--card-padding);
    margin-bottom: 30px;
    box-shadow: var(--card-shadow);
    overflow: hidden;
}
.inventory-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.95rem;
}
.inventory-table th {
    background-color: var(--primary-light);
    color: var(--primary-dark);
    padding: 15px;
    text-align: left;
    font-weight: 600;
    text-transform: uppercase;
    font-size: 0.85rem;
    letter-spacing: 0.5px;
}
.inventory-table tr {
    border-bottom: 1px solid #f3f3f3;
    transition: var(--transition);
}
.inventory-table tr:last-child {
    border-bottom: none;
}
.inventory-table tr:hover {
    background-color: #f9f9f9;
}
.inventory-table td {
    padding: 15px;
    vertical-align: middle;
}
.book-title {
    font-weight: 500;
    color: var(--text-color);
}
.book-author {
    color: var(--light-text);
    font-style: italic;
}
/* 库存和状态标签样式 */
.stock-badge, .status-badge {
    display: inline-block;
    padding: 6px 12px;
    border-radius: 50px;
    font-size: 0.85rem;
    font-weight: 500;
    text-align: center;
    min-width: 60px;
}
.stock-high {
    background-color: var(--success-color);
    color: #fff;
}
.stock-medium {
    background-color: var(--warning-color);
    color: #8a7800;
}
.stock-low {
    background-color: var(--danger-color);
    color: #fff;
}
.status-active {
    background-color: #d9f5e6;
    color: #2a9d5c;
}
.status-inactive {
    background-color: #ffe8e8;
    color: #e35555;
}
/* 操作按钮 */
.action-buttons {
    display: flex;
    gap: 8px;
}
.btn-adjust, .btn-view {
    padding: 8px 12px;
    border-radius: var(--border-radius);
    text-decoration: none;
    font-size: 0.85rem;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    transition: var(--transition);
}
.btn-adjust {
    background-color: var(--primary-light);
    color: var(--primary-dark);
    border: 1px solid var(--primary-color);
}
.btn-adjust:hover {
    background-color: var(--primary-color);
    color: white;
}
.btn-view {
    background-color: var(--secondary-color);
    color: #3573b5;
    border: 1px solid #8ab9e3;
}
.btn-view:hover {
    background-color: #8ab9e3;
    color: white;
}
/* 分页样式 */
.pagination-wrapper {
    display: flex;
    justify-content: center;
    margin-top: 30px;
}
.pagination {
    display: flex;
    list-style: none;
    padding: 0;
    margin: 0;
    gap: 5px;
}
.page-item {
    display: inline-block;
}
.page-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 40px;
    height: 40px;
    padding: 0 15px;
    border-radius: var(--border-radius);
    background-color: #fff;
    color: var(--text-color);
    text-decoration: none;
    transition: var(--transition);
    border: 1px solid #e3e3e3;
}
.page-item.active .page-link {
    background-color: var(--primary-color);
    color: white;
    border-color: var(--primary-color);
}
.page-item:not(.active) .page-link:hover {
    background-color: var(--primary-light);
    color: var(--primary-dark);
    border-color: var(--primary-light);
}
.page-item.disabled .page-link {
    background-color: #f5f5f5;
    color: #aaa;
    cursor: not-allowed;
}
/* 响应式调整 */
@media (max-width: 992px) {
    .inventory-container {
        padding: 15px;
    }
    .page-header {
        padding: 30px 20px;
    }
    .page-header h1 {
        font-size: 2rem;
    }
}
@media (max-width: 768px) {
    .search-form {
        flex-direction: column;
        align-items: stretch;
    }
    .log-button {
        text-align: center;
    }
    .page-header h1 {
        font-size: 1.8rem;
    }
    .table-container {
        overflow-x: auto;
    }
    .inventory-table {
        min-width: 800px;
    }
    .action-buttons {
        flex-direction: column;
    }
    .btn-adjust, .btn-view {
        text-align: center;
    }
}
@media (max-width: 576px) {
    .page-header {
        padding: 25px 15px;
    }
    .page-header h1 {
        font-size: 1.5rem;
    }
    .subtitle {
        font-size: 1rem;
    }
    .pagination .page-link {
        min-width: 35px;
        height: 35px;
        padding: 0 10px;
        font-size: 0.9rem;
    }
}
================================================================================
File: ./app/static/css/my_borrows.css
================================================================================
/* my_borrows.css - 少女粉色风格图书管理系统 */
:root {
  --primary-color: #e686a5;  /* 主要粉色 */
  --primary-light: #ffedf2;  /* 浅粉色 */
  --primary-dark: #d26a8c;   /* 深粉色 */
  --accent-color: #9a83c9;   /* 紫色点缀 */
  --text-primary: #4a4a4a;   /* 主要文字颜色 */
  --text-secondary: #848484; /* 次要文字颜色 */
  --border-color: #f4d7e1;   /* 边框颜色 */
  --success-color: #7ac9a1;  /* 成功色 */
  --danger-color: #ff8f9e;   /* 危险色 */
  --white: #ffffff;
}
body {
  font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
  color: var(--text-primary);
  background-color: #fdf6f8;
}
/* 容器 */
.container {
  width: 95% !important;
  max-width: 1200px !important;
  margin: 1.5rem auto;
  padding: 1.5rem;
  background-color: var(--white);
  box-shadow: 0 3px 15px rgba(230, 134, 165, 0.15);
  border-radius: 20px;
  box-sizing: border-box;
  position: relative;
}
/* 页面标题 */
.page-title {
  margin-bottom: 1.8rem;
  color: var(--primary-dark);
  border-bottom: 2px solid var(--border-color);
  padding-bottom: 12px;
  font-size: 1.8rem;
  font-weight: 600;
  position: relative;
  text-align: center;
}
.page-title:after {
  content: "";
  position: absolute;
  width: 80px;
  height: 3px;
  background-color: var(--primary-color);
  bottom: -2px;
  left: 50%;
  transform: translateX(-50%);
  border-radius: 3px;
}
/* 标签页样式 */
.tabs {
  display: flex;
  width: 100%;
  margin-bottom: 25px;
  border: none;
  background-color: var(--primary-light);
  border-radius: 25px;
  padding: 5px;
  box-shadow: 0 3px 10px rgba(230, 134, 165, 0.1);
}
/* tab 项 */
.tab {
  flex: 1;
  padding: 10px 20px;
  text-decoration: none;
  color: var(--text-primary);
  margin-right: 2px;
  border-radius: 20px;
  transition: all 0.3s ease;
  font-size: 0.95rem;
  text-align: center;
  white-space: nowrap;
}
.tab:hover {
  background-color: rgba(230, 134, 165, 0.1);
  color: var(--primary-dark);
  text-decoration: none;
}
.tab.active {
  background-color: var(--primary-color);
  color: white;
  box-shadow: 0 3px 8px rgba(230, 134, 165, 0.3);
}
.count {
  background-color: rgba(255, 255, 255, 0.3);
  border-radius: 20px;
  padding: 2px 8px;
  font-size: 0.75em;
  display: inline-block;
  margin-left: 5px;
  font-weight: 600;
}
.tab.active .count {
  background-color: rgba(255, 255, 255, 0.4);
}
/* 借阅列表与表格 */
.borrow-list {
  margin-top: 20px;
  margin-bottom: 2rem;
  width: 100%;
  overflow-x: auto;
}
.borrow-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  margin-bottom: 25px;
  border-radius: 15px;
  overflow: hidden;
  box-shadow: 0 5px 20px rgba(230, 134, 165, 0.08);
}
/* 调整列宽 - 解决状态列和操作列问题 */
.borrow-table th:nth-child(1),
.borrow-table td:nth-child(1) { width: 90px; }
.borrow-table th:nth-child(2),
.borrow-table td:nth-child(2) { width: 20%; }
.borrow-table th:nth-child(3),
.borrow-table td:nth-child(3),
.borrow-table th:nth-child(4),
.borrow-table td:nth-child(4) { width: 15%; }
/* 状态列 */
.borrow-table th:nth-child(5),
.borrow-table td:nth-child(5) {
  width: 15%;
  min-width: 120px;
  position: relative;
  overflow: visible;
  padding: 14px 25px;
  vertical-align: middle;
}
/* 状态表头文字微调 - 向右移动2px */
.borrow-table th:nth-child(5) {
  padding-left: 28px; /* 增加左内边距,使文字看起来稍微向右移动 */
}
/* 操作列 */
.borrow-table th:nth-child(6),
.borrow-table td:nth-child(6) {
  width: 18%;
  min-width: 140px;
  padding: 14px 18px;
  vertical-align: middle;
  text-align: left;
  padding: 14px 0 14px 15px; /* 减少右内边距,增加左内边距 */
}
.borrow-table th,
.borrow-table td {
  padding: 14px 18px;
  text-align: left;
  vertical-align: middle;
}
.borrow-table th {
  background-color: var(--primary-light);
  color: var(--primary-dark);
  font-weight: 600;
  font-size: 0.9rem;
  letter-spacing: 0.3px;
  border-bottom: 1px solid var(--border-color);
}
.borrow-table tr {
  border-bottom: 1px solid var(--border-color);
  transition: all 0.2s ease;
}
.borrow-table tbody tr:last-child {
  border-bottom: none;
}
.borrow-item {
  background-color: var(--white);
}
.borrow-item:hover {
  background-color: rgba(230, 134, 165, 0.03);
}
.borrow-item.overdue {
  background-color: rgba(255, 143, 158, 0.08);
}
/* 图书封面 */
.book-cover img {
  width: 65px;
  height: 90px;
  object-fit: cover;
  border-radius: 8px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
  transition: transform 0.3s ease;
  border: 3px solid var(--white);
}
.book-cover img:hover {
  transform: scale(1.05);
  box-shadow: 0 5px 15px rgba(230, 134, 165, 0.3);
}
/* 书名与作者 */
.book-title {
  font-weight: 600;
  font-size: 1rem;
}
.book-title a {
  color: var(--primary-dark);
  text-decoration: none;
  transition: color 0.3s ease;
}
.book-title a:hover {
  color: var(--primary-color);
}
.book-author {
  color: var(--text-secondary);
  font-size: 0.85rem;
  margin-top: 5px;
  display: flex;
  align-items: center;
}
.book-author:before {
  content: "🖋";
  margin-right: 5px;
  font-size: 0.9em;
}
/* 徽章 - 修复状态显示问题 */
.borrow-table .badge,
.book-status .badge {
  padding: 4px 10px;
  border-radius: 20px;
  font-weight: 500;
  font-size: 0.75rem;
  display: inline-block;
  margin-bottom: 4px;
  letter-spacing: 0.3px;
  white-space: nowrap;
  text-align: center;
  min-width: 60px;
}
.borrow-table .badge {
  position: static;
  top: auto;
  right: auto;
}
.badge-primary { background-color: var(--primary-color); color: white; }
.badge-success { background-color: var(--success-color); color: white; }
.badge-danger  { background-color: var(--danger-color); color: white; }
.badge-info    { background-color: var(--accent-color); color: white; }
.return-date {
  color: var(--text-secondary);
  font-size: 0.85rem;
  margin-top: 5px;
  display: flex;
  align-items: center;
}
.return-date:before {
  content: "📅";
  margin-right: 5px;
}
.text-danger {
  color: var(--danger-color) !important;
  font-weight: 600;
}
/* 操作按钮 - 简化样式 */
.actions {
  display: flex;
  flex-direction: row;
  gap: 10px;
  align-items: center;
  padding-left: 15px; /* 整体左移5px */
  margin-top: 17px;
  margin-right: 30px;
}
.actions .btn {
  min-width: 60px;
  padding: 8px 15px;
  font-size: 0.85rem;
  font-weight: 500;
  border-radius: 20px;
  border: none;
  text-align: center;
  white-space: nowrap;
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.1);
  transition: all 0.3s ease;
}
.btn-success {
  background-color: var(--success-color);
  color: white;
}
.btn-success:hover {
  background-color: #65b088;
  transform: translateY(-2px);
  box-shadow: 0 5px 12px rgba(122, 201, 161, 0.3);
}
.btn-primary {
  background-color: var(--primary-color);
  color: white;
}
.btn-primary:hover {
  background-color: var(--primary-dark);
  transform: translateY(-2px);
  box-shadow: 0 5px 12px rgba(230, 134, 165, 0.3);
}
.btn-secondary {
  background-color: #a0a0a0;
  color: white;
}
/* 无记录状态 */
.no-records {
  text-align: center;
  padding: 60px 30px;
  background-color: var(--primary-light);
  border-radius: 15px;
  margin: 30px 0;
  box-shadow: inset 0 0 15px rgba(230, 134, 165, 0.1);
}
.empty-icon {
  font-size: 4em;
  color: var(--primary-color);
  margin-bottom: 20px;
  opacity: 0.7;
}
.empty-text {
  color: var(--text-primary);
  margin-bottom: 25px;
  font-size: 1.1rem;
  max-width: 450px;
  margin: 0 auto;
  line-height: 1.6;
}
/* 分页 */
.pagination-container {
  display: flex;
  justify-content: center;
  margin-top: 25px;
}
.pagination {
  display: flex;
  list-style: none;
  padding: 0;
  gap: 5px;
}
.page-item { margin: 0 2px; }
.page-link {
  width: 36px;
  height: 36px;
  display: flex;
  justify-content: center;
  align-items: center;
  border-radius: 50%;
  background-color: white;
  color: var(--text-primary);
  border: 1px solid var(--border-color);
  transition: all 0.3s ease;
  font-size: 0.9rem;
}
.page-item.active .page-link,
.page-link:hover {
  background-color: var(--primary-color);
  color: white;
  border-color: var(--primary-color);
  box-shadow: 0 3px 8px rgba(230, 134, 165, 0.3);
}
/* 模态框 */
.modal-dialog {
  max-width: 95%;
  width: 500px;
  margin: 1.75rem auto;
}
.modal-content {
  border-radius: 15px;
  border: none;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
  overflow: hidden;
}
.modal-header {
  background-color: var(--primary-light);
  color: var(--primary-dark);
  border-bottom: 1px solid var(--border-color);
  padding: 15px 20px;
}
.modal-body {
  padding: 25px 20px;
  font-size: 1.1rem;
  text-align: center;
}
.modal-footer {
  border-top: 1px solid var(--border-color);
  padding: 15px 20px;
  display: flex;
  justify-content: center;
  gap: 10px;
}
/* 响应式 */
@media (max-width: 992px) {
  .container {
    width: 98% !important;
    padding: 1rem;
    margin: 0.5rem auto;
  }
}
@media (max-width: 768px) {
  .tabs {
    flex-direction: column;
    background: none;
    padding: 0;
  }
  .tab {
    border-radius: 15px;
    margin-bottom: 8px;
    margin-right: 0;
    padding: 12px 15px;
    background-color: var(--primary-light);
  }
  .borrow-table {
    min-width: 700px; /* 确保在小屏幕上可以滚动 */
  }
  .book-cover img {
    width: 45px;
    height: 65px;
  }
}
================================================================================
File: ./app/static/css/announcement-list.css
================================================================================
/* Fresh & Vibrant Style for Announcement List */
:root {
    --mint-green: #A8E6CF;
    --pale-yellow: #FFD3B6;
    --coral-pink: #FFAAA5;
    --sky-blue: #BDE4F4;
    --clean-white: #FFFFFF;
    --bright-orange: #FF8C69; /* Emphasis for buttons/key info */
    --lemon-yellow: #FFFACD;
    --text-dark: #424242;
    --text-medium: #616161; /* Slightly darker medium for better contrast */
    --text-light: #888888; /* Adjusted light text */
    --font-title: 'Poppins', sans-serif;
    --font-body: 'Nunito Sans', sans-serif;
    --card-shadow: 0 5px 18px rgba(0, 0, 0, 0.07);
    --card-hover-shadow: 0 8px 25px rgba(0, 0, 0, 0.12);
    --border-radius-main: 16px; /* Slightly larger radius for a softer look */
    --border-radius-small: 10px;
}
/* Apply base font and background to body (likely in base.css) */
body {
    font-family: var(--font-body);
    background-color: #fcfdfe; /* Very light off-white, almost white */
    color: var(--text-dark);
    line-height: 1.65;
}
.announcement-container {
    padding: 25px 30px;
    max-width: 960px;
    margin: 25px auto;
    background-color: var(--clean-white);
    border-radius: var(--border-radius-main);
    /* Optional: Subtle gradient background for the container itself */
    /* background-image: linear-gradient(to bottom right, #f0f9ff, #ffffff); */
}
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 30px;
    padding-bottom: 20px;
    border-bottom: 1px solid #eef2f5; /* Softer border */
}
.page-header h1 {
    font-family: var(--font-title);
    font-size: 2.2rem;
    font-weight: 700;
    color: var(--text-dark);
    margin: 0;
    display: flex;
    align-items: center;
}
.page-icon { /* Icon for page title */
    color: var(--coral-pink);
    margin-right: 12px;
    font-size: 1.8rem;
}
/* Optional: Style for a "Create New" button if you add one */
.btn-fresh-create {
    background-color: var(--mint-green);
    color: #3a7c68; /* Darker mint for text */
    border: none;
    padding: 10px 20px;
    border-radius: 25px;
    font-family: var(--font-body);
    font-weight: 600;
    text-decoration: none;
    transition: all 0.3s ease;
    font-size: 0.9rem;
    box-shadow: 0 2px 8px rgba(168, 230, 207, 0.4);
}
.btn-fresh-create:hover {
    background-color: #97e0c6;
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(168, 230, 207, 0.5);
}
.btn-fresh-create i {
    margin-right: 8px;
}
.announcement-list {
    margin-top: 20px;
    display: grid;
    gap: 25px; /* Spacing between announcement items */
}
.announcement-item {
    background-color: var(--clean-white);
    border-radius: var(--border-radius-main);
    box-shadow: var(--card-shadow);
    padding: 25px 30px;
    position: relative; /* For pin-badge */
    transition: transform 0.25s ease-out, box-shadow 0.25s ease-out;
    overflow: hidden; /* If using pseudo-elements for borders */
}
.announcement-item:hover {
    transform: translateY(-5px) scale(1.01);
    box-shadow: var(--card-hover-shadow);
}
.announcement-item.pinned {
    /* Use a top border or a more distinct background */
    border-top: 4px solid var(--mint-green);
    background-color: #f6fffb; /* Light mint */
}
.pin-badge {
    position: absolute;
    top: 0px;
    right: 0px;
    background: linear-gradient(135deg, var(--mint-green), #8fdcc3);
    color: var(--clean-white);
    padding: 6px 15px 6px 20px;
    border-radius: 0 0 0 var(--border-radius-main); /* Creative corner */
    font-size: 0.8rem;
    font-weight: 600;
    font-family: var(--font-body);
    box-shadow: -2px 2px 8px rgba(168, 230, 207, 0.3);
}
.pin-badge i {
    margin-right: 6px;
    font-size: 0.75rem;
}
.announcement-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start; /* Align date to top if title wraps */
    margin-bottom: 10px;
}
.announcement-header h3 {
    margin: 0;
    font-size: 1.4rem; /* Slightly larger title */
    font-family: var(--font-title);
    font-weight: 600;
    line-height: 1.3;
    margin-right: 15px; /* Space between title and date */
}
.announcement-header h3 a {
    color: var(--text-dark);
    text-decoration: none;
    transition: color 0.2s ease;
}
.announcement-header h3 a:hover {
    color: var(--coral-pink);
}
.date {
    color: var(--text-light);
    font-size: 0.85rem;
    font-weight: 400;
    white-space: nowrap; /* Prevent date from wrapping */
    padding-top: 3px; /* Align better with h3 */
}
.announcement-preview {
    margin: 15px 0;
    color: var(--text-medium);
    line-height: 1.7;
    font-size: 0.95rem;
    letter-spacing: 0.1px;
}
.announcement-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 20px;
    padding-top: 15px;
    border-top: 1px solid #f0f4f7; /* Lighter separator */
}
.publisher {
    color: var(--text-light);
    font-size: 0.85rem;
    display: flex;
    align-items: center;
}
.publisher i {
    margin-right: 6px;
    color: var(--sky-blue);
}
.read-more {
    color: var(--bright-orange);
    text-decoration: none;
    font-weight: 600;
    font-size: 0.9rem;
    font-family: var(--font-body);
    display: inline-flex; /* Allows icon alignment and hover effects */
    align-items: center;
    padding: 6px 12px;
    border-radius: 20px;
    background-color: transparent;
    transition: background-color 0.2s ease, color 0.2s ease, transform 0.2s ease;
}
.read-more:hover {
    background-color: var(--bright-orange);
    color: var(--clean-white);
    transform: translateX(3px);
}
.read-more i {
    margin-left: 6px;
    transition: transform 0.2s ease-in-out;
}
/* .read-more:hover i {
    transform: translateX(4px);
} */ /* Handled by transform on .read-more now */
/* Pagination Styles (copied and adapted from previous for consistency) */
.pagination-container {
    margin-top: 40px;
    display: flex;
    justify-content: center;
}
.pagination {
    display: flex;
    list-style: none;
    padding-left: 0;
}
.pagination .page-item .page-link {
    color: var(--coral-pink);
    background-color: var(--clean-white);
    border: 1px solid var(--pale-yellow);
    margin: 0 5px; /* Slightly more spacing */
    border-radius: 50%;
    width: 40px; /* Slightly larger */
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 0.95rem;
    font-family: var(--font-body);
    transition: all 0.25s ease-in-out;
    box-shadow: 0 1px 3px rgba(0,0,0,0.05);
}
.pagination .page-item .page-link:hover {
    background-color: var(--pale-yellow);
    color: var(--coral-pink);
    border-color: var(--coral-pink);
    text-decoration: none;
    transform: translateY(-2px);
    box-shadow: 0 3px 8px rgba(255, 211, 182, 0.5);
}
.pagination .page-item.active .page-link {
    background-color: var(--coral-pink);
    border-color: var(--coral-pink);
    color: var(--clean-white);
    box-shadow: 0 4px 10px rgba(255, 170, 165, 0.6);
}
.pagination .page-item.disabled .page-link {
    color: #cccccc;
    background-color: #f9f9f9;
    border-color: #eeeeee;
    pointer-events: none;
    box-shadow: none;
}
.no-records {
    text-align: center;
    padding: 60px 30px;
    background-color: #fffaf8; /* Very light coral/yellow tint */
    border-radius: var(--border-radius-main);
    color: var(--text-medium);
    margin-top: 20px;
    box-shadow: var(--card-shadow);
}
.no-records-icon {
    width: 60px;
    height: 60px;
    margin-bottom: 20px;
    opacity: 0.9;
}
/* Fallback for FontAwesome if SVG doesn't load or is removed */
.no-records .fas.fa-info-circle {
    font-size: 3.5rem;
    margin-bottom: 20px;
    color: var(--coral-pink);
    opacity: 0.8;
}
.no-records p {
    font-size: 1.15rem;
    font-family: var(--font-body);
    font-weight: 600;
    color: var(--text-dark);
    line-height: 1.6;
}
================================================================================
File: ./app/static/css/notifications.css
================================================================================
/* Fresh & Vibrant Style for Notifications */
:root {
    --mint-green: #A8E6CF;
    --pale-yellow: #FFD3B6;
    --coral-pink: #FFAAA5;
    --sky-blue: #BDE4F4;
    --clean-white: #FFFFFF;
    --bright-orange: #FF8C69; /* Emphasis for buttons/key info */
    --lemon-yellow: #FFFACD; /* Can be used for subtle highlights */
    --text-dark: #424242; /* Slightly softer than pure black */
    --text-medium: #757575;
    --text-light: #9E9E9E;
    --font-title: 'Poppins', sans-serif;
    --font-body: 'Nunito Sans', sans-serif;
    --card-shadow: 0 4px 15px rgba(0, 0, 0, 0.06);
    --card-hover-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
    --border-radius-main: 12px;
    --border-radius-small: 8px;
}
/* Apply base font and background to body (likely in base.css, but good for context) */
body {
    font-family: var(--font-body);
    background-color: var(--clean-white); /* Or a very light tint like #FDFCFA */
    color: var(--text-dark);
    line-height: 1.6;
    font-weight: 400;
}
.notifications-container {
    padding: 25px 30px;
    max-width: 900px;
    margin: 20px auto;
    background-color: var(--clean-white);
    /* Optional: add a subtle pattern or a large soft circular gradient */
    /* background-image: linear-gradient(135deg, var(--mint-green) -20%, var(--clean-white) 30%); */
    border-radius: var(--border-radius-main);
   /* box-shadow: 0 10px 30px rgba(168, 230, 207, 0.2); */ /* Subtle shadow for container */
}
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 30px;
    padding-bottom: 20px;
    border-bottom: 1px solid #f0f0f0; /* Softer border */
}
.page-header h1 {
    font-family: var(--font-title);
    font-size: 2rem; /* Slightly larger */
    font-weight: 600;
    color: var(--text-dark);
    margin: 0;
}
/* Fresh Action Button Style */
.btn-fresh-action {
    background-color: var(--bright-orange);
    color: var(--clean-white);
    border: none;
    padding: 10px 20px;
    border-radius: 25px; /* Pill shape */
    font-family: var(--font-body);
    font-weight: 600;
    text-decoration: none;
    transition: all 0.3s ease;
    font-size: 0.9rem;
    box-shadow: 0 2px 8px rgba(255, 140, 105, 0.3);
}
.btn-fresh-action:hover {
    background-color: #ff7b5a; /* Slightly darker orange */
    color: var(--clean-white);
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(255, 140, 105, 0.4);
}
.btn-fresh-action i {
    margin-right: 8px;
}
.filter-tabs {
    display: flex;
    margin-bottom: 25px;
    gap: 10px;
    /* border-bottom: 2px solid var(--pale-yellow); */ /* Optional subtle line */
}
.filter-tab {
    padding: 10px 20px;
    color: var(--text-medium);
    text-decoration: none;
    border-radius: var(--border-radius-small); /* Rounded tabs */
    font-weight: 600;
    font-size: 0.95rem;
    transition: all 0.3s ease;
    border-bottom: 3px solid transparent; /* Underline effect for active */
}
.filter-tab:hover {
    color: var(--coral-pink);
    background-color: rgba(255, 170, 165, 0.1); /* Light coral tint on hover */
}
.filter-tab.active {
    color: var(--coral-pink);
    border-bottom-color: var(--coral-pink);
    /* background-color: var(--coral-pink); */
    /* color: var(--clean-white); */
}
.notifications-list {
    margin-top: 20px;
    display: grid;
    gap: 20px;
}
.notification-card {
    background-color: var(--clean-white);
    border-radius: var(--border-radius-main);
    box-shadow: var(--card-shadow);
    padding: 20px 25px;
    transition: transform 0.25s ease, box-shadow 0.25s ease;
    display: flex; /* For icon alignment */
    align-items: flex-start; /* Align icon to top of content */
    gap: 15px;
    border-left: 5px solid transparent; /* Placeholder for unread state */
}
.notification-icon-area {
    font-size: 1.5rem;
    color: var(--sky-blue);
    padding-top: 5px; /* Align with title */
}
.notification-card.unread .notification-icon-area {
    color: var(--mint-green);
}
.notification-card:hover {
    transform: translateY(-4px);
    box-shadow: var(--card-hover-shadow);
}
.notification-card.unread {
    border-left-color: var(--mint-green);
    background-color: #f6fffb; /* Very light mint */
}
.notification-content {
    flex-grow: 1;
}
.notification-title {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 0;
    margin-bottom: 8px;
    font-size: 1.15rem; /* Adjusted size */
    font-family: var(--font-title);
    font-weight: 600;
}
.notification-title a {
    color: var(--text-dark);
    text-decoration: none;
    transition: color 0.2s ease;
}
.notification-title a:hover {
    color: var(--coral-pink);
}
.unread-badge {
    background-color: var(--bright-orange);
    color: white;
    font-size: 0.7rem;
    padding: 4px 10px;
    border-radius: 15px; /* Pill shape */
    margin-left: 10px;
    font-weight: 600;
    letter-spacing: 0.5px;
}
.notification-text {
    color: var(--text-medium);
    margin-bottom: 15px;
    line-height: 1.6;
    font-size: 0.9rem;
    letter-spacing: 0.2px;
}
.notification-meta {
    display: flex;
    justify-content: space-between;
    align-items: center;
    color: var(--text-light);
    font-size: 0.8rem;
}
.notification-type {
    background-color: var(--sky-blue); /* Sky Blue for type */
    color: #3E84A8; /* Darker text for contrast on sky blue */
    padding: 3px 10px;
    border-radius: var(--border-radius-small);
    font-weight: 600;
}
.notification-time {
    font-style: italic;
}
/* Pagination */
.pagination-container {
    margin-top: 30px;
    display: flex;
    justify-content: center;
}
.pagination {
    display: flex;
    list-style: none;
    padding-left: 0;
}
.pagination .page-item .page-link {
    color: var(--coral-pink);
    background-color: var(--clean-white);
    border: 1px solid var(--pale-yellow);
    margin: 0 4px;
    border-radius: 50%; /* Circular pagination items */
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 0.9rem;
    transition: all 0.2s ease-in-out;
}
.pagination .page-item .page-link:hover {
    background-color: var(--pale-yellow);
    color: var(--coral-pink);
    border-color: var(--coral-pink);
    text-decoration: none;
}
.pagination .page-item.active .page-link {
    background-color: var(--coral-pink);
    border-color: var(--coral-pink);
    color: var(--clean-white);
    box-shadow: 0 2px 5px rgba(255, 170, 165, 0.5);
}
.pagination .page-item.disabled .page-link {
    color: #ccc;
    background-color: #f8f8f8;
    border-color: #eee;
    pointer-events: none;
}
.no-records {
    text-align: center;
    padding: 50px 20px;
    background-color: #fafffd; /* Very light mint/yellow mix */
    border-radius: var(--border-radius-main);
    color: var(--text-medium);
    margin-top: 20px;
}
.no-records-icon { /* Style for the inline SVG */
    width: 60px;
    height: 60px;
    margin-bottom: 20px;
    opacity: 0.8;
}
/* If using Font Awesome for no-records icon: */
.no-records .fas.fa-bell-slash {
    font-size: 3.5rem;
    margin-bottom: 20px;
    color: var(--mint-green);
    opacity: 0.7;
}
.no-records p {
    font-size: 1.1rem;
    font-family: var(--font-body);
    font-weight: 600;
    color: var(--text-dark);
}
/* Notification Dropdown Styles (assuming this is for a navbar dropdown or similar) */
/* These are kept minimal as the main focus was the page content */
.notification-dropdown {
    width: 350px; /* Wider for more content */
    padding: 0;
    max-height: 450px;
    overflow-y: auto;
    border-radius: var(--border-radius-small);
    box-shadow: 0 5px 25px rgba(0,0,0,0.1);
    background-color: var(--clean-white);
}
.notification-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 12px 15px;
    background-color: var(--pale-yellow); /* Light yellow header */
    border-bottom: 1px solid #f0e0d0;
}
.notification-header h5 {
    margin:0;
    font-family: var(--font-title);
    font-weight: 600;
    color: var(--text-dark);
}
.mark-all-read { /* Link in dropdown header */
    font-size: 0.8rem;
    color: var(--coral-pink);
    font-weight: 600;
    text-decoration: none;
}
.mark-all-read:hover {
    text-decoration: underline;
}
.notification-items {
    max-height: 300px;
    overflow-y: auto;
}
.notification-item {
    padding: 12px 15px;
    border-bottom: 1px solid #f5f5f5;
    transition: background-color 0.2s ease;
}
.notification-item:last-child {
    border-bottom: none;
}
.notification-item:hover {
    background-color: var(--mint-green-light, #e6f7f0); /* Very light mint on hover */
}
.notification-item.unread {
    background-color: #fff8f0; /* Very light orange/yellow for unread in dropdown */
    border-left: 3px solid var(--bright-orange);
    padding-left: 12px;
}
.notification-item .notification-content h6 { /* Assuming title in dropdown is h6 */
    margin-bottom: 5px;
    font-size: 0.9rem;
    font-family: var(--font-title);
    font-weight: 600;
    color: var(--text-dark);
}
.notification-item .notification-text { /* Text snippet in dropdown */
    font-size: 0.8rem;
    color: var(--text-medium);
    margin-bottom: 5px;
    line-height: 1.4;
}
.notification-item .notification-time {
    font-size: 0.75rem;
    color: var(--text-light);
}
.view-all { /* Footer link in dropdown */
    text-align: center;
    font-weight: 600;
    padding: 12px 15px;
    display: block;
    text-decoration: none;
    color: var(--bright-orange);
    background-color: #fffaf5;
    transition: background-color 0.2s ease;
}
.view-all:hover {
    background-color: var(--pale-yellow);
}
.no-notifications { /* Message in empty dropdown */
    padding: 25px;
    text-align: center;
    color: var(--text-medium);
    font-size: 0.9rem;
}
================================================================================
File: ./app/static/css/inventory-logs.css
================================================================================
/* 冰雪奇缘主题库存日志页面样式 */
@import url('https://fonts.googleapis.com/css2?family=Dancing+Script:wght@400;700&family=Nunito:wght@300;400;600;700&display=swap');
:root {
  --primary-blue: #6fa8dc;
  --light-blue: #cfe2f3;
  --dark-blue: #1a5190;
  --accent-pink: #f4b8c4;
  --accent-purple: #b19cd9;
  --subtle-gold: #ffd966;
  --ice-white: #f3f9ff;
  --snow-white: #ffffff;
  --text-dark: #2c3e50;
  --text-light: #ecf0f1;
  --shadow-color: rgba(0, 53, 102, 0.15);
  --frost-blue: #a2d5f2;
  --elsa-blue: #85c1e9;
  --anna-purple: #c39bd3;
  --olaf-white: #f9fcff;
}
/* 全局样式重置 */
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
body {
  font-family: 'Nunito', sans-serif;
  background: #f5f9ff url('/static/images/disney-bg.jpg') no-repeat center center fixed;
  background-size: cover;
  color: var(--text-dark);
  line-height: 1.6;
  position: relative;
  overflow-x: hidden;
  min-height: 100vh;
}
/* 容器样式 */
.disney-container {
  max-width: 95%;
  margin: 2rem auto;
  padding: 0 15px;
  position: relative;
  z-index: 1;
}
/* 魔法粒子效果层 */
#magic-particles {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
}
/* 主卡片样式 */
.disney-card {
  background: linear-gradient(135deg, var(--ice-white) 0%, var(--snow-white) 100%);
  border-radius: 20px;
  box-shadow: 0 10px 30px var(--shadow-color),
              0 0 50px rgba(137, 196, 244, 0.3),
              inset 0 0 15px rgba(255, 255, 255, 0.8);
  overflow: hidden;
  position: relative;
  margin-bottom: 2rem;
  border: 1px solid rgba(200, 223, 255, 0.8);
  animation: card-glow 3s infinite alternate;
}
/* 卡片发光动画 */
@keyframes card-glow {
  from {
    box-shadow: 0 10px 30px var(--shadow-color),
                0 0 50px rgba(137, 196, 244, 0.3),
                inset 0 0 15px rgba(255, 255, 255, 0.8);
  }
  to {
    box-shadow: 0 10px 30px var(--shadow-color),
                0 0 70px rgba(137, 196, 244, 0.5),
                inset 0 0 20px rgba(255, 255, 255, 0.9);
  }
}
/* 装饰元素 */
.disney-decoration {
  position: absolute;
  background-size: contain;
  background-repeat: no-repeat;
  opacity: 0.7;
  z-index: 1;
  pointer-events: none;
}
.book-icon {
  top: 20px;
  right: 30px;
  width: 60px;
  height: 60px;
  background-image: url('https://api.iconify.design/ph:books-duotone.svg?color=%236fa8dc');
  transform: rotate(10deg);
  animation: float 6s ease-in-out infinite;
}
.crown-icon {
  bottom: 40px;
  left: 20px;
  width: 50px;
  height: 50px;
  background-image: url('https://api.iconify.design/fa6-solid:crown.svg?color=%23ffd966');
  animation: float 5s ease-in-out infinite 1s;
}
.wand-icon {
  top: 60px;
  left: 40px;
  width: 40px;
  height: 40px;
  background-image: url('https://api.iconify.design/fa-solid:magic.svg?color=%23b19cd9');
  animation: float 7s ease-in-out infinite 0.5s;
}
.snowflake-icon {
  bottom: 70px;
  right: 50px;
  width: 45px;
  height: 45px;
  background-image: url('https://api.iconify.design/fa-regular:snowflake.svg?color=%23a2d5f2');
  animation: float 4s ease-in-out infinite 1.5s, spin 15s linear infinite;
}
@keyframes float {
  0% { transform: translateY(0) rotate(0deg); }
  50% { transform: translateY(-15px) rotate(5deg); }
  100% { transform: translateY(0) rotate(0deg); }
}
@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
/* 卡片头部 */
.card-header-disney {
  background: linear-gradient(45deg, var(--elsa-blue), var(--frost-blue));
  color: var(--text-light);
  padding: 1.5rem;
  text-align: center;
  position: relative;
  border-bottom: 3px solid rgba(255, 255, 255, 0.5);
}
.card-header-disney h4 {
  font-size: 1.8rem;
  font-weight: 700;
  margin: 0;
  text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.2);
  position: relative;
  z-index: 2;
  font-family: 'Dancing Script', cursive;
  letter-spacing: 1px;
}
.card-header-disney i {
  margin-right: 10px;
  color: var(--subtle-gold);
  animation: pulse 2s infinite;
}
.princess-crown {
  position: absolute;
  top: -20px;
  left: 50%;
  transform: translateX(-50%);
  width: 60px;
  height: 30px;
  background-image: url('https://api.iconify.design/fa6-solid:crown.svg?color=%23ffd966');
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  filter: drop-shadow(0 0 5px rgba(255, 217, 102, 0.7));
}
@keyframes pulse {
  0% { transform: scale(1); }
  50% { transform: scale(1.1); }
  100% { transform: scale(1); }
}
/* 卡片内容 */
.card-body-disney {
  padding: 2rem;
  position: relative;
  z-index: 2;
}
/* 图书信息部分 */
.book-details-container {
  display: flex;
  background: linear-gradient(to right, rgba(162, 213, 242, 0.1), rgba(177, 156, 217, 0.1));
  border-radius: 15px;
  padding: 1.5rem;
  margin-bottom: 2rem;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
  border: 1px solid rgba(162, 213, 242, 0.3);
  position: relative;
  overflow: hidden;
}
.book-cover-wrapper {
  flex: 0 0 150px;
  margin-right: 2rem;
  position: relative;
}
.disney-book-cover {
  width: 100%;
  height: auto;
  border-radius: 8px;
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
  border: 4px solid white;
  object-fit: cover;
  z-index: 2;
  position: relative;
}
.disney-book-cover:hover {
  transform: translateY(-5px) scale(1.03);
  box-shadow: 0 15px 25px rgba(0, 0, 0, 0.15);
}
.book-cover-glow {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: radial-gradient(circle, rgba(162, 213, 242, 0.6) 0%, rgba(255, 255, 255, 0) 70%);
  z-index: 1;
  opacity: 0;
  transition: opacity 0.3s ease;
  pointer-events: none;
}
.book-cover-wrapper:hover .book-cover-glow {
  opacity: 1;
  animation: glow-pulse 2s infinite;
}
@keyframes glow-pulse {
  0% { opacity: 0.3; }
  50% { opacity: 0.7; }
  100% { opacity: 0.3; }
}
.book-info {
  flex: 1;
}
.book-title {
  font-family: 'Dancing Script', cursive;
  font-size: 2rem;
  margin-bottom: 1rem;
  color: var(--dark-blue);
  text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
  position: relative;
  display: inline-block;
}
.book-title::after {
  content: '';
  position: absolute;
  bottom: -5px;
  left: 0;
  width: 100%;
  height: 2px;
  background: linear-gradient(to right, var(--elsa-blue), var(--anna-purple));
  border-radius: 2px;
}
.info-row {
  display: flex;
  align-items: center;
  margin-bottom: 0.8rem;
  font-size: 1rem;
}
.disney-icon {
  width: 24px;
  height: 24px;
  margin-right: 10px;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}
.author-icon {
  background-image: url('https://api.iconify.design/fa-solid:user-edit.svg?color=%236fa8dc');
}
.publisher-icon {
  background-image: url('https://api.iconify.design/fa-solid:building.svg?color=%236fa8dc');
}
.isbn-icon {
  background-image: url('https://api.iconify.design/fa-solid:barcode.svg?color=%236fa8dc');
}
.stock-icon {
  background-image: url('https://api.iconify.design/fa-solid:warehouse.svg?color=%236fa8dc');
}
.stock-badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 12px;
  font-weight: bold;
  font-size: 0.9rem;
  color: white;
  margin-left: 5px;
}
.high-stock {
  background-color: #2ecc71;
  animation: badge-pulse 2s infinite;
}
.low-stock {
  background-color: #f39c12;
}
.out-stock {
  background-color: #e74c3c;
}
@keyframes badge-pulse {
  0% { transform: scale(1); }
  50% { transform: scale(1.1); }
  100% { transform: scale(1); }
}
/* 日志部分 */
.logs-section {
  background-color: var(--ice-white);
  border-radius: 15px;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
  padding: 1.5rem;
  position: relative;
  overflow: hidden;
  border: 1px solid rgba(162, 213, 242, 0.3);
}
.logs-section::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-image: url('https://api.iconify.design/ph:snowflake-thin.svg?color=%23a2d5f2');
  background-size: 20px;
  opacity: 0.05;
  pointer-events: none;
  animation: snow-bg 60s linear infinite;
}
@keyframes snow-bg {
  from { background-position: 0 0; }
  to { background-position: 100% 100%; }
}
.logs-title {
  text-align: center;
  font-size: 1.5rem;
  margin-bottom: 1.5rem;
  color: var(--dark-blue);
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Dancing Script', cursive;
}
.title-decoration {
  width: 100px;
  height: 2px;
  background: linear-gradient(to right, transparent, var(--elsa-blue), transparent);
  margin: 0 15px;
}
.title-decoration.right {
  transform: scaleX(-1);
}
.table-container {
  overflow-x: auto;
  border-radius: 10px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
  margin-bottom: 1.5rem;
}
.disney-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  background-color: white;
  border-radius: 10px;
  overflow: hidden;
}
.disney-table thead {
  background: linear-gradient(45deg, var(--elsa-blue), var(--frost-blue));
  color: white;
}
.disney-table th {
  padding: 1rem 0.8rem;
  text-align: left;
  font-weight: 600;
  letter-spacing: 0.5px;
  position: relative;
  border-bottom: 1px solid rgba(255, 255, 255, 0.3);
}
.disney-table th:first-child {
  border-top-left-radius: 10px;
}
.disney-table th:last-child {
  border-top-right-radius: 10px;
}
.disney-table td {
  padding: 0.8rem;
  border-bottom: 1px solid rgba(162, 213, 242, 0.2);
  vertical-align: middle;
}
.log-row {
  transition: all 0.3s ease;
}
.log-row:hover {
  background-color: rgba(162, 213, 242, 0.1);
  transform: translateY(-2px);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
}
.operation-badge {
  padding: 4px 10px;
  border-radius: 20px;
  font-size: 0.8rem;
  font-weight: 600;
  text-align: center;
  display: inline-block;
  min-width: 80px;
}
.in-badge {
  background-color: rgba(46, 204, 113, 0.15);
  color: #27ae60;
  border: 1px solid rgba(46, 204, 113, 0.3);
}
.out-badge {
  background-color: rgba(231, 76, 60, 0.15);
  color: #c0392b;
  border: 1px solid rgba(231, 76, 60, 0.3);
}
.remark-cell {
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.empty-logs {
  text-align: center;
  padding: 3rem 0 !important;
}
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  color: #95a5a6;
}
.empty-icon {
  width: 80px;
  height: 80px;
  background-image: url('https://api.iconify.design/ph:book-open-duotone.svg?color=%2395a5a6');
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  margin-bottom: 1rem;
  opacity: 0.7;
}
.empty-state p {
  font-size: 1.1rem;
}
/* 分页样式 */
.disney-pagination {
  margin-top: 1.5rem;
  display: flex;
  justify-content: center;
}
.pagination-list {
  display: flex;
  list-style: none;
  gap: 5px;
  align-items: center;
}
.page-item {
  margin: 0 2px;
}
.page-link {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px 12px;
  border-radius: 20px;
  color: var(--dark-blue);
  background-color: white;
  text-decoration: none;
  font-weight: 600;
  transition: all 0.3s ease;
  min-width: 40px;
  border: 1px solid rgba(111, 168, 220, 0.3);
}
.page-link:hover:not(.disabled .page-link) {
  background-color: var(--light-blue);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  transform: translateY(-2px);
}
.page-item.active .page-link {
  background: linear-gradient(45deg, var(--elsa-blue), var(--frost-blue));
  color: white;
  box-shadow: 0 4px 8px rgba(111, 168, 220, 0.3);
}
.page-item.dots .page-link {
  border: none;
  background: none;
  pointer-events: none;
}
.page-item.disabled .page-link {
  color: #b2bec3;
  pointer-events: none;
  background-color: rgba(236, 240, 241, 0.5);
  border: 1px solid rgba(189, 195, 199, 0.3);
}
/* 卡片底部 */
.card-footer-disney {
  padding: 1.5rem;
  background: linear-gradient(45deg, rgba(162, 213, 242, 0.2), rgba(177, 156, 217, 0.2));
  border-top: 1px solid rgba(162, 213, 242, 0.3);
}
.button-container {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
}
.disney-button {
  padding: 10px 20px;
  border-radius: 30px;
  font-weight: 600;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  transition: all 0.3s ease;
  position: relative;
  overflow: hidden;
  z-index: 1;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.disney-button::before {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
  transition: all 0.6s ease;
  z-index: -1;
}
.disney-button:hover::before {
  left: 100%;
}
.disney-button:hover {
  transform: translateY(-3px);
  box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
}
.button-icon {
  margin-right: 10px;
}
.return-btn {
  background: linear-gradient(45deg, #3498db, #2980b9);
  color: white;
}
.adjust-btn {
  background: linear-gradient(45deg, #9b59b6, #8e44ad);
  color: white;
}
/* 响应式设计 */
@media (max-width: 992px) {
  .book-details-container {
    flex-direction: column;
  }
  .book-cover-wrapper {
    margin-right: 0;
    margin-bottom: 1.5rem;
    text-align: center;
    width: 180px;
    margin: 0 auto 1.5rem;
  }
  .logs-title {
    font-size: 1.3rem;
  }
  .title-decoration {
    width: 50px;
  }
}
@media (max-width: 768px) {
  .disney-container {
    margin: 1rem auto;
  }
  .card-header-disney h4 {
    font-size: 1.5rem;
  }
  .card-body-disney {
    padding: 1.5rem 1rem;
  }
  .book-title {
    font-size: 1.7rem;
  }
  .disney-button {
    padding: 8px 15px;
    font-size: 0.9rem;
  }
}
@media (max-width: 576px) {
  .button-container {
    justify-content: center;
    gap: 1rem;
  }
  .book-title {
    font-size: 1.5rem;
  }
  .logs-title {
    font-size: 1.2rem;
  }
  .title-decoration {
    width: 30px;
  }
}
/* 飘落的雪花 */
.snowflake {
  position: fixed;
  top: -50px;
  animation: fall linear infinite;
  z-index: 0;
  pointer-events: none;
  color: rgba(255, 255, 255, 0.8);
  text-shadow: 0 0 5px rgba(162, 213, 242, 0.5);
}
@keyframes fall {
  to {
    transform: translateY(100vh) rotate(360deg);
  }
}
================================================================================
File: ./app/static/css/user-roles.css
================================================================================
/* 角色管理页面样式 */
.roles-container {
    padding: 20px;
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
/* 页面标题 */
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 25px;
    padding-bottom: 15px;
    border-bottom: 1px solid #f0f0f0;
}
.page-header h1 {
    font-size: 1.8rem;
    color: #333;
    margin: 0;
}
.page-header .actions {
    display: flex;
    gap: 10px;
}
/* 角色列表 */
.role-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 20px;
    margin-bottom: 30px;
}
/* 角色卡片 */
.role-card {
    background-color: #f9f9fb;
    border-radius: 8px;
    padding: 20px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.role-card:hover {
    transform: translateY(-5px);
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.role-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: 15px;
}
.role-name {
    font-size: 1.3rem;
    color: #333;
    margin: 0;
    font-weight: 600;
}
.role-actions {
    display: flex;
    gap: 5px;
}
.role-actions .btn {
    padding: 5px 8px;
    color: #6c757d;
    background: none;
    border: none;
}
.role-actions .btn:hover {
    color: #4c84ff;
}
.role-actions .btn-delete-role:hover {
    color: #dc3545;
}
.role-description {
    color: #555;
    margin-bottom: 20px;
    min-height: 50px;
    line-height: 1.5;
}
.role-stats {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 0.9rem;
    color: #6c757d;
}
.stat-item {
    display: flex;
    align-items: center;
    gap: 5px;
}
.stat-item i {
    color: #4c84ff;
}
/* 角色标签 */
.role-badge {
    display: inline-block;
    padding: 5px 12px;
    border-radius: 20px;
    font-size: 0.85rem;
    font-weight: 500;
}
.role-badge.admin {
    background-color: #e3f2fd;
    color: #1976d2;
}
.role-badge.user {
    background-color: #e8f5e9;
    color: #43a047;
}
.role-badge.custom {
    background-color: #fff3e0;
    color: #ef6c00;
}
/* 无数据提示 */
.no-data-message {
    grid-column: 1 / -1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 50px 0;
    color: #6c757d;
}
.no-data-message i {
    font-size: 3rem;
    margin-bottom: 15px;
    opacity: 0.5;
}
/* 权限信息部分 */
.permissions-info {
    margin-top: 30px;
}
.permissions-info h3 {
    font-size: 1.4rem;
    margin-bottom: 15px;
    color: #333;
}
.permission-table {
    width: 100%;
    margin-bottom: 0;
}
.permission-table th {
    background-color: #f8f9fa;
    font-weight: 600;
}
.permission-table td, .permission-table th {
    padding: 12px 15px;
    text-align: center;
}
.permission-table td:first-child {
    text-align: left;
    font-weight: 500;
}
.text-success {
    color: #28a745;
}
.text-danger {
    color: #dc3545;
}
/* 模态框样式 */
.modal-header {
    background-color: #f8f9fa;
    border-bottom: 1px solid #f0f0f0;
}
.modal-title {
    font-weight: 600;
    color: #333;
}
.modal-footer {
    border-top: 1px solid #f0f0f0;
    padding: 15px;
}
/* 表单样式 */
.form-group {
    margin-bottom: 20px;
}
.form-group label {
    font-weight: 500;
    margin-bottom: 8px;
    color: #333;
    display: block;
}
.form-control {
    height: auto;
    padding: 10px 15px;
    border: 1px solid #ddd;
    border-radius: 4px;
    font-size: 0.95rem;
    transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.form-control:focus {
    border-color: #4c84ff;
    box-shadow: 0 0 0 0.2rem rgba(76, 132, 255, 0.25);
}
/* 响应式调整 */
@media (max-width: 992px) {
    .role-list {
        grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    }
}
@media (max-width: 576px) {
    .role-list {
        grid-template-columns: 1fr;
    }
    .page-header {
        flex-direction: column;
        align-items: flex-start;
    }
    .page-header .actions {
        margin-top: 15px;
    }
}
/* 权限选择部分样式 */
.permissions-container {
    max-height: 350px;
    overflow-y: auto;
    border: 1px solid #e9ecef;
    border-radius: 4px;
    padding: 15px;
    background-color: #f8f9fa;
}
.permission-group {
    margin-bottom: 20px;
}
.permission-group:last-child {
    margin-bottom: 0;
}
.permission-group-title {
    font-weight: 600;
    color: #495057;
    margin-bottom: 10px;
    padding-bottom: 5px;
    border-bottom: 1px solid #dee2e6;
}
.permission-items {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 10px;
}
.permission-item {
    display: flex;
    align-items: center;
}
.permission-checkbox {
    margin-right: 8px;
}
.permission-name {
    font-weight: 500;
    margin-bottom: 5px;
    display: block;
}
.permission-description {
    font-size: 0.85rem;
    color: #6c757d;
    display: block;
}
.loading-permissions {
    grid-column: 1 / -1;
    text-align: center;
    padding: 20px;
    color: #6c757d;
}
/* 将模态框调整为大一点 */
.modal-lg {
    max-width: 800px;
}
/* 权限项样式 */
.permission-item {
    position: relative;
    padding: 8px 12px;
    border-radius: 4px;
    transition: background-color 0.2s;
}
.permission-item:hover {
    background-color: #e9ecef;
}
.permission-item label {
    display: flex;
    flex-direction: column;
    cursor: pointer;
    margin-bottom: 0;
    padding-left: 25px;
}
.permission-item input[type="checkbox"] {
    position: absolute;
    left: 12px;
    top: 12px;
}
================================================================================
File: ./app/static/css/overdue_analysis.css
================================================================================
/* app/static/css/overdue_analysis.css */
/* 保留您现有的 CSS 样式 */
.stats-cards .stats-card {
    border-left: 4px solid #007bff;
}
#current-overdue {
    border-left-color: #dc3545;
}
#current-overdue .card-value {
    color: #dc3545;
}
#returned-overdue {
    border-left-color: #ffc107;
}
#returned-overdue .card-value {
    color: #ffc107;
}
#overdue-rate {
    border-left-color: #28a745;
}
#overdue-rate .card-value {
    color: #28a745;
}
.chart-legend {
    display: flex;
    flex-wrap: wrap;
    margin-top: 15px;
    gap: 15px;
}
.legend-item {
    display: flex;
    align-items: center;
    font-size: 14px;
}
.legend-color {
    width: 15px;
    height: 15px;
    border-radius: 4px;
    margin-right: 5px;
}
/* 添加下面的 CSS 修复图表容器问题 */
.chart-container {
    position: relative;
    height: 400px; /* 固定高度 */
    overflow: hidden; /* 防止内容溢出 */
    margin-bottom: 30px;
}
.chart-container.half {
    min-height: 350px;
    max-height: 380px; /* 最大高度限制 */
}
.chart-container canvas {
    max-height: 100%;
    width: 100% !important;
    height: 320px !important; /* 确保固定高度 */
}
/* 修复图表行的问题 */
.chart-row {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    margin-bottom: 30px;
    align-items: stretch; /* 确保两个容器高度一致 */
}
.chart-row .half {
    flex: 1 1 calc(50% - 10px);
    min-width: 300px;
    display: flex;
    flex-direction: column;
}
/* 添加一个明确的底部间距,防止页面无限延伸 */
.statistics-container {
    padding-bottom: 50px;
}
/* 响应式调整 */
@media (max-width: 768px) {
    .chart-row {
        flex-direction: column;
    }
    .chart-container.half {
        width: 100%;
        margin-bottom: 20px;
    }
}
================================================================================
File: ./app/static/css/announcement-detail.css
================================================================================
.announcement-detail-container {
    padding: 20px;
    max-width: 900px;
    margin: 0 auto;
}
.page-header {
    margin-bottom: 25px;
    position: relative;
}
.back-link {
    display: inline-block;
    margin-bottom: 15px;
    color: #6c757d;
    text-decoration: none;
    transition: color 0.2s;
}
.back-link:hover {
    color: #007bff;
}
.page-header h1 {
    margin-top: 0;
    margin-bottom: 20px;
    font-size: 2rem;
    line-height: 1.3;
}
.announcement-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    margin-bottom: 25px;
    padding: 15px;
    background-color: #f8f9fa;
    border-radius: 8px;
}
.meta-item {
    display: flex;
    align-items: center;
    font-size: 0.95rem;
    color: #6c757d;
}
.meta-item i {
    margin-right: 8px;
}
.meta-item.pinned {
    color: #dc3545;
    font-weight: 500;
}
.announcement-content {
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0,0,0,0.05);
    padding: 25px;
    line-height: 1.7;
    color: #333;
}
/* 内容中的富文本样式 */
.announcement-content h1,
.announcement-content h2,
.announcement-content h3 {
    margin-top: 1.5em;
    margin-bottom: 0.8em;
}
.announcement-content p {
    margin-bottom: 1em;
}
.announcement-content img {
    max-width: 100%;
    height: auto;
    border-radius: 4px;
    margin: 15px 0;
}
.announcement-content ul,
.announcement-content ol {
    margin-bottom: 1em;
    padding-left: 2em;
}
.announcement-content a {
    color: #007bff;
}
.announcement-content blockquote {
    border-left: 4px solid #e3e3e3;
    padding-left: 15px;
    color: #6c757d;
    margin-left: 0;
    margin-right: 0;
}
================================================================================
File: ./app/static/js/book-import.js
================================================================================
// 图书批量导入页面的JavaScript功能
document.addEventListener('DOMContentLoaded', function() {
    // 显示选择的文件名
    const fileInput = document.getElementById('file');
    if (fileInput) {
        fileInput.addEventListener('change', function() {
            const fileName = this.value.split('\\').pop();
            const label = document.querySelector('.custom-file-label');
            if (label) {
                label.textContent = fileName || '点击这里选择文件...';
                // 添加有文件的类
                if (fileName) {
                    this.parentElement.classList.add('has-file');
                    // 显示文件类型检查和预览信息
                    checkFileAndPreview(this.files[0]);
                } else {
                    this.parentElement.classList.remove('has-file');
                }
            }
        });
    }
    // 监听表单提交
    const form = document.querySelector('form');
    if (form) {
        form.addEventListener('submit', function(e) {
            const fileInput = document.getElementById('file');
            if (!fileInput || !fileInput.files || !fileInput.files.length) {
                e.preventDefault();
                showMessage('请先选择要导入的Excel文件', 'warning');
                return;
            }
            const importBtn = document.querySelector('.import-btn');
            if (importBtn) {
                importBtn.innerHTML = ' 正在导入...';
                importBtn.disabled = true;
            }
            // 添加花朵飘落动画效果
            addFallingElements(10);
            // 设置超时处理,如果30秒后还没响应,提示用户
            window.importTimeout = setTimeout(function() {
                showMessage('导入处理时间较长,请耐心等待...', 'info');
            }, 30000);
        });
    }
    // 美化表单提交按钮的点击效果
    const importBtn = document.querySelector('.import-btn');
    if (importBtn) {
        importBtn.addEventListener('click', function(e) {
            // 按钮的点击效果已由表单提交事件处理
            // 避免重复处理
            if (!form || form.reportValidity() === false) {
                e.preventDefault();
            }
        });
    }
    // 为浮动元素添加动画
    initFloatingElements();
    // 检查页面中的flash消息
    checkFlashMessages();
});
// 检查页面中的flash消息
function checkFlashMessages() {
    // Flask的flash消息通常会渲染为带有特定类的元素
    const flashMessages = document.querySelectorAll('.alert');
    if (flashMessages && flashMessages.length > 0) {
        // 如果存在flash消息,说明页面是提交后重新加载的
        // 恢复按钮状态
        const importBtn = document.querySelector('.import-btn');
        if (importBtn) {
            importBtn.innerHTML = ' 开始导入';
            importBtn.disabled = false;
        }
        // 清除可能的超时
        if (window.importTimeout) {
            clearTimeout(window.importTimeout);
        }
    }
}
// 检查文件类型并尝试预览
function checkFileAndPreview(file) {
    if (!file) return;
    // 检查文件类型
    const validTypes = ['.xlsx', '.xls', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-excel'];
    let isValid = false;
    validTypes.forEach(type => {
        if (file.name.toLowerCase().endsWith(type) || file.type === type) {
            isValid = true;
        }
    });
    if (!isValid) {
        showMessage('请选择有效的Excel文件 (.xlsx 或 .xls)', 'warning');
        return;
    }
    // 显示文件准备就绪的消息
    showMessage(`文件 "${file.name}" 已准备就绪,点击"开始导入"按钮继续。`, 'success');
}
// 显示提示消息
function showMessage(message, type = 'info') {
    // 检查是否已有消息容器
    let messageContainer = document.querySelector('.import-message');
    if (!messageContainer) {
        // 创建新的消息容器
        messageContainer = document.createElement('div');
        messageContainer.className = 'import-message animate__animated animate__fadeIn';
        // 插入到按钮之后
        const importBtn = document.querySelector('.import-btn');
        if (importBtn && importBtn.parentNode) {
            importBtn.parentNode.insertBefore(messageContainer, importBtn.nextSibling);
        }
    }
    // 设置消息内容和样式
    messageContainer.innerHTML = `
        
             ${message}
        
    `;
    // 如果是临时消息,设置自动消失
    if (type !== 'danger') {
        setTimeout(() => {
            messageContainer.classList.add('animate__fadeOut');
            setTimeout(() => {
                if (messageContainer.parentNode) {
                    messageContainer.parentNode.removeChild(messageContainer);
                }
            }, 600);
        }, 5000);
    }
}
// 根据消息类型获取图标
function getIconForMessageType(type) {
    switch (type) {
        case 'success': return 'fa-check-circle';
        case 'warning': return 'fa-exclamation-triangle';
        case 'danger': return 'fa-times-circle';
        default: return 'fa-info-circle';
    }
}
// 初始化浮动元素
function initFloatingElements() {
    const floatingElements = document.querySelectorAll('.snowflake, .flower');
    floatingElements.forEach(element => {
        const randomDuration = 15 + Math.random() * 20;
        const randomDelay = Math.random() * 10;
        element.style.animationDuration = `${randomDuration}s`;
        element.style.animationDelay = `${randomDelay}s`;
    });
}
// 添加花朵飘落效果
function addFallingElements(count) {
    const container = document.querySelector('.import-container');
    if (!container) return;
    for (let i = 0; i < count; i++) {
        const element = document.createElement('div');
        element.className = 'falling-element animate__animated animate__fadeInDown';
        // 随机选择花朵或雪花
        const isFlower = Math.random() > 0.5;
        element.classList.add(isFlower ? 'falling-flower' : 'falling-snowflake');
        // 随机位置
        const left = Math.random() * 100;
        element.style.left = `${left}%`;
        // 随机延迟
        const delay = Math.random() * 2;
        element.style.animationDelay = `${delay}s`;
        // 随机大小
        const size = 10 + Math.random() * 20;
        element.style.width = `${size}px`;
        element.style.height = `${size}px`;
        container.appendChild(element);
        // 动画结束后移除元素
        setTimeout(() => {
            if (element.parentNode) {
                element.parentNode.removeChild(element);
            }
        }, 5000);
    }
}
================================================================================
File: ./app/static/js/log-list.js
================================================================================
document.addEventListener('DOMContentLoaded', function() {
    // 日期范围选择器逻辑
    const dateRangeSelect = document.getElementById('date_range');
    const dateRangeInputs = document.querySelector('.date-range-inputs');
    if (dateRangeSelect && dateRangeInputs) {
        dateRangeSelect.addEventListener('change', function() {
            if (this.value === 'custom') {
                dateRangeInputs.style.display = 'flex';
            } else {
                dateRangeInputs.style.display = 'none';
            }
        });
    }
    // 导出日志功能
    const btnExport = document.getElementById('btnExport');
    const exportModal = new bootstrap.Modal(document.getElementById('exportLogModal'));
    const confirmExport = document.getElementById('confirmExport');
    if (btnExport) {
        btnExport.addEventListener('click', function() {
            exportModal.show();
        });
    }
    if (confirmExport) {
        confirmExport.addEventListener('click', function() {
            // 获取导出格式
            const exportFormat = document.getElementById('exportFormat').value;
            // 获取当前筛选条件
            const userId = document.getElementById('user_id').value;
            const action = document.getElementById('action').value;
            const targetType = document.getElementById('target_type').value;
            let startDate = '';
            let endDate = '';
            const dateRange = document.getElementById('date_range').value;
            if (dateRange === 'custom') {
                startDate = document.getElementById('start_date').value;
                endDate = document.getElementById('end_date').value;
            } else {
                // 根据选择的日期范围计算日期
                const today = new Date();
                endDate = formatDate(today);
                if (dateRange === '1') {
                    const yesterday = new Date(today);
                    yesterday.setDate(yesterday.getDate() - 1);
                    startDate = formatDate(yesterday);
                } else if (dateRange === '7') {
                    const lastWeek = new Date(today);
                    lastWeek.setDate(lastWeek.getDate() - 7);
                    startDate = formatDate(lastWeek);
                } else if (dateRange === '30') {
                    const lastMonth = new Date(today);
                    lastMonth.setDate(lastMonth.getDate() - 30);
                    startDate = formatDate(lastMonth);
                }
            }
            // 显示加载提示
            showAlert('info', '正在生成导出文件,请稍候...');
            // 发送导出请求
            fetch('/log/api/export', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    user_id: userId || null,
                    action: action || null,
                    target_type: targetType || null,
                    start_date: startDate || null,
                    end_date: endDate || null,
                    format: exportFormat
                })
            })
            .then(response => response.json())
            .then(data => {
                exportModal.hide();
                if (data.success) {
                    showAlert('success', data.message);
                    // 处理文件下载
                    if (data.filedata && data.filename) {
                        // 解码Base64数据
                        const binaryData = atob(data.filedata);
                        // 转换为Blob
                        const blob = new Blob([new Uint8Array([...binaryData].map(char => char.charCodeAt(0)))],
                            { type: data.filetype });
                        // 创建下载链接
                        const url = window.URL.createObjectURL(blob);
                        const a = document.createElement('a');
                        a.style.display = 'none';
                        a.href = url;
                        a.download = data.filename;
                        // 触发下载
                        document.body.appendChild(a);
                        a.click();
                        // 清理
                        window.URL.revokeObjectURL(url);
                        document.body.removeChild(a);
                    }
                } else {
                    showAlert('danger', data.message || '导出失败');
                }
            })
            .catch(error => {
                exportModal.hide();
                showAlert('danger', '导出失败: ' + error.message);
            });
        });
    }
    // 清除日志功能
    const btnClear = document.getElementById('btnClear');
    const clearModal = new bootstrap.Modal(document.getElementById('clearLogModal'));
    const confirmClear = document.getElementById('confirmClear');
    if (btnClear) {
        btnClear.addEventListener('click', function() {
            clearModal.show();
        });
    }
    if (confirmClear) {
        confirmClear.addEventListener('click', function() {
            const days = parseInt(document.getElementById('clearDays').value);
            fetch('/log/api/clear', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ days: days })
            })
            .then(response => response.json())
            .then(data => {
                clearModal.hide();
                if (data.success) {
                    showAlert('success', data.message);
                    // 2秒后刷新页面
                    setTimeout(() => {
                        window.location.reload();
                    }, 2000);
                } else {
                    showAlert('danger', data.message);
                }
            })
            .catch(error => {
                clearModal.hide();
                showAlert('danger', '操作失败: ' + error.message);
            });
        });
    }
    // 辅助函数 - 格式化日期为 YYYY-MM-DD
    function formatDate(date) {
        const year = date.getFullYear();
        const month = String(date.getMonth() + 1).padStart(2, '0');
        const day = String(date.getDate()).padStart(2, '0');
        return `${year}-${month}-${day}`;
    }
    // 辅助函数 - 显示提示框
    function showAlert(type, message) {
        // 移除之前的所有alert
        const existingAlerts = document.querySelectorAll('.alert-floating');
        existingAlerts.forEach(alert => alert.remove());
        const alertDiv = document.createElement('div');
        alertDiv.className = `alert alert-${type} alert-dismissible fade show alert-floating`;
        alertDiv.innerHTML = `
            
            ${message}
            
        `;
        document.body.appendChild(alertDiv);
        // 添加CSS,如果还没有添加
        if (!document.getElementById('alert-floating-style')) {
            const style = document.createElement('style');
            style.id = 'alert-floating-style';
            style.textContent = `
                .alert-floating {
                    position: fixed;
                    top: 20px;
                    right: 20px;
                    z-index: 9999;
                    min-width: 300px;
                    box-shadow: 0 4px 8px rgba(0,0,0,0.1);
                    border-left: 4px solid;
                    animation: slideIn 0.3s ease-out forwards;
                }
                @keyframes slideIn {
                    from { transform: translateX(100%); opacity: 0; }
                    to { transform: translateX(0); opacity: 1; }
                }
                .alert-floating i {
                    margin-right: 8px;
                }
                .alert-floating .close {
                    padding: 0.75rem;
                }
            `;
            document.head.appendChild(style);
        }
        // 5秒后自动关闭
        setTimeout(() => {
            if (alertDiv.parentNode) {
                alertDiv.classList.add('fade');
                setTimeout(() => alertDiv.remove(), 300);
            }
        }, 5000);
        // 点击关闭按钮关闭
        const closeButton = alertDiv.querySelector('.close');
        if (closeButton) {
            closeButton.addEventListener('click', function() {
                alertDiv.classList.add('fade');
                setTimeout(() => alertDiv.remove(), 300);
            });
        }
    }
});
================================================================================
File: ./app/static/js/my_borrows.js
================================================================================
// my_borrows.js
document.addEventListener('DOMContentLoaded', function() {
    // 归还图书功能
    const returnButtons = document.querySelectorAll('.return-btn');
    const returnModal = document.getElementById('returnModal');
    const returnBookTitle = document.getElementById('returnBookTitle');
    const confirmReturnButton = document.getElementById('confirmReturn');
    let currentBorrowId = null;
    returnButtons.forEach(button => {
        button.addEventListener('click', function() {
            const borrowId = this.getAttribute('data-id');
            const bookTitle = this.getAttribute('data-title');
            currentBorrowId = borrowId;
            returnBookTitle.textContent = bookTitle;
            // 使用 Bootstrap 的 jQuery 方法显示模态框
            $('#returnModal').modal('show');
        });
    });
    confirmReturnButton.addEventListener('click', function() {
        if (!currentBorrowId) return;
        // 发送归还请求
        fetch(`/borrow/return/${currentBorrowId}`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-Requested-With': 'XMLHttpRequest'
            },
            body: JSON.stringify({})
        })
        .then(response => response.json())
        .then(data => {
            // 隐藏模态框
            $('#returnModal').modal('hide');
            if (data.success) {
                // 显示成功消息
                showAlert('success', data.message);
                // 重新加载页面以更新借阅状态
                setTimeout(() => window.location.reload(), 1500);
            } else {
                // 显示错误消息
                showAlert('danger', data.message);
            }
        })
        .catch(error => {
            $('#returnModal').modal('hide');
            showAlert('danger', '操作失败,请稍后重试');
            console.error('Error:', error);
        });
    });
    // 续借图书功能
    const renewButtons = document.querySelectorAll('.renew-btn');
    const renewModal = document.getElementById('renewModal');
    const renewBookTitle = document.getElementById('renewBookTitle');
    const confirmRenewButton = document.getElementById('confirmRenew');
    renewButtons.forEach(button => {
        button.addEventListener('click', function() {
            const borrowId = this.getAttribute('data-id');
            const bookTitle = this.getAttribute('data-title');
            currentBorrowId = borrowId;
            renewBookTitle.textContent = bookTitle;
            // 使用 Bootstrap 的 jQuery 方法显示模态框
            $('#renewModal').modal('show');
        });
    });
    confirmRenewButton.addEventListener('click', function() {
        if (!currentBorrowId) return;
        // 发送续借请求
        fetch(`/borrow/renew/${currentBorrowId}`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-Requested-With': 'XMLHttpRequest'
            },
            body: JSON.stringify({})
        })
        .then(response => response.json())
        .then(data => {
            // 隐藏模态框
            $('#renewModal').modal('hide');
            if (data.success) {
                // 显示成功消息
                showAlert('success', data.message);
                // 重新加载页面以更新借阅状态
                setTimeout(() => window.location.reload(), 1500);
            } else {
                // 显示错误消息
                showAlert('danger', data.message);
            }
        })
        .catch(error => {
            $('#renewModal').modal('hide');
            showAlert('danger', '操作失败,请稍后重试');
            console.error('Error:', error);
        });
    });
    // 显示提示消息
    function showAlert(type, message) {
        const alertDiv = document.createElement('div');
        alertDiv.className = `alert alert-${type} alert-dismissible fade show fixed-top mx-auto mt-3`;
        alertDiv.style.maxWidth = '500px';
        alertDiv.style.zIndex = '9999';
        alertDiv.innerHTML = `
            ${message}
            
        `;
        document.body.appendChild(alertDiv);
        // 3秒后自动消失
        setTimeout(() => {
            alertDiv.remove();
        }, 3000);
    }
});
================================================================================
File: ./app/static/js/announcement-form.js
================================================================================
// 公告编辑表单的Javascript
document.addEventListener('DOMContentLoaded', function() {
    // 表单提交前验证
    document.getElementById('announcementForm').addEventListener('submit', function(e) {
        // 由于富文本内容在各页面单独处理,这里仅做一些通用表单验证
        const title = document.getElementById('title').value.trim();
        if (!title) {
            e.preventDefault();
            alert('请输入公告标题');
            return false;
        }
    });
    // 返回按钮处理
    const cancelButton = document.querySelector('button[type="button"]');
    if (cancelButton) {
        cancelButton.addEventListener('click', function() {
            // 如果有未保存内容,给出提示
            if (formHasChanges()) {
                if (!confirm('表单有未保存的内容,确定要离开吗?')) {
                    return;
                }
            }
            history.back();
        });
    }
    // 检测表单是否有变化
    function formHasChanges() {
        // 这里可以添加逻辑来检测表单内容是否有变化
        // 简单实现:检查标题是否不为空
        const title = document.getElementById('title').value.trim();
        return title !== '';
    }
});
================================================================================
File: ./app/static/js/user-edit.js
================================================================================
// 用户编辑页面交互
document.addEventListener('DOMContentLoaded', function() {
    const passwordField = document.getElementById('password');
    const confirmPasswordGroup = document.getElementById('confirmPasswordGroup');
    const confirmPasswordField = document.getElementById('confirm_password');
    const userEditForm = document.getElementById('userEditForm');
    // 如果输入密码,显示确认密码字段
    passwordField.addEventListener('input', function() {
        if (this.value.trim() !== '') {
            confirmPasswordGroup.style.display = 'block';
        } else {
            confirmPasswordGroup.style.display = 'none';
            confirmPasswordField.value = '';
        }
    });
    // 表单提交验证
    userEditForm.addEventListener('submit', function(event) {
        let valid = true;
        // 清除之前的错误提示
        const invalidFields = document.querySelectorAll('.is-invalid');
        const feedbackElements = document.querySelectorAll('.invalid-feedback');
        invalidFields.forEach(field => {
            field.classList.remove('is-invalid');
        });
        feedbackElements.forEach(element => {
            element.parentNode.removeChild(element);
        });
        // 邮箱格式验证
        const emailField = document.getElementById('email');
        if (emailField.value.trim() !== '') {
            const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
            if (!emailPattern.test(emailField.value.trim())) {
                showError(emailField, '请输入有效的邮箱地址');
                valid = false;
            }
        }
        // 手机号码格式验证
        const phoneField = document.getElementById('phone');
        if (phoneField.value.trim() !== '') {
            const phonePattern = /^1[3456789]\d{9}$/;
            if (!phonePattern.test(phoneField.value.trim())) {
                showError(phoneField, '请输入有效的手机号码');
                valid = false;
            }
        }
        // 密码验证
        if (passwordField.value.trim() !== '') {
            if (passwordField.value.length < 6) {
                showError(passwordField, '密码长度至少为6个字符');
                valid = false;
            }
            if (passwordField.value !== confirmPasswordField.value) {
                showError(confirmPasswordField, '两次输入的密码不一致');
                valid = false;
            }
        }
        if (!valid) {
            event.preventDefault();
        }
    });
    // 显示表单字段错误
    function showError(field, message) {
        field.classList.add('is-invalid');
        const feedback = document.createElement('div');
        feedback.className = 'invalid-feedback';
        feedback.innerText = message;
        field.parentNode.appendChild(feedback);
    }
    // 处理表单提交后的成功反馈
    const successAlert = document.querySelector('.alert-success');
    if (successAlert) {
        // 如果有成功消息,显示成功对话框
        setTimeout(() => {
            $('#successModal').modal('show');
        }, 500);
    }
});
================================================================================
File: ./app/static/js/book_ranking.js
================================================================================
// app/static/js/book_ranking.js
document.addEventListener('DOMContentLoaded', function() {
    const timeRangeSelect = document.getElementById('time-range');
    const limitSelect = document.getElementById('limit-count');
    let rankingChart = null;
    // 初始加载
    loadRankingData();
    // 添加事件监听器
    timeRangeSelect.addEventListener('change', loadRankingData);
    limitSelect.addEventListener('change', loadRankingData);
    function loadRankingData() {
        const timeRange = timeRangeSelect.value;
        const limit = limitSelect.value;
        // 显示加载状态
        document.getElementById('ranking-table-body').innerHTML = `
            
                正在打开书页...  | 
            
        `;
        // 调用API获取数据
        fetch(`/statistics/api/book-ranking?time_range=${timeRange}&limit=${limit}`)
            .then(response => response.json())
            .then(data => {
                // 更新表格
                updateRankingTable(data);
                // 更新图表
                updateRankingChart(data);
            })
            .catch(error => {
                console.error('加载排行数据失败:', error);
                document.getElementById('ranking-table-body').innerHTML = `
                    
                        | 加载数据失败,请稍后重试 | 
                    
                `;
            });
    }
    function updateRankingTable(data) {
        const tableBody = document.getElementById('ranking-table-body');
        if (data.length === 0) {
            tableBody.innerHTML = `
                
                    | 暂无数据 | 
                
            `;
            return;
        }
        let tableHtml = '';
        data.forEach((book, index) => {
            // 给每个单元格添加适当的类名以匹配CSS
            tableHtml += `
                
                    | ${index + 1} | 
                    
                         
                     | 
                    ${book.title} | 
                    ${book.author} | 
                    ${book.borrow_count} | 
                
            `;
        });
        tableBody.innerHTML = tableHtml;
    }
    function updateRankingChart(data) {
        // 销毁旧图表
        if (rankingChart) {
            rankingChart.destroy();
        }
        if (data.length === 0) {
            return;
        }
        // 准备图表数据
        const labels = data.map(book => book.title);
        const borrowCounts = data.map(book => book.borrow_count);
        // 创建图表
        const ctx = document.getElementById('ranking-chart').getContext('2d');
        rankingChart = new Chart(ctx, {
            type: 'bar',
            data: {
                labels: labels,
                datasets: [{
                    label: '借阅次数',
                    data: borrowCounts,
                    backgroundColor: 'rgba(183, 110, 121, 0.6)', // 玫瑰金色调
                    borderColor: 'rgba(140, 45, 90, 1)', // 浆果红
                    borderWidth: 1
                }]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                scales: {
                    y: {
                        beginAtZero: true,
                        title: {
                            display: true,
                            text: '借阅次数',
                            font: {
                                family: "'Open Sans', sans-serif",
                                size: 13
                            },
                            color: '#5D5053'
                        },
                        ticks: {
                            color: '#8A797C',
                            font: {
                                family: "'Open Sans', sans-serif"
                            }
                        },
                        grid: {
                            color: 'rgba(211, 211, 211, 0.3)'
                        }
                    },
                    x: {
                        title: {
                            display: true,
                            text: '图书',
                            font: {
                                family: "'Open Sans', sans-serif",
                                size: 13
                            },
                            color: '#5D5053'
                        },
                        ticks: {
                            color: '#8A797C',
                            font: {
                                family: "'Open Sans', sans-serif"
                            }
                        },
                        grid: {
                            display: false
                        }
                    }
                },
                plugins: {
                    legend: {
                        display: false
                    },
                    title: {
                        display: true,
                        text: '热门图书借阅排行',
                        font: {
                            family: "'Playfair Display', serif",
                            size: 16,
                            weight: 'bold'
                        },
                        color: '#B76E79'
                    }
                }
            }
        });
    }
});
================================================================================
File: ./app/static/js/user-roles.js
================================================================================
// 角色管理页面交互
document.addEventListener('DOMContentLoaded', function() {
    // 获取DOM元素
    const addRoleBtn = document.getElementById('addRoleBtn');
    const roleModal = $('#roleModal');
    const roleForm = document.getElementById('roleForm');
    const roleIdInput = document.getElementById('roleId');
    const roleNameInput = document.getElementById('roleName');
    const roleDescriptionInput = document.getElementById('roleDescription');
    const saveRoleBtn = document.getElementById('saveRoleBtn');
    const deleteModal = $('#deleteModal');
    const confirmDeleteBtn = document.getElementById('confirmDeleteBtn');
    let roleIdToDelete = null;
    // 加载角色用户统计
    fetchRoleUserCounts();
    // 初始化时加载权限列表
    loadPermissions();
    // 添加角色按钮点击事件
    if (addRoleBtn) {
        addRoleBtn.addEventListener('click', function() {
            // 重置表单
            roleIdInput.value = '';
            roleNameInput.value = '';
            roleDescriptionInput.value = '';
            // 更新模态框标题
            document.getElementById('roleModalLabel').textContent = '添加角色';
            // 启用所有权限复选框
            document.querySelectorAll('.permission-checkbox').forEach(checkbox => {
                checkbox.checked = false;
                checkbox.disabled = false;
            });
            // 隐藏系统角色警告
            const systemRoleAlert = document.getElementById('systemRoleAlert');
            if (systemRoleAlert) {
                systemRoleAlert.style.display = 'none';
            }
            // 显示模态框
            roleModal.modal('show');
        });
    }
    // 编辑角色按钮点击事件
    const editButtons = document.querySelectorAll('.btn-edit-role');
    editButtons.forEach(button => {
        button.addEventListener('click', function() {
            const roleCard = this.closest('.role-card');
            const roleId = roleCard.getAttribute('data-id');
            const roleName = roleCard.querySelector('.role-name').textContent;
            let roleDescription = roleCard.querySelector('.role-description').textContent;
            // 移除"暂无描述"文本
            if (roleDescription.trim() === '暂无描述') {
                roleDescription = '';
            }
            // 填充表单
            roleIdInput.value = roleId;
            roleNameInput.value = roleName;
            roleDescriptionInput.value = roleDescription.trim();
            // 更新模态框标题
            document.getElementById('roleModalLabel').textContent = '编辑角色';
            // 加载角色权限
            loadRolePermissions(roleId);
            // 显示模态框
            roleModal.modal('show');
        });
    });
    // 删除角色按钮点击事件
    const deleteButtons = document.querySelectorAll('.btn-delete-role');
    deleteButtons.forEach(button => {
        button.addEventListener('click', function() {
            const roleCard = this.closest('.role-card');
            roleIdToDelete = roleCard.getAttribute('data-id');
            // 显示确认删除模态框
            deleteModal.modal('show');
        });
    });
    // 保存角色按钮点击事件
    if (saveRoleBtn) {
        saveRoleBtn.addEventListener('click', function() {
            if (!roleNameInput.value.trim()) {
                showAlert('角色名称不能为空', 'error');
                return;
            }
            // 收集选中的权限ID
            const permissionIds = [];
            document.querySelectorAll('.permission-checkbox:checked:not(:disabled)').forEach(checkbox => {
                permissionIds.push(parseInt(checkbox.value));
            });
            const roleData = {
                id: roleIdInput.value || null,
                role_name: roleNameInput.value.trim(),
                description: roleDescriptionInput.value.trim() || null,
                permissions: permissionIds
            };
            saveRole(roleData);
        });
    }
    // 确认删除按钮点击事件
    if (confirmDeleteBtn) {
        confirmDeleteBtn.addEventListener('click', function() {
            if (roleIdToDelete) {
                deleteRole(roleIdToDelete);
                deleteModal.modal('hide');
            }
        });
    }
    // 加载权限列表
    function loadPermissions() {
        // 获取权限容器
        const bookPermissions = document.getElementById('book-permissions');
        const userPermissions = document.getElementById('user-permissions');
        const borrowPermissions = document.getElementById('borrow-permissions');
        const systemPermissions = document.getElementById('system-permissions');
        if (!bookPermissions) return; // 如果元素不存在就退出
        // 设置加载中状态
        bookPermissions.innerHTML = ' 加载权限中...
';
        userPermissions.innerHTML = '';
        borrowPermissions.innerHTML = '';
        systemPermissions.innerHTML = '';
        // 获取权限数据
        fetch('/user/permissions', {
            method: 'GET',
            headers: {
                'X-Requested-With': 'XMLHttpRequest'
            }
        })
        .then(response => {
            if (!response.ok) {
                throw new Error('网络响应异常');
            }
            return response.json();
        })
        .then(data => {
            if (data.success) {
                // 清除加载中状态
                bookPermissions.innerHTML = '';
                // 按分组整理权限
                const permissionGroups = {
                    book: [],   // 图书相关权限
                    user: [],   // 用户相关权限
                    borrow: [], // 借阅相关权限
                    system: []  // 系统相关权限
                };
                // 定义权限分组映射
                const permGroupMap = {
                    'manage_books': 'book',
                    'manage_categories': 'book',
                    'import_export_books': 'book',
                    'manage_users': 'user',
                    'manage_roles': 'user',
                    'manage_borrows': 'borrow',
                    'manage_overdue': 'borrow',
                    // 系统相关权限
                    'manage_announcements': 'system',
                    'manage_inventory': 'system',
                    'view_logs': 'system',
                    'view_statistics': 'system'
                };
                // 根据映射表分组
                data.permissions.forEach(perm => {
                    const group = permGroupMap[perm.code] || 'system';
                    permissionGroups[group].push(perm);
                });
                // 渲染各组权限
                renderPermissionGroup(bookPermissions, permissionGroups.book);
                renderPermissionGroup(userPermissions, permissionGroups.user);
                renderPermissionGroup(borrowPermissions, permissionGroups.borrow);
                renderPermissionGroup(systemPermissions, permissionGroups.system);
            } else {
                bookPermissions.innerHTML = '加载权限失败
';
            }
        })
        .catch(error => {
            console.error('Error:', error);
            bookPermissions.innerHTML = '加载权限失败,请刷新页面重试
';
        });
    }
    // 渲染权限组
    function renderPermissionGroup(container, permissions) {
        if (permissions.length === 0) {
            container.innerHTML = '暂无相关权限
';
            return;
        }
        let html = '';
        permissions.forEach(perm => {
            html += `
                
                    
                    
                
            `;
        });
        container.innerHTML = html;
    }
    // 加载角色的权限
    function loadRolePermissions(roleId) {
        if (!roleId) return;
        // 先清空所有已选权限
        document.querySelectorAll('.permission-checkbox').forEach(checkbox => {
            checkbox.checked = false;
        });
        // 如果是系统内置角色,显示警告并禁用权限选择
        const isSystemRole = (roleId == 1 || roleId == 2);
        const systemRoleAlert = document.getElementById('systemRoleAlert');
        const permissionCheckboxes = document.querySelectorAll('.permission-checkbox');
        if (systemRoleAlert) {
            systemRoleAlert.style.display = isSystemRole ? 'block' : 'none';
        }
        // 管理员角色自动选中所有权限并禁用
        if (roleId == 1) { // 管理员
            permissionCheckboxes.forEach(checkbox => {
                checkbox.checked = true;
                checkbox.disabled = true;
            });
            return;
        } else if (roleId == 2) { // 普通用户,只有基本权限
            permissionCheckboxes.forEach(checkbox => {
                checkbox.checked = false;
                checkbox.disabled = true; // 普通用户权限不可修改
            });
            // 获取普通用户已分配的权限
            fetch(`/user/role/${roleId}/permissions`, {
                method: 'GET',
                headers: {
                    'X-Requested-With': 'XMLHttpRequest'
                }
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    permissionCheckboxes.forEach(checkbox => {
                        // 如果权限ID在返回的列表中,则选中
                        checkbox.checked = data.permissions.includes(parseInt(checkbox.value));
                    });
                }
            });
            return;
        }
        // 为自定义角色加载并选中权限
        fetch(`/user/role/${roleId}/permissions`, {
            method: 'GET',
            headers: {
                'X-Requested-With': 'XMLHttpRequest'
            }
        })
        .then(response => response.json())
        .then(data => {
            if (data.success) {
                permissionCheckboxes.forEach(checkbox => {
                    // 启用所有复选框
                    checkbox.disabled = false;
                    // 如果权限ID在返回的列表中,则选中
                    checkbox.checked = data.permissions.includes(parseInt(checkbox.value));
                });
            }
        });
    }
    // 保存角色
    function saveRole(roleData) {
        // 显示加载状态
        saveRoleBtn.innerHTML = ' 保存中...';
        saveRoleBtn.disabled = true;
        fetch('/user/role/save', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-Requested-With': 'XMLHttpRequest'
            },
            body: JSON.stringify(roleData)
        })
        .then(response => {
            if (!response.ok) {
                throw new Error('网络响应异常');
            }
            return response.json();
        })
        .then(data => {
            // 恢复按钮状态
            saveRoleBtn.innerHTML = '保存';
            saveRoleBtn.disabled = false;
            if (data.success) {
                // 关闭模态框
                roleModal.modal('hide');
                showAlert(data.message, 'success');
                setTimeout(() => {
                    window.location.reload();
                }, 1500);
            } else {
                showAlert(data.message, 'error');
            }
        })
        .catch(error => {
            console.error('Error:', error);
            // 恢复按钮状态
            saveRoleBtn.innerHTML = '保存';
            saveRoleBtn.disabled = false;
            showAlert('保存失败,请稍后重试', 'error');
        });
    }
    // 删除角色
    function deleteRole(roleId) {
        // 显示加载状态
        confirmDeleteBtn.innerHTML = ' 删除中...';
        confirmDeleteBtn.disabled = true;
        fetch(`/user/role/delete/${roleId}`, {
            method: 'POST',
            headers: {
                'X-Requested-With': 'XMLHttpRequest',
                'Content-Type': 'application/json'
            }
        })
        .then(response => {
            if (!response.ok) {
                throw new Error('网络响应异常');
            }
            return response.json();
        })
        .then(data => {
            // 恢复按钮状态
            confirmDeleteBtn.innerHTML = '确认删除';
            confirmDeleteBtn.disabled = false;
            if (data.success) {
                showAlert(data.message, 'success');
                setTimeout(() => {
                    window.location.reload();
                }, 1500);
            } else {
                showAlert(data.message, 'error');
            }
        })
        .catch(error => {
            console.error('Error:', error);
            // 恢复按钮状态
            confirmDeleteBtn.innerHTML = '确认删除';
            confirmDeleteBtn.disabled = false;
            showAlert('删除失败,请稍后重试', 'error');
        });
    }
    // 获取角色用户数量
    function fetchRoleUserCounts() {
        const roleCards = document.querySelectorAll('.role-card');
        roleCards.forEach(card => {
            const roleId = card.getAttribute('data-id');
            const countElement = document.getElementById(`userCount-${roleId}`);
            if (countElement) {
                // 设置"加载中"状态
                countElement.innerHTML = '加载中...';
                // 定义默认的角色用户数量 (用于API不可用时)
                const defaultCounts = {
                    '1': 1, // 管理员
                    '2': 5, // 普通用户
                };
                // 尝试获取用户数量
                fetch(`/user/role/${roleId}/count`)
                    .then(response => {
                        if (!response.ok) {
                            throw new Error('API不可用');
                        }
                        return response.json();
                    })
                    .then(data => {
                        // 检查返回数据的success属性
                        if (data.success) {
                            countElement.textContent = data.count;
                        } else {
                            throw new Error(data.message || 'API返回错误');
                        }
                    })
                    .catch(error => {
                        console.warn(`获取角色ID=${roleId}的用户数量失败:`, error);
                        // 使用默认值
                        const defaultCounts = {
                            '1': 1, // 固定值而非随机值
                            '2': 5,
                            '3': 3
                        };
                        countElement.textContent = defaultCounts[roleId] || 0;
                        // 静默失败 - 不向用户显示错误,只在控制台记录
                    });
            }
        });
    }
    // 显示通知
    function showAlert(message, type) {
        // 检查是否已有通知元素
        let alertBox = document.querySelector('.alert-box');
        if (!alertBox) {
            alertBox = document.createElement('div');
            alertBox.className = 'alert-box';
            document.body.appendChild(alertBox);
        }
        // 创建新的通知
        const alert = document.createElement('div');
        alert.className = `alert alert-${type === 'success' ? 'success' : 'danger'} fade-in`;
        alert.innerHTML = message;
        // 添加到通知框中
        alertBox.appendChild(alert);
        // 自动关闭
        setTimeout(() => {
            alert.classList.add('fade-out');
            setTimeout(() => {
                alertBox.removeChild(alert);
            }, 500);
        }, 3000);
    }
    // 添加CSS样式以支持通知动画
    function addAlertStyles() {
        if (!document.getElementById('alert-styles')) {
            const style = document.createElement('style');
            style.id = 'alert-styles';
            style.textContent = `
                .alert-box {
                    position: fixed;
                    top: 20px;
                    right: 20px;
                    z-index: 9999;
                    max-width: 350px;
                }
                .alert {
                    margin-bottom: 10px;
                    padding: 15px;
                    border-radius: 4px;
                    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
                    opacity: 0;
                    transition: opacity 0.3s ease;
                }
                .fade-in {
                    opacity: 1;
                }
                .fade-out {
                    opacity: 0;
                }
            `;
            document.head.appendChild(style);
        }
    }
    // 添加通知样式
    addAlertStyles();
});
================================================================================
File: ./app/static/js/user-add.js
================================================================================
document.addEventListener('DOMContentLoaded', function() {
    // 密码显示/隐藏切换
    const togglePasswordButtons = document.querySelectorAll('.toggle-password');
    togglePasswordButtons.forEach(button => {
        button.addEventListener('click', function() {
            const passwordField = this.previousElementSibling;
            const type = passwordField.getAttribute('type') === 'password' ? 'text' : 'password';
            passwordField.setAttribute('type', type);
            // 更改图标
            const icon = this.querySelector('i');
            if (type === 'text') {
                icon.classList.remove('fa-eye');
                icon.classList.add('fa-eye-slash');
            } else {
                icon.classList.remove('fa-eye-slash');
                icon.classList.add('fa-eye');
            }
        });
    });
    // 密码一致性检查
    const passwordInput = document.getElementById('password');
    const confirmPasswordInput = document.getElementById('confirm_password');
    const passwordMatchMessage = document.getElementById('password-match-message');
    function checkPasswordMatch() {
        if (confirmPasswordInput.value === '') {
            passwordMatchMessage.textContent = '';
            passwordMatchMessage.className = 'form-text';
            return;
        }
        if (passwordInput.value === confirmPasswordInput.value) {
            passwordMatchMessage.textContent = '密码匹配';
            passwordMatchMessage.className = 'form-text text-success';
        } else {
            passwordMatchMessage.textContent = '密码不匹配';
            passwordMatchMessage.className = 'form-text text-danger';
        }
    }
    passwordInput.addEventListener('input', checkPasswordMatch);
    confirmPasswordInput.addEventListener('input', checkPasswordMatch);
    // 发送邮箱验证码
    const sendVerificationCodeButton = document.getElementById('sendVerificationCode');
    const emailInput = document.getElementById('email');
    sendVerificationCodeButton.addEventListener('click', function() {
        const email = emailInput.value.trim();
        // 验证邮箱格式
        if (!email) {
            alert('请输入邮箱地址');
            return;
        }
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        if (!emailRegex.test(email)) {
            alert('请输入有效的邮箱地址');
            return;
        }
        // 禁用按钮,防止重复点击
        this.disabled = true;
        this.textContent = '发送中...';
        // 发送AJAX请求
        fetch('/user/send_verification_code', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ email: email }),
        })
        .then(response => response.json())
        .then(data => {
            if (data.success) {
                // 成功发送,开始倒计时
                startCountdown(this);
                alert(data.message);
            } else {
                // 发送失败,恢复按钮状态
                this.disabled = false;
                this.textContent = '发送验证码';
                alert(data.message || '发送失败,请稍后重试');
            }
        })
        .catch(error => {
            console.error('Error:', error);
            this.disabled = false;
            this.textContent = '发送验证码';
            alert('发送失败,请稍后重试');
        });
    });
    // 验证码倒计时(60秒)
    function startCountdown(button) {
        let seconds = 60;
        const originalText = '发送验证码';
        const countdownInterval = setInterval(() => {
            seconds--;
            button.textContent = `${seconds}秒后重发`;
            if (seconds <= 0) {
                clearInterval(countdownInterval);
                button.textContent = originalText;
                button.disabled = false;
            }
        }, 1000);
    }
    // 表单提交前验证
    const addUserForm = document.getElementById('addUserForm');
    addUserForm.addEventListener('submit', function(event) {
        // 检查密码是否匹配
        if (passwordInput.value !== confirmPasswordInput.value) {
            event.preventDefault();
            alert('两次输入的密码不匹配,请重新输入');
            return;
        }
        // 如果还有其他前端验证,可以继续添加
    });
    // 自动填充用户名为昵称的默认值
    const usernameInput = document.getElementById('username');
    const nicknameInput = document.getElementById('nickname');
    usernameInput.addEventListener('change', function() {
        // 只有当昵称字段为空时才自动填充
        if (!nicknameInput.value) {
            nicknameInput.value = this.value;
        }
    });
});
================================================================================
File: ./app/static/js/browse.js
================================================================================
// 图书浏览页面脚本
$(document).ready(function() {
    // 分类筛选下拉菜单
    $('.category-filter-toggle').click(function() {
        $(this).toggleClass('active');
        $('.category-filter-dropdown').toggleClass('show');
    });
    // 点击外部关闭下拉菜单
    $(document).click(function(e) {
        if (!$(e.target).closest('.category-filters').length) {
            $('.category-filter-dropdown').removeClass('show');
            $('.category-filter-toggle').removeClass('active');
        }
    });
    // 处理借阅图书
    let bookIdToBorrow = null;
    let bookTitleToBorrow = '';
    $('.borrow-book').click(function(e) {
        e.preventDefault();
        bookIdToBorrow = $(this).data('id');
        // 获取图书标题
        const bookCard = $(this).closest('.book-card');
        bookTitleToBorrow = bookCard.find('.book-title').text();
        $('#borrowBookTitle').text(bookTitleToBorrow);
        $('#borrowModal').modal('show');
    });
    $('#confirmBorrow').click(function() {
        if (!bookIdToBorrow) return;
        // 禁用按钮防止重复提交
        $(this).prop('disabled', true).html(' 处理中...');
        $.ajax({
            url: `/borrow/add/${bookIdToBorrow}`,
            type: 'POST',
            success: function(response) {
                $('#borrowModal').modal('hide');
                if (response.success) {
                    showNotification(response.message, 'success');
                    // 更新UI显示
                    const bookCard = $(`.book-card[data-id="${bookIdToBorrow}"]`);
                    // 更改可借状态
                    bookCard.find('.book-ribbon span').removeClass('available').addClass('unavailable').text('已借出');
                    // 更改借阅按钮
                    bookCard.find('.btn-borrow').replaceWith(`
                        
                    `);
                    // 创建借阅成功动画
                    const successOverlay = $('借阅成功
');
                    bookCard.append(successOverlay);
                    setTimeout(() => {
                        successOverlay.fadeOut(500, function() {
                            $(this).remove();
                        });
                    }, 2000);
                } else {
                    showNotification(response.message, 'error');
                }
                // 恢复按钮状态
                $('#confirmBorrow').prop('disabled', false).html('确认借阅');
            },
            error: function() {
                $('#borrowModal').modal('hide');
                showNotification('借阅操作失败,请稍后重试', 'error');
                $('#confirmBorrow').prop('disabled', false).html('确认借阅');
            }
        });
    });
    // 清除模态框数据
    $('#borrowModal').on('hidden.bs.modal', function() {
        bookIdToBorrow = null;
        bookTitleToBorrow = '';
        $('#borrowBookTitle').text('');
    });
    // 动态添加动画CSS
    const animationCSS = `
        .borrow-success-overlay {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: rgba(102, 126, 234, 0.9);
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            color: white;
            font-weight: 600;
            border-radius: 10px;
            z-index: 10;
            animation: fadeIn 0.3s;
        }
        
        .borrow-success-overlay i {
            font-size: 40px;
            margin-bottom: 10px;
            animation: scaleIn 0.5s;
        }
        
        @keyframes fadeIn {
            from { opacity: 0; }
            to { opacity: 1; }
        }
        
        @keyframes scaleIn {
            from { transform: scale(0); }
            to { transform: scale(1); }
        }
    `;
    $('
    
        
        
        
        
        404
        噢!页面不见了~
        
            抱歉,您要找的页面似乎藏起来了,或者从未存在过。
            请检查您输入的网址是否正确,或者回到首页继续浏览吧!
         
        返回首页