Files
PerToolBoxFront/pages/notes.php
2026-03-31 15:54:32 +08:00

157 lines
5.6 KiB
PHP

<?php
/**
* PerToolBox Front - 便签本页面
*
* Copyright (C) 2024 Sea Network Technology Studio
* Author: Canglan <admin@sea-studio.top>
* License: AGPL v3
*/
require_once '../config.php';
include_once '../header.php';
include_once '../sidebar.php';
?>
<div class="main-content">
<div class="card">
<div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold">📝 便签本</h1>
<button id="addBtn" class="btn btn-primary">+ 新建便签</button>
</div>
<!-- 便签网格 -->
<div id="noteGrid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="text-center text-gray-400 py-8 col-span-full">加载中...</div>
</div>
</div>
</div>
<!-- 添加/编辑模态框 -->
<div id="modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
<div class="bg-white rounded-lg w-full max-w-lg p-6">
<h3 id="modalTitle" class="text-xl font-bold mb-4">新建便签</h3>
<input type="text" id="noteTitle" placeholder="标题" class="form-input mb-3">
<textarea id="noteContent" rows="6" placeholder="内容" class="form-input mb-3"></textarea>
<input type="text" id="noteTags" placeholder="标签(用逗号分隔)" class="form-input mb-4">
<div class="flex gap-2">
<button id="modalConfirm" class="btn btn-primary flex-1">保存</button>
<button id="modalCancel" class="btn btn-secondary flex-1">取消</button>
</div>
</div>
</div>
<script>
let currentEditId = null;
async function loadNotes() {
try {
const notes = await apiRequest('/notes');
renderNotes(notes);
} catch (error) {
document.getElementById('noteGrid').innerHTML = `<div class="text-center text-red-500 py-8 col-span-full">${error.message}</div>`;
}
}
function renderNotes(notes) {
if (!notes.length) {
document.getElementById('noteGrid').innerHTML = '<div class="text-center text-gray-400 py-8 col-span-full">暂无便签,新建一个吧~</div>';
return;
}
const html = notes.map(note => `
<div class="border rounded-lg p-4 hover:shadow-md transition bg-white">
<div class="flex justify-between items-start mb-2">
<h3 class="font-bold text-lg">${escapeHtml(note.title)}</h3>
<div class="flex gap-1">
<button onclick="editNote(${note.id})" class="text-blue-500 hover:text-blue-700">✏️</button>
<button onclick="deleteNote(${note.id})" class="text-red-500 hover:text-red-700">🗑️</button>
</div>
</div>
<p class="text-gray-600 whitespace-pre-wrap text-sm">${escapeHtml(note.content || '').substring(0, 200)}${(note.content || '').length > 200 ? '...' : ''}</p>
${note.tags && note.tags.length ? `<div class="mt-2 flex gap-1 flex-wrap">${note.tags.map(t => `<span class="text-xs bg-gray-100 px-2 py-0.5 rounded">${escapeHtml(t)}</span>`).join('')}</div>` : ''}
<div class="mt-2 text-xs text-gray-400">${new Date(note.created_at).toLocaleString()}</div>
</div>
`).join('');
document.getElementById('noteGrid').innerHTML = html;
}
function openModal(editId = null) {
currentEditId = editId;
const modal = document.getElementById('modal');
const title = document.getElementById('modalTitle');
if (editId) {
title.textContent = '编辑便签';
apiRequest(`/notes/${editId}`).then(note => {
document.getElementById('noteTitle').value = note.title;
document.getElementById('noteContent').value = note.content || '';
document.getElementById('noteTags').value = (note.tags || []).join(',');
});
} else {
title.textContent = '新建便签';
document.getElementById('noteTitle').value = '';
document.getElementById('noteContent').value = '';
document.getElementById('noteTags').value = '';
}
modal.classList.remove('hidden');
modal.classList.add('flex');
}
function closeModal() {
const modal = document.getElementById('modal');
modal.classList.add('hidden');
modal.classList.remove('flex');
currentEditId = null;
}
async function saveNote() {
const data = {
title: document.getElementById('noteTitle').value.trim(),
content: document.getElementById('noteContent').value,
tags: document.getElementById('noteTags').value.split(',').map(t => t.trim()).filter(t => t)
};
if (!data.title) {
showToast('请输入标题', 'error');
return;
}
try {
if (currentEditId) {
await apiRequest(`/notes/${currentEditId}`, {
method: 'PUT',
body: JSON.stringify(data)
});
} else {
await apiRequest('/notes', {
method: 'POST',
body: JSON.stringify(data)
});
}
closeModal();
loadNotes();
} catch (error) {
showToast(error.message, 'error');
}
}
async function deleteNote(id) {
if (!confirm('确定删除吗?')) return;
try {
await apiRequest(`/notes/${id}`, { method: 'DELETE' });
loadNotes();
} catch (error) {
showToast(error.message, 'error');
}
}
document.getElementById('addBtn').addEventListener('click', () => openModal());
document.getElementById('modalConfirm').addEventListener('click', saveNote);
document.getElementById('modalCancel').addEventListener('click', closeModal);
recordUsage('notes');
loadNotes();
</script>
<?php include_once '../footer.php'; ?>