734 lines
26 KiB
HTML
734 lines
26 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||
<title>智慧图书馆管理系统 | 交互式图书管理</title>
|
||
<!-- Font Awesome 6 图标库 -->
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||
<!-- 使用 Google Fonts 提升字体观感 -->
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;14..32,400;14..32,500;14..32,600;14..32,700&display=swap" rel="stylesheet">
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Inter', sans-serif;
|
||
background: #f1f5f9;
|
||
color: #0f172a;
|
||
padding: 24px 20px;
|
||
}
|
||
|
||
/* 主容器 */
|
||
.library-container {
|
||
max-width: 1400px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
/* 头部设计 */
|
||
.header {
|
||
background: linear-gradient(135deg, #1e3c72 0%, #2b3b4e 100%);
|
||
border-radius: 32px;
|
||
padding: 20px 28px;
|
||
margin-bottom: 28px;
|
||
color: white;
|
||
box-shadow: 0 10px 25px -5px rgba(0,0,0,0.1);
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
.logo-area h1 {
|
||
font-size: 1.8rem;
|
||
font-weight: 700;
|
||
letter-spacing: -0.3px;
|
||
}
|
||
.logo-area p {
|
||
font-size: 0.85rem;
|
||
opacity: 0.85;
|
||
margin-top: 6px;
|
||
}
|
||
.stats-badge {
|
||
display: flex;
|
||
gap: 24px;
|
||
background: rgba(255,255,255,0.12);
|
||
padding: 12px 24px;
|
||
border-radius: 60px;
|
||
backdrop-filter: blur(4px);
|
||
}
|
||
.stat-item {
|
||
text-align: center;
|
||
}
|
||
.stat-number {
|
||
font-size: 1.5rem;
|
||
font-weight: 800;
|
||
}
|
||
.stat-label {
|
||
font-size: 0.7rem;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1px;
|
||
}
|
||
|
||
/* 工具栏 + 搜索区域 */
|
||
.action-bar {
|
||
background: white;
|
||
border-radius: 28px;
|
||
padding: 16px 24px;
|
||
margin-bottom: 28px;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 16px;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
.search-group {
|
||
display: flex;
|
||
flex: 2;
|
||
min-width: 240px;
|
||
background: #f8fafc;
|
||
border-radius: 60px;
|
||
border: 1px solid #e2e8f0;
|
||
overflow: hidden;
|
||
transition: all 0.2s;
|
||
}
|
||
.search-group:focus-within {
|
||
border-color: #3b82f6;
|
||
box-shadow: 0 0 0 3px rgba(59,130,246,0.2);
|
||
}
|
||
.search-group input {
|
||
flex: 1;
|
||
padding: 12px 20px;
|
||
border: none;
|
||
background: transparent;
|
||
font-size: 0.95rem;
|
||
outline: none;
|
||
}
|
||
.search-group button {
|
||
background: #3b82f6;
|
||
border: none;
|
||
padding: 0 24px;
|
||
color: white;
|
||
cursor: pointer;
|
||
transition: 0.2s;
|
||
font-weight: 500;
|
||
}
|
||
.search-group button:hover {
|
||
background: #2563eb;
|
||
}
|
||
.btn-primary {
|
||
background: #1e293b;
|
||
border: none;
|
||
padding: 10px 24px;
|
||
border-radius: 40px;
|
||
color: white;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
transition: all 0.2s;
|
||
}
|
||
.btn-primary i { font-size: 1rem; }
|
||
.btn-primary:hover {
|
||
background: #0f172a;
|
||
transform: translateY(-1px);
|
||
}
|
||
.btn-outline {
|
||
background: transparent;
|
||
border: 1px solid #cbd5e1;
|
||
padding: 8px 18px;
|
||
border-radius: 40px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: 0.2s;
|
||
}
|
||
.btn-outline:hover {
|
||
background: #f1f5f9;
|
||
border-color: #3b82f6;
|
||
}
|
||
|
||
/* 图书网格展示区 */
|
||
.books-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||
gap: 24px;
|
||
margin-bottom: 40px;
|
||
}
|
||
.book-card {
|
||
background: white;
|
||
border-radius: 28px;
|
||
overflow: hidden;
|
||
transition: all 0.25s ease;
|
||
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
|
||
border: 1px solid #eef2ff;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.book-card:hover {
|
||
transform: translateY(-5px);
|
||
box-shadow: 0 20px 30px -12px rgba(0,0,0,0.15);
|
||
border-color: #cbdffc;
|
||
}
|
||
.book-cover {
|
||
background: linear-gradient(145deg, #e6f0ff, #d9e6fc);
|
||
height: 140px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 3rem;
|
||
color: #2c5282;
|
||
}
|
||
.book-info {
|
||
padding: 20px 18px 18px;
|
||
flex: 1;
|
||
}
|
||
.book-title {
|
||
font-weight: 800;
|
||
font-size: 1.2rem;
|
||
margin-bottom: 8px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
}
|
||
.book-author {
|
||
color: #4b5563;
|
||
font-size: 0.85rem;
|
||
margin-bottom: 12px;
|
||
display: flex;
|
||
gap: 12px;
|
||
flex-wrap: wrap;
|
||
}
|
||
.book-meta {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-top: 12px;
|
||
font-size: 0.8rem;
|
||
border-top: 1px solid #eef2ff;
|
||
padding-top: 12px;
|
||
}
|
||
.status {
|
||
padding: 4px 12px;
|
||
border-radius: 30px;
|
||
font-weight: 600;
|
||
font-size: 0.7rem;
|
||
}
|
||
.status.available {
|
||
background: #dcfce7;
|
||
color: #15803d;
|
||
}
|
||
.status.borrowed {
|
||
background: #fee2e2;
|
||
color: #b91c1c;
|
||
}
|
||
.card-actions {
|
||
display: flex;
|
||
gap: 10px;
|
||
margin-top: 14px;
|
||
}
|
||
.btn-sm {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: 8px 0;
|
||
border-radius: 40px;
|
||
font-size: 0.75rem;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: 0.2s;
|
||
background: #f1f5f9;
|
||
border: none;
|
||
}
|
||
.btn-borrow {
|
||
background: #3b82f6;
|
||
color: white;
|
||
}
|
||
.btn-borrow:hover {
|
||
background: #2563eb;
|
||
}
|
||
.btn-return {
|
||
background: #e2e8f0;
|
||
color: #1e293b;
|
||
}
|
||
.btn-return:hover {
|
||
background: #cbd5e1;
|
||
}
|
||
.btn-delete {
|
||
background: #fff1f0;
|
||
color: #e11d48;
|
||
}
|
||
.btn-delete:hover {
|
||
background: #ffe4e2;
|
||
}
|
||
|
||
/* 模态框 */
|
||
.modal {
|
||
display: none;
|
||
position: fixed;
|
||
top: 0; left: 0;
|
||
width: 100%; height: 100%;
|
||
background: rgba(0,0,0,0.5);
|
||
backdrop-filter: blur(5px);
|
||
justify-content: center;
|
||
align-items: center;
|
||
z-index: 1000;
|
||
}
|
||
.modal-content {
|
||
background: white;
|
||
border-radius: 36px;
|
||
max-width: 500px;
|
||
width: 90%;
|
||
padding: 28px 24px;
|
||
box-shadow: 0 25px 40px rgba(0,0,0,0.2);
|
||
animation: fadeSlideUp 0.2s ease;
|
||
}
|
||
@keyframes fadeSlideUp {
|
||
from { opacity: 0; transform: translateY(20px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
.modal-content h3 {
|
||
font-size: 1.6rem;
|
||
margin-bottom: 20px;
|
||
}
|
||
.modal-content input, .modal-content select {
|
||
width: 100%;
|
||
padding: 12px 16px;
|
||
margin: 12px 0;
|
||
border: 1px solid #cbd5e1;
|
||
border-radius: 28px;
|
||
font-family: inherit;
|
||
outline: none;
|
||
}
|
||
.modal-footer {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 12px;
|
||
margin-top: 20px;
|
||
}
|
||
.empty-message {
|
||
text-align: center;
|
||
padding: 60px 20px;
|
||
background: white;
|
||
border-radius: 40px;
|
||
color: #64748b;
|
||
}
|
||
|
||
footer {
|
||
text-align: center;
|
||
font-size: 0.75rem;
|
||
margin-top: 32px;
|
||
color: #5a6e8a;
|
||
}
|
||
@media (max-width: 680px) {
|
||
.action-bar { flex-direction: column; align-items: stretch; }
|
||
.stats-badge { justify-content: space-between; width: 100%; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="library-container">
|
||
<!-- 头部区域 -->
|
||
<div class="header">
|
||
<div class="logo-area">
|
||
<h1><i class="fas fa-book-open"></i> 墨渊·智慧图书馆系统</h1>
|
||
<p>数字化借阅 | 实时库存 | 一键管理</p>
|
||
</div>
|
||
<div class="stats-badge">
|
||
<div class="stat-item"><div class="stat-number" id="totalBooksStat">0</div><div class="stat-label">馆藏总量</div></div>
|
||
<div class="stat-item"><div class="stat-number" id="availableBooksStat">0</div><div class="stat-label">可借图书</div></div>
|
||
<div class="stat-item"><div class="stat-number" id="borrowedBooksStat">0</div><div class="stat-label">借出中</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 操作栏:搜索 + 新增图书 -->
|
||
<div class="action-bar">
|
||
<div class="search-group">
|
||
<input type="text" id="searchInput" placeholder="按书名、作者或 ISBN 搜索图书...">
|
||
<button id="searchBtn"><i class="fas fa-search"></i> 检索</button>
|
||
</div>
|
||
<div style="display: flex; gap: 12px;">
|
||
<button class="btn-primary" id="addBookBtn"><i class="fas fa-plus-circle"></i> 新书入库</button>
|
||
<button class="btn-outline" id="resetFilterBtn"><i class="fas fa-undo-alt"></i> 全部书目</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 图书动态网格 -->
|
||
<div id="booksGridContainer" class="books-grid">
|
||
<!-- 卡片会通过js动态渲染 -->
|
||
<div class="empty-message"><i class="fas fa-spinner fa-pulse"></i> 加载藏书数据中...</div>
|
||
</div>
|
||
<footer>
|
||
<i class="fas fa-exchange-alt"></i> 支持借阅、归还、删除图书 · 数据实时同步至本地存储
|
||
</footer>
|
||
</div>
|
||
|
||
<!-- 添加 / 编辑图书模态框(复用为添加和编辑) -->
|
||
<div id="bookModal" class="modal">
|
||
<div class="modal-content">
|
||
<h3 id="modalTitle"><i class="fas fa-book"></i> 添加新书</h3>
|
||
<input type="text" id="bookTitle" placeholder="书名 *" autocomplete="off">
|
||
<input type="text" id="bookAuthor" placeholder="作者 *">
|
||
<input type="text" id="bookIsbn" placeholder="ISBN (选填)">
|
||
<select id="bookStatus">
|
||
<option value="available">可借阅</option>
|
||
<option value="borrowed">已借出</option>
|
||
</select>
|
||
<div class="modal-footer">
|
||
<button class="btn-outline" id="closeModalBtn">取消</button>
|
||
<button class="btn-primary" id="saveBookBtn">保存图书</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// ---------- 图书馆核心数据模型 ----------
|
||
let libraryBooks = []; // 存储图书对象
|
||
let currentFilter = ""; // 搜索关键词
|
||
let editingBookId = null; // 编辑模式下记录正在编辑的图书ID
|
||
|
||
// 预置样例数据 (更优雅的初始馆藏)
|
||
const defaultBooks = [
|
||
{ id: "lib_001", title: "深入理解计算机系统", author: "Randal E. Bryant", isbn: "9787111544937", status: "available", borrowCount: 0 },
|
||
{ id: "lib_002", title: "你当像鸟飞往你的山", author: "塔拉·韦斯特弗", isbn: "9787559444110", status: "borrowed", borrowCount: 3 },
|
||
{ id: "lib_003", title: "JavaScript 高级程序设计", author: "Nicholas C. Zakas", isbn: "9787115376257", status: "available", borrowCount: 1 },
|
||
{ id: "lib_004", title: "三体:黑暗森林", author: "刘慈欣", isbn: "9787536692930", status: "available", borrowCount: 5 },
|
||
{ id: "lib_005", title: "Python网络爬虫开发", author: "崔庆才", isbn: "9787115487519", status: "borrowed", borrowCount: 2 },
|
||
{ id: "lib_006", title: "杀死一只知更鸟", author: "哈珀·李", isbn: "9787544242666", status: "available", borrowCount: 1 },
|
||
{ id: "lib_007", title: "设计数据密集型应用", author: "Martin Kleppmann", isbn: "9787115518022", status: "available", borrowCount: 0 }
|
||
];
|
||
|
||
// 辅助函数:生成简短唯一ID (足够使用)
|
||
function generateBookId() {
|
||
return 'bk_' + Date.now() + '_' + Math.random().toString(36).substr(2, 6);
|
||
}
|
||
|
||
// 初始化 / 加载本地存储
|
||
function loadDataFromStorage() {
|
||
const stored = localStorage.getItem("librarySystemData");
|
||
if (stored) {
|
||
try {
|
||
libraryBooks = JSON.parse(stored);
|
||
if (!libraryBooks.length) libraryBooks = [...defaultBooks];
|
||
} catch(e) { libraryBooks = [...defaultBooks]; }
|
||
} else {
|
||
libraryBooks = [...defaultBooks];
|
||
}
|
||
// 确保每个对象有 borrowCount 字段,避免老数据缺失
|
||
libraryBooks = libraryBooks.map(book => ({
|
||
...book,
|
||
borrowCount: book.borrowCount !== undefined ? book.borrowCount : 0
|
||
}));
|
||
saveToLocalStorage();
|
||
}
|
||
|
||
function saveToLocalStorage() {
|
||
localStorage.setItem("librarySystemData", JSON.stringify(libraryBooks));
|
||
}
|
||
|
||
// 更新统计数字
|
||
function updateStats() {
|
||
const total = libraryBooks.length;
|
||
const available = libraryBooks.filter(book => book.status === "available").length;
|
||
const borrowed = total - available;
|
||
document.getElementById("totalBooksStat").innerText = total;
|
||
document.getElementById("availableBooksStat").innerText = available;
|
||
document.getElementById("borrowedBooksStat").innerText = borrowed;
|
||
}
|
||
|
||
// 核心渲染:根据 currentFilter 过滤图书并展示网格
|
||
function renderBooks() {
|
||
const container = document.getElementById("booksGridContainer");
|
||
let filteredBooks = [...libraryBooks];
|
||
|
||
if (currentFilter.trim() !== "") {
|
||
const keyword = currentFilter.trim().toLowerCase();
|
||
filteredBooks = filteredBooks.filter(book =>
|
||
book.title.toLowerCase().includes(keyword) ||
|
||
book.author.toLowerCase().includes(keyword) ||
|
||
(book.isbn && book.isbn.toLowerCase().includes(keyword))
|
||
);
|
||
}
|
||
|
||
if (filteredBooks.length === 0) {
|
||
container.innerHTML = `<div class="empty-message"><i class="fas fa-book-dead"></i> 没有找到相关图书,试试其他关键词或 <button class="btn-outline" id="addFromEmptyBtn" style="margin-left:8px;">+ 添加新书</button></div>`;
|
||
const emptyBtn = document.getElementById("addFromEmptyBtn");
|
||
if (emptyBtn) emptyBtn.addEventListener("click", () => openAddModal());
|
||
updateStats();
|
||
return;
|
||
}
|
||
|
||
// 生成卡片HTML
|
||
let cardsHtml = "";
|
||
filteredBooks.forEach(book => {
|
||
const isAvailable = book.status === "available";
|
||
const statusClass = isAvailable ? "available" : "borrowed";
|
||
const statusText = isAvailable ? "可借阅" : "已借出";
|
||
// 封面图标随标题变化
|
||
const coverIcon = isAvailable ? "fa-book" : "fa-book-open-reader";
|
||
cardsHtml += `
|
||
<div class="book-card" data-id="${book.id}">
|
||
<div class="book-cover">
|
||
<i class="fas ${coverIcon} fa-3x"></i>
|
||
</div>
|
||
<div class="book-info">
|
||
<div class="book-title">
|
||
<span>${escapeHtml(book.title)}</span>
|
||
<span class="status ${statusClass}">${statusText}</span>
|
||
</div>
|
||
<div class="book-author">
|
||
<i class="fas fa-user-pen"></i> ${escapeHtml(book.author)}
|
||
${book.isbn ? `<span style="color:#6c757d;"><i class="fas fa-barcode"></i> ${escapeHtml(book.isbn)}</span>` : ''}
|
||
</div>
|
||
<div class="book-meta">
|
||
<span><i class="fas fa-chart-simple"></i> 借阅次数: ${book.borrowCount || 0}</span>
|
||
<span><i class="fas fa-fingerprint"></i> ID: ${book.id.slice(-6)}</span>
|
||
</div>
|
||
<div class="card-actions">
|
||
${isAvailable ?
|
||
`<button class="btn-sm btn-borrow borrow-action" data-id="${book.id}"><i class="fas fa-hand-peace"></i> 借阅</button>` :
|
||
`<button class="btn-sm btn-return return-action" data-id="${book.id}"><i class="fas fa-undo-alt"></i> 归还</button>`
|
||
}
|
||
<button class="btn-sm btn-outline edit-action" data-id="${book.id}"><i class="fas fa-edit"></i> 编辑</button>
|
||
<button class="btn-sm btn-delete delete-action" data-id="${book.id}"><i class="fas fa-trash-alt"></i> 删除</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
});
|
||
container.innerHTML = cardsHtml;
|
||
|
||
// 绑定卡片内部按钮事件 (事件委托也可,但直接绑定更清晰)
|
||
document.querySelectorAll('.borrow-action').forEach(btn => {
|
||
btn.addEventListener('click', (e) => {
|
||
e.stopPropagation();
|
||
const id = btn.getAttribute('data-id');
|
||
borrowBookById(id);
|
||
});
|
||
});
|
||
document.querySelectorAll('.return-action').forEach(btn => {
|
||
btn.addEventListener('click', (e) => {
|
||
e.stopPropagation();
|
||
const id = btn.getAttribute('data-id');
|
||
returnBookById(id);
|
||
});
|
||
});
|
||
document.querySelectorAll('.edit-action').forEach(btn => {
|
||
btn.addEventListener('click', (e) => {
|
||
e.stopPropagation();
|
||
const id = btn.getAttribute('data-id');
|
||
openEditModal(id);
|
||
});
|
||
});
|
||
document.querySelectorAll('.delete-action').forEach(btn => {
|
||
btn.addEventListener('click', (e) => {
|
||
e.stopPropagation();
|
||
const id = btn.getAttribute('data-id');
|
||
deleteBookById(id);
|
||
});
|
||
});
|
||
updateStats();
|
||
}
|
||
|
||
// 简单防XSS
|
||
function escapeHtml(str) {
|
||
if (!str) return '';
|
||
return str.replace(/[&<>]/g, function(m) {
|
||
if (m === '&') return '&';
|
||
if (m === '<') return '<';
|
||
if (m === '>') return '>';
|
||
return m;
|
||
}).replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, function(c) {
|
||
return c;
|
||
});
|
||
}
|
||
|
||
// 借阅逻辑
|
||
function borrowBookById(id) {
|
||
const book = libraryBooks.find(b => b.id === id);
|
||
if (!book) return;
|
||
if (book.status === 'borrowed') {
|
||
alert("❌ 这本书已被借出,无法重复借阅。");
|
||
return;
|
||
}
|
||
book.status = 'borrowed';
|
||
book.borrowCount = (book.borrowCount || 0) + 1;
|
||
saveToLocalStorage();
|
||
renderBooks(); // 刷新视图
|
||
showToastMessage(`📖 成功借阅《${book.title}》,请按时归还。`);
|
||
}
|
||
|
||
// 归还逻辑
|
||
function returnBookById(id) {
|
||
const book = libraryBooks.find(b => b.id === id);
|
||
if (!book) return;
|
||
if (book.status === 'available') {
|
||
alert("⚠️ 这本书当前可借阅状态,无需归还。");
|
||
return;
|
||
}
|
||
book.status = 'available';
|
||
saveToLocalStorage();
|
||
renderBooks();
|
||
showToastMessage(`✅ 归还成功!《${book.title}》已恢复在库。`);
|
||
}
|
||
|
||
// 删除图书 (确认)
|
||
function deleteBookById(id) {
|
||
const book = libraryBooks.find(b => b.id === id);
|
||
if (!book) return;
|
||
const confirmDel = confirm(`确定要永久删除《${book.title}》吗?此操作不可恢复。`);
|
||
if (confirmDel) {
|
||
libraryBooks = libraryBooks.filter(b => b.id !== id);
|
||
saveToLocalStorage();
|
||
renderBooks();
|
||
showToastMessage(`🗑️ 已删除图书《${book.title}》`);
|
||
}
|
||
}
|
||
|
||
// 新增/保存图书 (添加模式 or 编辑模式)
|
||
function saveBookFromModal() {
|
||
const title = document.getElementById("bookTitle").value.trim();
|
||
const author = document.getElementById("bookAuthor").value.trim();
|
||
const isbn = document.getElementById("bookIsbn").value.trim();
|
||
const status = document.getElementById("bookStatus").value;
|
||
|
||
if (!title || !author) {
|
||
alert("请填写完整书名和作者信息");
|
||
return;
|
||
}
|
||
|
||
if (editingBookId) {
|
||
// 编辑模式
|
||
const existingBook = libraryBooks.find(b => b.id === editingBookId);
|
||
if (existingBook) {
|
||
existingBook.title = title;
|
||
existingBook.author = author;
|
||
existingBook.isbn = isbn || "";
|
||
existingBook.status = status;
|
||
saveToLocalStorage();
|
||
showToastMessage(`✏️ 图书信息已更新`);
|
||
}
|
||
} else {
|
||
// 新增模式:检查重复ISBN(可选提示) 但不强制
|
||
const newId = generateBookId();
|
||
const newBook = {
|
||
id: newId,
|
||
title: title,
|
||
author: author,
|
||
isbn: isbn || "",
|
||
status: status,
|
||
borrowCount: 0
|
||
};
|
||
libraryBooks.push(newBook);
|
||
saveToLocalStorage();
|
||
showToastMessage(`📚 新书《${title}》已加入馆藏。`);
|
||
}
|
||
closeModal();
|
||
renderBooks();
|
||
}
|
||
|
||
// 打开新增模态框
|
||
function openAddModal() {
|
||
editingBookId = null;
|
||
document.getElementById("modalTitle").innerHTML = '<i class="fas fa-plus-circle"></i> 添加新书';
|
||
document.getElementById("bookTitle").value = "";
|
||
document.getElementById("bookAuthor").value = "";
|
||
document.getElementById("bookIsbn").value = "";
|
||
document.getElementById("bookStatus").value = "available";
|
||
document.getElementById("bookModal").style.display = "flex";
|
||
}
|
||
|
||
// 打开编辑模态框并填充数据
|
||
function openEditModal(id) {
|
||
const book = libraryBooks.find(b => b.id === id);
|
||
if (!book) return;
|
||
editingBookId = id;
|
||
document.getElementById("modalTitle").innerHTML = '<i class="fas fa-pen-fancy"></i> 编辑图书';
|
||
document.getElementById("bookTitle").value = book.title;
|
||
document.getElementById("bookAuthor").value = book.author;
|
||
document.getElementById("bookIsbn").value = book.isbn || "";
|
||
document.getElementById("bookStatus").value = book.status;
|
||
document.getElementById("bookModal").style.display = "flex";
|
||
}
|
||
|
||
function closeModal() {
|
||
document.getElementById("bookModal").style.display = "none";
|
||
editingBookId = null;
|
||
}
|
||
|
||
// 简陋的toast (临时div)
|
||
let toastTimeout = null;
|
||
function showToastMessage(msg) {
|
||
let toast = document.getElementById("dynamicToast");
|
||
if (!toast) {
|
||
toast = document.createElement("div");
|
||
toast.id = "dynamicToast";
|
||
toast.style.position = "fixed";
|
||
toast.style.bottom = "30px";
|
||
toast.style.left = "50%";
|
||
toast.style.transform = "translateX(-50%)";
|
||
toast.style.backgroundColor = "#1e293b";
|
||
toast.style.color = "#fff";
|
||
toast.style.padding = "12px 24px";
|
||
toast.style.borderRadius = "60px";
|
||
toast.style.fontSize = "0.9rem";
|
||
toast.style.fontWeight = "500";
|
||
toast.style.zIndex = "2000";
|
||
toast.style.boxShadow = "0 6px 14px rgba(0,0,0,0.2)";
|
||
toast.style.backdropFilter = "blur(8px)";
|
||
toast.style.border = "1px solid #4f7eb3";
|
||
document.body.appendChild(toast);
|
||
}
|
||
toast.innerHTML = `<i class="fas fa-info-circle"></i> ${msg}`;
|
||
toast.style.opacity = "1";
|
||
if (toastTimeout) clearTimeout(toastTimeout);
|
||
toastTimeout = setTimeout(() => {
|
||
toast.style.opacity = "0";
|
||
setTimeout(() => { if(toast) toast.style.display = "none"; }, 300);
|
||
}, 2500);
|
||
toast.style.display = "block";
|
||
}
|
||
|
||
// 搜索逻辑
|
||
function performSearch() {
|
||
const inputVal = document.getElementById("searchInput").value;
|
||
currentFilter = inputVal;
|
||
renderBooks();
|
||
}
|
||
|
||
function resetFilter() {
|
||
document.getElementById("searchInput").value = "";
|
||
currentFilter = "";
|
||
renderBooks();
|
||
}
|
||
|
||
// 事件绑定与初始化
|
||
function bindEvents() {
|
||
document.getElementById("searchBtn").addEventListener("click", performSearch);
|
||
document.getElementById("resetFilterBtn").addEventListener("click", resetFilter);
|
||
document.getElementById("addBookBtn").addEventListener("click", openAddModal);
|
||
document.getElementById("closeModalBtn").addEventListener("click", closeModal);
|
||
document.getElementById("saveBookBtn").addEventListener("click", saveBookFromModal);
|
||
// 点击模态框背景关闭
|
||
window.addEventListener("click", (e) => {
|
||
const modal = document.getElementById("bookModal");
|
||
if (e.target === modal) closeModal();
|
||
});
|
||
// 搜索框支持回车
|
||
document.getElementById("searchInput").addEventListener("keypress", (e) => {
|
||
if (e.key === "Enter") performSearch();
|
||
});
|
||
}
|
||
|
||
// 启动
|
||
function init() {
|
||
loadDataFromStorage();
|
||
bindEvents();
|
||
renderBooks();
|
||
}
|
||
|
||
init();
|
||
</script>
|
||
</body>
|
||
</html> |