/**
* 图书添加页面脚本
* 处理图书表单的交互、验证和预览功能
*/
let isSubmitting = false;
$(document).ready(function() {
// 全局变量
let cropper;
let coverBlob;
let tags = [];
const coverPreview = $('#coverPreview');
const coverInput = $('#cover');
const tagInput = $('#tagInput');
const tagsContainer = $('#tagsContainer');
const tagsHiddenInput = $('#tags');
// 初始化函数
function initialize() {
initSelect2();
initFormProgress();
initTagsFromInput();
initCoverHandlers();
initNumberControls();
initPriceSlider();
initCharCounter();
initFormValidation();
attachEventListeners();
}
// ========== 组件初始化 ==========
// 初始化Select2
function initSelect2() {
$('.select2').select2({
placeholder: "选择分类...",
allowClear: true,
theme: "classic",
width: '100%'
});
}
// 初始化表单进度条
function initFormProgress() {
updateFormProgress();
$('input, textarea, select').on('change keyup', function() {
updateFormProgress();
});
}
// 初始化标签(从隐藏输入字段)
function initTagsFromInput() {
const tagsValue = $('#tags').val();
if (tagsValue) {
tags = tagsValue.split(',');
renderTags();
}
}
// 初始化封面处理
function initCoverHandlers() {
// 拖放上传功能
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();
}
});
// 重置页面加载完后的字符计数
if ($('#description').val()) {
$('#charCount').text($('#description').val().length);
}
}
// 初始化数字控制
function initNumberControls() {
$('#stockDecrement').on('click', function() {
const input = $('#stock');
const value = parseInt(input.val());
if (value > parseInt(input.attr('min'))) {
input.val(value - 1).trigger('change');
}
});
$('#stockIncrement').on('click', function() {
const input = $('#stock');
const value = parseInt(input.val());
input.val(value + 1).trigger('change');
});
}
// 初始化价格滑块
function initPriceSlider() {
$('#priceRange').on('input', function() {
$('#price').val($(this).val());
});
$('#price').on('input', function() {
const value = parseFloat($(this).val()) || 0;
$('#priceRange').val(Math.min(value, 500));
});
}
// 初始化字符计数器
function initCharCounter() {
$('#description').on('input', function() {
const count = $(this).val().length;
$('#charCount').text(count);
if (count > 2000) {
$('#charCount').addClass('text-danger');
} else {
$('#charCount').removeClass('text-danger');
}
});
}
// 初始化表单验证
ffunction initFormValidation() {
$('#bookForm').on('submit', function(e) {
// 如果表单正在提交中,阻止重复提交
if (isSubmitting) {
e.preventDefault();
showNotification('表单正在提交中,请勿重复点击', 'warning');
return false;
}
let isValid = true;
$('[required]').each(function() {
if (!$(this).val().trim()) {
isValid = false;
$(this).addClass('is-invalid');
// 添加错误提示
if (!$(this).next('.invalid-feedback').length) {
$(this).after(`
此字段不能为空
`);
}
} else {
$(this).removeClass('is-invalid').next('.invalid-feedback').remove();
}
});
// 验证ISBN格式(如果已填写)
const isbn = $('#isbn').val().trim();
if (isbn) {
// 移除所有非数字、X和x字符后检查
const cleanIsbn = isbn.replace(/[^0-9Xx]/g, '');
const isbnRegex = /^(?:\d{10}|\d{13})$|^(?:\d{9}[Xx])$/;
if (!isbnRegex.test(cleanIsbn)) {
isValid = false;
$('#isbn').addClass('is-invalid');
if (!$('#isbn').next('.invalid-feedback').length) {
$('#isbn').after(`ISBN格式不正确,应为10位或13位
`);
}
}
}
if (!isValid) {
e.preventDefault();
// 滚动到第一个错误字段
$('html, body').animate({
scrollTop: $('.is-invalid:first').offset().top - 100
}, 500);
showNotification('请正确填写所有标记的字段', 'error');
} else {
// 设置表单锁定状态
isSubmitting = true;
// 修改提交按钮样式
const submitBtn = $(this).find('button[type="submit"]');
const originalHtml = submitBtn.html();
submitBtn.prop('disabled', true)
.html(' 保存中...');
// 显示提交中通知
showNotification('表单提交中...', 'info');
// 如果表单提交时间过长,30秒后自动解锁
setTimeout(function() {
if (isSubmitting) {
isSubmitting = false;
submitBtn.prop('disabled', false).html(originalHtml);
showNotification('提交超时,请重试', 'warning');
}
}, 30000);
}
});
// 输入时移除错误样式
$('input, textarea, select').on('input change', function() {
$(this).removeClass('is-invalid').next('.invalid-feedback').remove();
});
}
// 还需要在服务端处理成功后重置状态
// 在页面加载完成时,添加监听服务器重定向事件
$(window).on('pageshow', function(event) {
if (event.originalEvent.persisted ||
(window.performance && window.performance.navigation.type === 2)) {
// 如果页面是从缓存加载的或通过后退按钮回到的
isSubmitting = false;
$('button[type="submit"]').prop('disabled', false)
.html(' 保存图书');
}
});
// 绑定事件监听器
function attachEventListeners() {
// 文件选择处理
coverInput.on('change', handleCoverSelect);
// 裁剪控制
$('#rotateLeft').on('click', function() { cropper && cropper.rotate(-90); });
$('#rotateRight').on('click', function() { cropper && cropper.rotate(90); });
$('#zoomIn').on('click', function() { cropper && cropper.zoom(0.1); });
$('#zoomOut').on('click', function() { cropper && cropper.zoom(-0.1); });
$('#cropImage').on('click', applyCrop);
$('#removeCover').on('click', removeCover);
// 标签处理
tagInput.on('keydown', handleTagKeydown);
$('#addTagBtn').on('click', addTag);
$(document).on('click', '.tag-remove', removeTag);
// ISBN查询
$('#isbnLookup').on('click', lookupISBN);
// 预览按钮
$('#previewBtn').on('click', showPreview);
// 表单重置
$('#resetBtn').on('click', confirmReset);
}
// ========== 功能函数 ==========
// 更新表单进度条
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) {
$('.btn-primary').addClass('pulse');
} else {
$('.btn-primary').removeClass('pulse');
}
}
// 处理封面选择
function handleCoverSelect(e) {
const file = e.target.files[0];
if (!file) return;
// 验证文件类型
if (!file.type.match('image.*')) {
showNotification('请选择图片文件', 'warning');
return;
}
// 验证文件大小(最大5MB)
if (file.size > 5 * 1024 * 1024) {
showNotification('图片大小不能超过5MB', 'warning');
return;
}
const reader = new FileReader();
reader.onload = function(e) {
// 先显示在预览框中,确保用户能立即看到上传的图片
coverPreview.html(`
`);
// 准备裁剪图片
$('#cropperImage').attr('src', e.target.result);
// 确保图片加载完成后再显示模态框
$('#cropperImage').on('load', function() {
// 打开模态框
$('#cropperModal').modal('show');
// 在模态框完全显示后初始化裁剪器
$('#cropperModal').on('shown.bs.modal', function() {
if (cropper) {
cropper.destroy();
}
try {
cropper = new Cropper(document.getElementById('cropperImage'), {
aspectRatio: 5 / 7,
viewMode: 2,
responsive: true,
guides: true,
background: true,
ready: function() {
console.log('Cropper初始化成功');
}
});
} catch (err) {
console.error('Cropper初始化失败:', err);
showNotification('图片处理工具初始化失败,请重试', 'error');
}
});
});
};
// 处理读取错误
reader.onerror = function() {
showNotification('读取图片失败,请重试', 'error');
};
reader.readAsDataURL(file);
}
// 应用裁剪
function applyCrop() {
if (!cropper) {
showNotification('图片处理工具未就绪,请重新上传', 'error');
$('#cropperModal').modal('hide');
return;
}
try {
const canvas = cropper.getCroppedCanvas({
width: 500,
height: 700,
fillColor: '#fff',
imageSmoothingEnabled: true,
imageSmoothingQuality: 'high',
});
if (!canvas) {
throw new Error('无法生成裁剪后的图片');
}
canvas.toBlob(function(blob) {
if (!blob) {
showNotification('图片处理失败,请重试', 'error');
return;
}
const url = URL.createObjectURL(blob);
coverPreview.html(`
`);
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');
showNotification('封面图片已更新', 'success');
}, 'image/jpeg', 0.95);
} catch (err) {
console.error('裁剪失败:', err);
showNotification('图片裁剪失败,请重试', 'error');
$('#cropperModal').modal('hide');
}
}
// 移除封面
function removeCover() {
coverPreview.html(`
`);
coverInput.val('');
coverBlob = null;
}
// 渲染标签
function renderTags() {
tagsContainer.empty();
tags.forEach(tag => {
tagsContainer.append(`
${tag}
`);
});
tagsHiddenInput.val(tags.join(','));
}
// 添加标签
function addTag() {
const tag = tagInput.val().trim();
if (tag && !tags.includes(tag)) {
tags.push(tag);
renderTags();
tagInput.val('').focus();
}
}
// 处理标签输入键盘事件
function handleTagKeydown(e) {
if (e.key === 'Enter' || e.key === ',') {
e.preventDefault();
addTag();
}
}
// 移除标签
function removeTag() {
const tagToRemove = $(this).data('tag');
tags = tags.filter(t => t !== tagToRemove);
renderTags();
}
// ISBN查询
function lookupISBN() {
const isbn = $('#isbn').val().trim();
if (!isbn) {
showNotification('请先输入ISBN', 'warning');
return;
}
// 验证ISBN格式
const cleanIsbn = isbn.replace(/[^0-9Xx]/g, '');
const isbnRegex = /^(?:\d{10}|\d{13})$|^(?:\d{9}[Xx])$/;
if (!isbnRegex.test(cleanIsbn)) {
showNotification('ISBN格式不正确,应为10位或13位', 'warning');
return;
}
$(this).html('');
// 先检查ISBN是否已存在
$.get('/book/api/check-isbn', {isbn: isbn}, function(data) {
if (data.exists) {
$('#isbnLookup').html('');
showNotification(`ISBN "${isbn}" 已存在: 《${data.book_title}》`, 'warning');
$('#isbn').addClass('is-invalid');
if (!$('#isbn').next('.invalid-feedback').length) {
$('#isbn').after(`此ISBN已被图书《${data.book_title}》使用
`);
}
} else {
// 继续查询外部API(模拟)
simulateISBNLookup(isbn);
}
}).fail(function() {
$('#isbnLookup').html('');
showNotification('服务器查询失败,请稍后再试', 'error');
});
}
// 模拟ISBN查询
function simulateISBNLookup(isbn) {
// 模拟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');
$('#priceRange').val('59.70');
showNotification('ISBN查询成功', 'success');
} else if (isbn === '9787544270878') {
$('#title').val('挪威的森林').trigger('blur');
$('#author').val('村上春树').trigger('blur');
$('#publisher').val('南海出版社').trigger('blur');
$('#publish_year').val('2017').trigger('blur');
$('#category_id').val('2').trigger('change');
tags = ['外国文学', '日本', '小说'];
renderTags();
$('#description').val('《挪威的森林》是日本作家村上春树创作的长篇小说,首次出版于1987年。小说讲述了一个悲伤的爱情故事,背景设定在20世纪60年代末的日本。主人公渡边纠缠在与平静的直子和开朗的绿子两人的感情中,最终选择了生活。').trigger('input');
$('#price').val('39.50').trigger('input');
$('#priceRange').val('39.50');
showNotification('ISBN查询成功', 'success');
} else {
showNotification('未找到相关图书信息', 'warning');
}
$('#isbnLookup').html('');
updateFormProgress();
}, 1500);
}
// 显示预览
function showPreview() {
// 检查必填字段
if (!$('#title').val().trim() || !$('#author').val().trim()) {
showNotification('请至少填写书名和作者后再预览', 'warning');
return;
}
try {
// 确保所有值都有默认值,防止undefined错误
const title = $('#title').val() || '未填写标题';
const author = $('#author').val() || '未填写作者';
const publisher = $('#publisher').val() || '-';
const isbn = $('#isbn').val() || '-';
const publishYear = $('#publish_year').val() || '-';
const description = $('#description').val() || '';
const stock = $('#stock').val() || '0';
let price = parseFloat($('#price').val()) || 0;
// 填充预览内容
$('#previewTitle').text(title);
$('#previewAuthor').text(author ? '作者: ' + author : '未填写作者');
$('#previewPublisher').text(publisher);
$('#previewISBN').text(isbn);
$('#previewYear').text(publishYear);
// 获取分类文本
const categoryId = $('#category_id').val();
const categoryText = categoryId ? $('#category_id option:selected').text() : '-';
$('#previewCategory').text(categoryText);
// 价格和库存
$('#previewPrice').text(price ? '¥' + price.toFixed(2) : '¥0.00');
$('#previewStock').text('库存: ' + stock);
// 标签
const previewTags = $('#previewTags');
previewTags.empty();
if (tags && tags.length > 0) {
tags.forEach(tag => {
previewTags.append(`${tag}`);
});
} else {
previewTags.append('暂无标签');
}
// 描述
if (description) {
$('#previewDescription').html(`${description.replace(/\n/g, '
')}
`);
} else {
$('#previewDescription').html(`暂无简介内容
`);
}
// 封面
const previewCover = $('#previewCover');
previewCover.empty(); // 清空现有内容
if ($('#coverPreview img').length) {
const coverSrc = $('#coverPreview img').attr('src');
previewCover.html(`
`);
} else {
previewCover.html(`
暂无封面
`);
}
// 显示预览模态框
$('#previewModal').modal('show');
console.log('预览模态框已显示');
} catch (err) {
console.error('生成预览时发生错误:', err);
showNotification('生成预览时出错,请重试', 'error');
}
}
// 确认重置表单
function confirmReset() {
if (confirm('确定要重置表单吗?所有已填写的内容将被清空。')) {
$('#bookForm')[0].reset();
removeCover();
tags = [];
renderTags();
updateFormProgress();
$('.select2').val(null).trigger('change');
$('#charCount').text('0');
showNotification('表单已重置', 'info');
}
}
// 通知提示函数
function showNotification(message, type) {
// 创建通知元素
const notification = $(`
`);
// 添加到页面
if ($('.notification-container').length === 0) {
$('body').append('');
}
$('.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';
}
}
// 初始化页面
initialize();
});