843 lines
38 KiB
HTML
843 lines
38 KiB
HTML
{% extends 'base.html' %}
|
||
|
||
{% block title %}添加图书 - 图书管理系统{% endblock %}
|
||
|
||
{% block head %}
|
||
<link rel="stylesheet" href="{{ url_for('static', filename='css/book-form.css') }}">
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css">
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.12/cropper.min.css">
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="book-form-container animate__animated animate__fadeIn">
|
||
<!-- 顶部导航和标题区域 -->
|
||
<div class="page-header-wrapper">
|
||
<div class="page-header">
|
||
<div class="header-title-section">
|
||
<h1 class="page-title"><i class="fas fa-book-medical pulse-icon"></i> 添加新图书</h1>
|
||
<p class="subtitle">创建新书籍记录并添加到系统库存</p>
|
||
</div>
|
||
<div class="header-actions">
|
||
<a href="{{ url_for('book.book_list') }}" class="btn btn-light btn-icon-text">
|
||
<i class="fas fa-arrow-left"></i>
|
||
<span>返回列表</span>
|
||
</a>
|
||
<div class="form-progress">
|
||
<div class="progress">
|
||
<div class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" id="formProgress"></div>
|
||
</div>
|
||
<span class="progress-text" id="progressText">完成 0%</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 主表单区域 -->
|
||
<form method="POST" enctype="multipart/form-data" class="book-form" id="bookForm">
|
||
<div class="form-row">
|
||
<!-- 左侧表单区域 -->
|
||
<div class="col-lg-8">
|
||
<!-- 基本信息卡片 -->
|
||
<div class="card form-card">
|
||
<div class="card-header">
|
||
<div class="card-header-icon">
|
||
<i class="fas fa-info-circle"></i>
|
||
</div>
|
||
<div class="card-header-title">基本信息</div>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-section">
|
||
<div class="form-row">
|
||
<div class="form-group col-md-12">
|
||
<label for="title" class="form-label">
|
||
<i class="fas fa-heading label-icon"></i>书名
|
||
<span class="required">*</span>
|
||
</label>
|
||
<input type="text" class="form-control custom-input floating-input" id="title" name="title" required>
|
||
<span class="floating-label">请输入完整图书名称</span>
|
||
<div class="form-text">完整准确的书名有助于读者查找</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group col-md-6">
|
||
<label for="author" class="form-label">
|
||
<i class="fas fa-user-edit label-icon"></i>作者
|
||
<span class="required">*</span>
|
||
</label>
|
||
<input type="text" class="form-control custom-input floating-input" id="author" name="author" required>
|
||
<span class="floating-label">作者姓名</span>
|
||
</div>
|
||
<div class="form-group col-md-6">
|
||
<label for="publisher" class="form-label">
|
||
<i class="fas fa-building label-icon"></i>出版社
|
||
</label>
|
||
<input type="text" class="form-control custom-input floating-input" id="publisher" name="publisher">
|
||
<span class="floating-label">出版社名称</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group col-md-6">
|
||
<label for="isbn" class="form-label">
|
||
<i class="fas fa-barcode label-icon"></i>ISBN
|
||
</label>
|
||
<div class="input-group">
|
||
<input type="text" class="form-control custom-input floating-input" id="isbn" name="isbn">
|
||
<span class="floating-label">国际标准图书编号</span>
|
||
<div class="input-group-append">
|
||
<button class="btn btn-primary isbn-lookup-btn" type="button" id="isbnLookup">
|
||
<i class="fas fa-search"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="form-text">输入ISBN并点击查询按钮自动填充图书信息</div>
|
||
</div>
|
||
<div class="form-group col-md-6">
|
||
<label for="publish_year" class="form-label">
|
||
<i class="fas fa-calendar-alt label-icon"></i>出版年份
|
||
</label>
|
||
<input type="text" class="form-control custom-input floating-input" id="publish_year" name="publish_year">
|
||
<span class="floating-label">如:2023</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 分类和标签卡片 -->
|
||
<div class="card form-card">
|
||
<div class="card-header">
|
||
<div class="card-header-icon">
|
||
<i class="fas fa-tags"></i>
|
||
</div>
|
||
<div class="card-header-title">分类与标签</div>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-section">
|
||
<div class="form-row">
|
||
<div class="form-group col-md-6">
|
||
<label for="category_id" class="form-label">
|
||
<i class="fas fa-folder label-icon"></i>图书分类
|
||
</label>
|
||
<select class="form-control custom-select select2" id="category_id" name="category_id">
|
||
<option value="">选择分类...</option>
|
||
{% for category in categories %}
|
||
<option value="{{ category.id }}">{{ category.name }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
<div class="form-text">为图书选择合适的分类以便于管理和查找</div>
|
||
</div>
|
||
<div class="form-group col-md-6">
|
||
<label for="tags" class="form-label">
|
||
<i class="fas fa-tag label-icon"></i>标签
|
||
</label>
|
||
<div class="tag-input-container">
|
||
<input type="text" class="form-control custom-input tag-input" id="tagInput" placeholder="输入标签后按回车添加">
|
||
<input type="hidden" id="tags" name="tags">
|
||
<button type="button" class="btn btn-sm btn-primary add-tag-btn">
|
||
<i class="fas fa-plus"></i>
|
||
</button>
|
||
</div>
|
||
<div class="tags-container" id="tagsContainer"></div>
|
||
<div class="form-text">添加多个标签以提高图书的检索率</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 图书简介卡片 -->
|
||
<div class="card form-card">
|
||
<div class="card-header">
|
||
<div class="card-header-icon">
|
||
<i class="fas fa-align-left"></i>
|
||
</div>
|
||
<div class="card-header-title">图书简介</div>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-section">
|
||
<div class="form-group">
|
||
<label for="description" class="form-label">
|
||
<i class="fas fa-file-alt label-icon"></i>内容简介
|
||
</label>
|
||
<textarea class="form-control custom-textarea" id="description" name="description" rows="8" placeholder="请输入图书的简要介绍..."></textarea>
|
||
<div class="form-text">
|
||
<div class="d-flex justify-content-between">
|
||
<span>简要描述图书的内容、特点和主要观点</span>
|
||
<span class="text-count"><span id="charCount">0</span>/2000</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右侧表单区域 -->
|
||
<div class="col-lg-4">
|
||
<!-- 封面图片卡片 -->
|
||
<div class="card form-card">
|
||
<div class="card-header">
|
||
<div class="card-header-icon">
|
||
<i class="fas fa-image"></i>
|
||
</div>
|
||
<div class="card-header-title">封面图片</div>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="cover-preview-container">
|
||
<div class="cover-preview" id="coverPreview">
|
||
<div class="no-cover-placeholder">
|
||
<i class="fas fa-book-open"></i>
|
||
<span>暂无封面</span>
|
||
<p class="placeholder-tip">点击上传或拖放图片至此处</p>
|
||
</div>
|
||
</div>
|
||
<div class="upload-options">
|
||
<div class="upload-btn-group">
|
||
<label for="cover" class="btn btn-primary custom-upload-btn">
|
||
<i class="fas fa-upload"></i> 上传图片
|
||
</label>
|
||
<button type="button" class="btn btn-outline-secondary btn-icon" id="removeCover">
|
||
<i class="fas fa-trash-alt"></i>
|
||
</button>
|
||
</div>
|
||
<input type="file" id="cover" name="cover" class="form-control-file" accept="image/*" style="display:none;">
|
||
<div class="form-text text-center mt-2">
|
||
<small>推荐尺寸: 500×700px (竖版封面)</small><br>
|
||
<small>支持格式: JPG, PNG, WebP</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 库存和价格卡片 -->
|
||
<div class="card form-card">
|
||
<div class="card-header">
|
||
<div class="card-header-icon">
|
||
<i class="fas fa-cubes"></i>
|
||
</div>
|
||
<div class="card-header-title">库存和价格</div>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-section">
|
||
<div class="form-group">
|
||
<label for="stock" class="form-label">
|
||
<i class="fas fa-layer-group label-icon"></i>库存数量
|
||
</label>
|
||
<div class="input-number-group">
|
||
<button type="button" class="btn btn-outline-secondary input-number-decrement">
|
||
<i class="fas fa-minus"></i>
|
||
</button>
|
||
<input type="number" class="form-control custom-input text-center" id="stock" name="stock" min="0" value="0">
|
||
<button type="button" class="btn btn-outline-secondary input-number-increment">
|
||
<i class="fas fa-plus"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="price" class="form-label">
|
||
<i class="fas fa-yen-sign label-icon"></i>价格
|
||
</label>
|
||
<div class="input-group">
|
||
<div class="input-group-prepend">
|
||
<span class="input-group-text">¥</span>
|
||
</div>
|
||
<input type="number" class="form-control custom-input" id="price" name="price" step="0.01" min="0" placeholder="0.00">
|
||
</div>
|
||
<div class="price-slider mt-3">
|
||
<input type="range" class="custom-range" id="priceRange" min="0" max="500" step="0.5" value="0">
|
||
<div class="d-flex justify-content-between">
|
||
<small>¥0</small>
|
||
<small>¥250</small>
|
||
<small>¥500</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 提交按钮区域 -->
|
||
<div class="form-submit-container">
|
||
<div class="action-buttons">
|
||
<button type="submit" class="btn btn-primary btn-lg btn-block submit-btn pulse">
|
||
<i class="fas fa-save"></i> 保存图书
|
||
</button>
|
||
<div class="secondary-buttons">
|
||
<button type="button" class="btn btn-outline-secondary btn-block" id="previewBtn">
|
||
<i class="fas fa-eye"></i> 预览
|
||
</button>
|
||
<button type="reset" class="btn btn-outline-danger btn-block reset-btn">
|
||
<i class="fas fa-undo"></i> 重置
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="form-tip">
|
||
<i class="fas fa-info-circle"></i>
|
||
<span>带 <span class="required">*</span> 的字段为必填项</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- 图片裁剪模态框 -->
|
||
<div class="modal fade" id="cropperModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg" role="document">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">调整封面图片</h5>
|
||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||
<span aria-hidden="true">×</span>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="img-container">
|
||
<img id="cropperImage" src="" alt="图片预览">
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<div class="cropper-controls">
|
||
<div class="btn-group mr-2">
|
||
<button type="button" class="btn btn-outline-secondary" id="rotateLeft">
|
||
<i class="fas fa-undo"></i>
|
||
</button>
|
||
<button type="button" class="btn btn-outline-secondary" id="rotateRight">
|
||
<i class="fas fa-redo"></i>
|
||
</button>
|
||
</div>
|
||
<div class="btn-group">
|
||
<button type="button" class="btn btn-outline-secondary" id="zoomIn">
|
||
<i class="fas fa-search-plus"></i>
|
||
</button>
|
||
<button type="button" class="btn btn-outline-secondary" id="zoomOut">
|
||
<i class="fas fa-search-minus"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
|
||
<button type="button" class="btn btn-primary" id="cropImage">应用裁剪</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 图书预览模态框 -->
|
||
<div class="modal fade" id="previewModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg" role="document">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">图书预览</h5>
|
||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||
<span aria-hidden="true">×</span>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="book-preview-container">
|
||
<div class="row">
|
||
<div class="col-md-4">
|
||
<div class="book-preview-cover" id="previewCover">
|
||
<div class="no-cover-placeholder">
|
||
<i class="fas fa-book-open"></i>
|
||
<span>暂无封面</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-8">
|
||
<div class="book-preview-details">
|
||
<h3 id="previewTitle">书名加载中...</h3>
|
||
<p class="book-author" id="previewAuthor">作者加载中...</p>
|
||
|
||
<div class="book-info-section">
|
||
<div class="book-info-item">
|
||
<span class="info-label">出版社:</span>
|
||
<span class="info-value" id="previewPublisher">-</span>
|
||
</div>
|
||
<div class="book-info-item">
|
||
<span class="info-label">ISBN:</span>
|
||
<span class="info-value" id="previewISBN">-</span>
|
||
</div>
|
||
<div class="book-info-item">
|
||
<span class="info-label">出版年份:</span>
|
||
<span class="info-value" id="previewYear">-</span>
|
||
</div>
|
||
<div class="book-info-item">
|
||
<span class="info-label">分类:</span>
|
||
<span class="info-value" id="previewCategory">-</span>
|
||
</div>
|
||
<div class="book-info-item">
|
||
<span class="info-label">价格:</span>
|
||
<span class="info-value" id="previewPrice">-</span>
|
||
</div>
|
||
<div class="book-info-item">
|
||
<span class="info-label">库存:</span>
|
||
<span class="info-value" id="previewStock">-</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="book-tags" id="previewTags"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="book-description" id="previewDescription">
|
||
<h4>图书简介</h4>
|
||
<p>简介加载中...</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
|
||
<button type="button" class="btn btn-primary" data-dismiss="modal" id="continueEditBtn">继续编辑</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block scripts %}
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.12/cropper.min.js"></script>
|
||
<script>
|
||
$(document).ready(function() {
|
||
// 初始化Select2
|
||
$('.select2').select2({
|
||
placeholder: "选择分类...",
|
||
allowClear: true,
|
||
theme: "classic"
|
||
});
|
||
|
||
// 浮动标签效果
|
||
$('.floating-input').on('focus blur', function(e) {
|
||
$(this).parents('.form-group').toggleClass('focused', (e.type === 'focus' || this.value.length > 0));
|
||
}).trigger('blur');
|
||
|
||
// 表单进度条
|
||
updateFormProgress();
|
||
$('input, textarea, select').on('change keyup', function() {
|
||
updateFormProgress();
|
||
});
|
||
|
||
function updateFormProgress() {
|
||
const requiredFields = $('[required]');
|
||
const filledFields = requiredFields.filter(function() {
|
||
return $(this).val() !== '';
|
||
});
|
||
|
||
const otherFields = $('input:not([required]), textarea:not([required]), select:not([required])').not('[type="file"]');
|
||
const filledOtherFields = otherFields.filter(function() {
|
||
return $(this).val() !== '';
|
||
});
|
||
|
||
let requiredWeight = 70; // 必填字段权重70%
|
||
let otherWeight = 30; // 非必填字段权重30%
|
||
|
||
let requiredProgress = requiredFields.length ? (filledFields.length / requiredFields.length) * requiredWeight : requiredWeight;
|
||
let otherProgress = otherFields.length ? (filledOtherFields.length / otherFields.length) * otherWeight : 0;
|
||
|
||
let totalProgress = Math.floor(requiredProgress + otherProgress);
|
||
|
||
$('#formProgress').css('width', totalProgress + '%').attr('aria-valuenow', totalProgress);
|
||
$('#progressText').text('完成 ' + totalProgress + '%');
|
||
|
||
if (totalProgress >= 100) {
|
||
$('.submit-btn').addClass('pulse');
|
||
} else {
|
||
$('.submit-btn').removeClass('pulse');
|
||
}
|
||
}
|
||
|
||
// 封面图片处理
|
||
let cropper;
|
||
let coverBlob;
|
||
const coverPreview = $('#coverPreview');
|
||
const coverInput = $('#cover');
|
||
|
||
// 文件选择处理
|
||
coverInput.on('change', function(e) {
|
||
const file = e.target.files[0];
|
||
if (!file) return;
|
||
|
||
const reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
// 打开模态框并初始化裁剪
|
||
$('#cropperImage').attr('src', e.target.result);
|
||
$('#cropperModal').modal('show');
|
||
|
||
// 模态框显示后初始化Cropper
|
||
$('#cropperModal').on('shown.bs.modal', function() {
|
||
if (cropper) {
|
||
cropper.destroy();
|
||
}
|
||
cropper = new Cropper(document.getElementById('cropperImage'), {
|
||
aspectRatio: 5 / 7,
|
||
viewMode: 2,
|
||
responsive: true,
|
||
guides: true,
|
||
background: true
|
||
});
|
||
});
|
||
};
|
||
reader.readAsDataURL(file);
|
||
});
|
||
|
||
// 裁剪控制
|
||
$('#rotateLeft').on('click', function() {
|
||
cropper.rotate(-90);
|
||
});
|
||
|
||
$('#rotateRight').on('click', function() {
|
||
cropper.rotate(90);
|
||
});
|
||
|
||
$('#zoomIn').on('click', function() {
|
||
cropper.zoom(0.1);
|
||
});
|
||
|
||
$('#zoomOut').on('click', function() {
|
||
cropper.zoom(-0.1);
|
||
});
|
||
|
||
// 应用裁剪
|
||
$('#cropImage').on('click', function() {
|
||
const canvas = cropper.getCroppedCanvas({
|
||
width: 500,
|
||
height: 700,
|
||
fillColor: '#fff',
|
||
imageSmoothingEnabled: true,
|
||
imageSmoothingQuality: 'high',
|
||
});
|
||
|
||
canvas.toBlob(function(blob) {
|
||
const url = URL.createObjectURL(blob);
|
||
coverPreview.html(`<img src="${url}" class="cover-image" alt="图书封面">`);
|
||
coverBlob = blob;
|
||
|
||
// 模拟File对象
|
||
const fileList = new DataTransfer();
|
||
const file = new File([blob], "cover.jpg", {type: "image/jpeg"});
|
||
fileList.items.add(file);
|
||
document.getElementById('cover').files = fileList.files;
|
||
|
||
$('#cropperModal').modal('hide');
|
||
}, 'image/jpeg', 0.95);
|
||
});
|
||
|
||
// 移除封面
|
||
$('#removeCover').on('click', function() {
|
||
coverPreview.html(`
|
||
<div class="no-cover-placeholder">
|
||
<i class="fas fa-book-open"></i>
|
||
<span>暂无封面</span>
|
||
<p class="placeholder-tip">点击上传或拖放图片至此处</p>
|
||
</div>
|
||
`);
|
||
coverInput.val('');
|
||
coverBlob = null;
|
||
});
|
||
|
||
// 拖放上传功能
|
||
coverPreview.on('dragover', function(e) {
|
||
e.preventDefault();
|
||
$(this).addClass('dragover');
|
||
}).on('dragleave drop', function(e) {
|
||
e.preventDefault();
|
||
$(this).removeClass('dragover');
|
||
}).on('drop', function(e) {
|
||
e.preventDefault();
|
||
const file = e.originalEvent.dataTransfer.files[0];
|
||
if (file && file.type.match('image.*')) {
|
||
coverInput[0].files = e.originalEvent.dataTransfer.files;
|
||
coverInput.trigger('change');
|
||
}
|
||
}).on('click', function() {
|
||
if (!$(this).find('img').length) {
|
||
coverInput.click();
|
||
}
|
||
});
|
||
|
||
// 标签处理
|
||
const tagInput = $('#tagInput');
|
||
const tagsContainer = $('#tagsContainer');
|
||
const tagsHiddenInput = $('#tags');
|
||
let tags = [];
|
||
|
||
function renderTags() {
|
||
tagsContainer.empty();
|
||
tags.forEach(tag => {
|
||
tagsContainer.append(`
|
||
<span class="tag-item">
|
||
${tag}
|
||
<i class="fas fa-times remove-tag" data-tag="${tag}"></i>
|
||
</span>
|
||
`);
|
||
});
|
||
tagsHiddenInput.val(tags.join(','));
|
||
}
|
||
|
||
function addTag() {
|
||
const tag = tagInput.val().trim();
|
||
if (tag && !tags.includes(tag)) {
|
||
tags.push(tag);
|
||
renderTags();
|
||
tagInput.val('').focus();
|
||
}
|
||
}
|
||
|
||
tagInput.on('keydown', function(e) {
|
||
if (e.key === 'Enter' || e.key === ',') {
|
||
e.preventDefault();
|
||
addTag();
|
||
}
|
||
});
|
||
|
||
$('.add-tag-btn').on('click', addTag);
|
||
|
||
$(document).on('click', '.remove-tag', function() {
|
||
const tagToRemove = $(this).data('tag');
|
||
tags = tags.filter(t => t !== tagToRemove);
|
||
renderTags();
|
||
});
|
||
|
||
// 数字输入控制
|
||
$('.input-number-decrement').on('click', function() {
|
||
const input = $(this).siblings('input');
|
||
const value = parseInt(input.val());
|
||
if (value > parseInt(input.attr('min'))) {
|
||
input.val(value - 1).trigger('change');
|
||
}
|
||
});
|
||
|
||
$('.input-number-increment').on('click', function() {
|
||
const input = $(this).siblings('input');
|
||
const value = parseInt(input.val());
|
||
input.val(value + 1).trigger('change');
|
||
});
|
||
|
||
// 价格滑块联动
|
||
$('#priceRange').on('input', function() {
|
||
$('#price').val($(this).val());
|
||
});
|
||
|
||
$('#price').on('input', function() {
|
||
const value = parseFloat($(this).val()) || 0;
|
||
$('#priceRange').val(Math.min(value, 500));
|
||
});
|
||
|
||
// 字符计数
|
||
$('#description').on('input', function() {
|
||
const count = $(this).val().length;
|
||
$('#charCount').text(count);
|
||
if (count > 2000) {
|
||
$('#charCount').addClass('text-danger');
|
||
} else {
|
||
$('#charCount').removeClass('text-danger');
|
||
}
|
||
});
|
||
|
||
// ISBN查询模拟
|
||
$('#isbnLookup').on('click', function() {
|
||
const isbn = $('#isbn').val().trim();
|
||
if (!isbn) return;
|
||
|
||
$(this).html('<i class="fas fa-spinner fa-spin"></i>');
|
||
|
||
// 模拟API查询延迟
|
||
setTimeout(() => {
|
||
// 模拟查到的数据
|
||
if (isbn === '9787020002207') {
|
||
$('#title').val('红楼梦').trigger('blur');
|
||
$('#author').val('曹雪芹').trigger('blur');
|
||
$('#publisher').val('人民文学出版社').trigger('blur');
|
||
$('#publish_year').val('1996').trigger('blur');
|
||
$('#category_id').val('1').trigger('change');
|
||
tags = ['中国文学', '古典', '名著'];
|
||
renderTags();
|
||
$('#description').val('《红楼梦》是中国古代章回体长篇小说,中国古典四大名著之一,通行本共120回,一般认为前80回是清代作家曹雪芹所著,后40回作者有争议。小说以贾、史、王、薛四大家族的兴衰为背景,以贾府的家庭琐事、闺阁闲情为脉络,以贾宝玉、林黛玉、薛宝钗的爱情婚姻悲剧为主线,刻画了以贾宝玉和金陵十二钗为中心的正邪两赋有情人的人性美和悲剧美。').trigger('input');
|
||
$('#price').val('59.70').trigger('input');
|
||
|
||
// 显示成功通知
|
||
showNotification('ISBN查询成功', 'success');
|
||
} else {
|
||
showNotification('未找到相关图书信息', 'warning');
|
||
}
|
||
|
||
$(this).html('<i class="fas fa-search"></i>');
|
||
}, 1500);
|
||
});
|
||
|
||
// 表单预览
|
||
$('#previewBtn').on('click', function() {
|
||
// 填充预览内容
|
||
$('#previewTitle').text($('#title').val() || '未填写标题');
|
||
$('#previewAuthor').text($('#author').val() || '未填写作者');
|
||
$('#previewPublisher').text($('#publisher').val() || '-');
|
||
$('#previewISBN').text($('#isbn').val() || '-');
|
||
$('#previewYear').text($('#publish_year').val() || '-');
|
||
|
||
// 获取分类文本
|
||
const categoryId = $('#category_id').val();
|
||
const categoryText = categoryId ? $('#category_id option:selected').text() : '-';
|
||
$('#previewCategory').text(categoryText);
|
||
|
||
// 价格和库存
|
||
const price = parseFloat($('#price').val());
|
||
$('#previewPrice').text(price ? '¥' + price.toFixed(2) : '-');
|
||
$('#previewStock').text($('#stock').val() || '0');
|
||
|
||
// 标签
|
||
const previewTags = $('#previewTags');
|
||
previewTags.empty();
|
||
tags.forEach(tag => {
|
||
previewTags.append(`<span class="preview-tag">${tag}</span>`);
|
||
});
|
||
|
||
// 描述
|
||
const description = $('#description').val();
|
||
if (description) {
|
||
$('#previewDescription').html(`
|
||
<h4>图书简介</h4>
|
||
<p>${description.replace(/\n/g, '<br>')}</p>
|
||
`);
|
||
} else {
|
||
$('#previewDescription').html(`
|
||
<h4>图书简介</h4>
|
||
<p class="text-muted">未填写图书简介</p>
|
||
`);
|
||
}
|
||
|
||
// 封面
|
||
if ($('#coverPreview img').length) {
|
||
const coverSrc = $('#coverPreview img').attr('src');
|
||
$('#previewCover').html(`<img src="${coverSrc}" class="img-fluid" alt="封面预览">`);
|
||
} else {
|
||
$('#previewCover').html(`
|
||
<div class="no-cover-placeholder">
|
||
<i class="fas fa-book-open"></i>
|
||
<span>暂无封面</span>
|
||
</div>
|
||
`);
|
||
}
|
||
|
||
// 显示预览模态框
|
||
$('#previewModal').modal('show');
|
||
});
|
||
|
||
// 表单验证
|
||
$('#bookForm').on('submit', function(e) {
|
||
let isValid = true;
|
||
|
||
$('[required]').each(function() {
|
||
if (!$(this).val().trim()) {
|
||
isValid = false;
|
||
$(this).addClass('is-invalid');
|
||
|
||
// 添加错误提示
|
||
if (!$(this).next('.invalid-feedback').length) {
|
||
$(this).after(`<div class="invalid-feedback">此字段不能为空</div>`);
|
||
}
|
||
} else {
|
||
$(this).removeClass('is-invalid').next('.invalid-feedback').remove();
|
||
}
|
||
});
|
||
|
||
if (!isValid) {
|
||
e.preventDefault();
|
||
|
||
// 滚动到第一个错误字段
|
||
$('html, body').animate({
|
||
scrollTop: $('.is-invalid:first').offset().top - 100
|
||
}, 500);
|
||
|
||
showNotification('请填写所有必填字段', 'error');
|
||
} else {
|
||
showNotification('表单提交中...', 'info');
|
||
}
|
||
});
|
||
|
||
// 表单重置
|
||
$('.reset-btn').on('click', function() {
|
||
if (confirm('确定要重置表单吗?所有已填写的内容将被清空。')) {
|
||
$('#bookForm')[0].reset();
|
||
$('.floating-input').trigger('blur');
|
||
$('#coverPreview').html(`
|
||
<div class="no-cover-placeholder">
|
||
<i class="fas fa-book-open"></i>
|
||
<span>暂无封面</span>
|
||
<p class="placeholder-tip">点击上传或拖放图片至此处</p>
|
||
</div>
|
||
`);
|
||
tags = [];
|
||
renderTags();
|
||
updateFormProgress();
|
||
$('.select2').val(null).trigger('change');
|
||
$('#charCount').text('0');
|
||
|
||
showNotification('表单已重置', 'info');
|
||
}
|
||
});
|
||
|
||
// 输入时移除错误样式
|
||
$('input, textarea, select').on('input change', function() {
|
||
$(this).removeClass('is-invalid').next('.invalid-feedback').remove();
|
||
});
|
||
|
||
// 通知提示函数
|
||
function showNotification(message, type) {
|
||
// 创建通知元素
|
||
const notification = $(`
|
||
<div class="custom-notification notification-${type} animate__animated animate__fadeInRight">
|
||
<div class="notification-icon">
|
||
<i class="fas ${getIconForType(type)}"></i>
|
||
</div>
|
||
<div class="notification-content">
|
||
<p>${message}</p>
|
||
</div>
|
||
<button class="notification-close">
|
||
<i class="fas fa-times"></i>
|
||
</button>
|
||
</div>
|
||
`);
|
||
|
||
// 添加到页面
|
||
if ($('.notification-container').length === 0) {
|
||
$('body').append('<div class="notification-container"></div>');
|
||
}
|
||
$('.notification-container').append(notification);
|
||
|
||
// 自动关闭
|
||
setTimeout(() => {
|
||
notification.removeClass('animate__fadeInRight').addClass('animate__fadeOutRight');
|
||
setTimeout(() => {
|
||
notification.remove();
|
||
}, 500);
|
||
}, 5000);
|
||
|
||
// 点击关闭
|
||
notification.find('.notification-close').on('click', function() {
|
||
notification.removeClass('animate__fadeInRight').addClass('animate__fadeOutRight');
|
||
setTimeout(() => {
|
||
notification.remove();
|
||
}, 500);
|
||
});
|
||
}
|
||
|
||
function getIconForType(type) {
|
||
switch(type) {
|
||
case 'success': return 'fa-check-circle';
|
||
case 'warning': return 'fa-exclamation-triangle';
|
||
case 'error': return 'fa-times-circle';
|
||
case 'info':
|
||
default: return 'fa-info-circle';
|
||
}
|
||
}
|
||
});
|
||
</script>
|
||
{% endblock %}
|