feat: 新增草稿箱自动保存功能
- 编辑时自动保存到 localStorage(每5秒或输入后2秒) - 打开添加弹框时检查是否有草稿并提示恢复 - 成功添加后清除草稿 - 弹框标题显示“已自动保存”指示器
This commit is contained in:
@@ -848,6 +848,7 @@ INDEX_TEMPLATE = '''
|
||||
<h5 class="modal-title">
|
||||
<span id="addModalIcon"></span>
|
||||
<span id="addModalTitle">添加条目</span>
|
||||
<span id="draftIndicator" class="badge bg-secondary ms-2" style="display:none;">已自动保存</span>
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
@@ -1531,6 +1532,129 @@ function changeSort() {
|
||||
// ============ 添加功能 ============
|
||||
|
||||
// 快捷添加按钮
|
||||
// ============ 草稿箱 ============
|
||||
|
||||
const DRAFT_KEY = 'xian_favor_draft';
|
||||
let draftTimer = null;
|
||||
|
||||
// 保存草稿到 localStorage
|
||||
function saveDraft() {
|
||||
const type = document.getElementById('addType')?.value || 'text';
|
||||
const draft = {
|
||||
type,
|
||||
title: document.getElementById('addTitle')?.value || '',
|
||||
content: document.getElementById('addContent')?.value || '',
|
||||
url: document.getElementById('addUrl')?.value || '',
|
||||
source: document.getElementById('addSource')?.value || '',
|
||||
status: document.getElementById('addStatus')?.value || 'pending',
|
||||
priority: document.getElementById('addPriority')?.value || 'medium',
|
||||
due_date: document.getElementById('addDueDate')?.value || '',
|
||||
note: document.getElementById('addNote')?.value || '',
|
||||
tags: document.getElementById('addTags')?.value || '',
|
||||
is_starred: document.getElementById('addStarred')?.checked || false,
|
||||
saved_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
// 只在有内容时保存
|
||||
if (draft.title || draft.content || draft.url || draft.note) {
|
||||
localStorage.setItem(DRAFT_KEY, JSON.stringify(draft));
|
||||
showDraftIndicator();
|
||||
}
|
||||
}
|
||||
|
||||
// 加载草稿
|
||||
function loadDraft() {
|
||||
const draft = localStorage.getItem(DRAFT_KEY);
|
||||
if (!draft) return null;
|
||||
return JSON.parse(draft);
|
||||
}
|
||||
|
||||
// 清除草稿
|
||||
function clearDraft() {
|
||||
localStorage.removeItem(DRAFT_KEY);
|
||||
hideDraftIndicator();
|
||||
}
|
||||
|
||||
// 显示草稿指示器
|
||||
function showDraftIndicator() {
|
||||
const indicator = document.getElementById('draftIndicator');
|
||||
if (indicator) indicator.style.display = 'inline';
|
||||
}
|
||||
|
||||
// 隐藏草稿指示器
|
||||
function hideDraftIndicator() {
|
||||
const indicator = document.getElementById('draftIndicator');
|
||||
if (indicator) indicator.style.display = 'none';
|
||||
}
|
||||
|
||||
// 检查是否有草稿并提示恢复
|
||||
function checkAndRestoreDraft(type) {
|
||||
const draft = loadDraft();
|
||||
if (!draft) return false;
|
||||
|
||||
// 草稿类型匹配才提示
|
||||
if (draft.type === type && (draft.title || draft.content || draft.url || draft.note)) {
|
||||
const savedTime = new Date(draft.saved_at).toLocaleString('zh-CN');
|
||||
if (confirm(`发现未保存的草稿(${savedTime}),是否恢复?`)) {
|
||||
restoreDraftToForm(draft);
|
||||
return true;
|
||||
} else {
|
||||
clearDraft(); // 用户选择不恢复,清除草稿
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 将草稿恢复到表单
|
||||
function restoreDraftToForm(draft) {
|
||||
document.getElementById('addTitle').value = draft.title || '';
|
||||
if (draft.type === 'text') {
|
||||
document.getElementById('addContent').value = draft.content || '';
|
||||
}
|
||||
if (['link', 'column'].includes(draft.type)) {
|
||||
document.getElementById('addUrl').value = draft.url || '';
|
||||
}
|
||||
if (draft.type === 'column') {
|
||||
document.getElementById('addSource').value = draft.source || '';
|
||||
}
|
||||
if (draft.type === 'todo') {
|
||||
document.getElementById('addStatus').value = draft.status || 'pending';
|
||||
document.getElementById('addPriority').value = draft.priority || 'medium';
|
||||
document.getElementById('addDueDate').value = draft.due_date || '';
|
||||
}
|
||||
document.getElementById('addNote').value = draft.note || '';
|
||||
document.getElementById('addTags').value = draft.tags || '';
|
||||
document.getElementById('addStarred').checked = draft.is_starred || false;
|
||||
showDraftIndicator();
|
||||
}
|
||||
|
||||
// 启动自动保存
|
||||
function startAutoSave() {
|
||||
// 每5秒自动保存一次
|
||||
draftTimer = setInterval(saveDraft, 5000);
|
||||
|
||||
// 监听输入事件立即保存(带延迟)
|
||||
const form = document.getElementById('addForm');
|
||||
if (form) {
|
||||
form.addEventListener('input', () => {
|
||||
clearTimeout(draftTimer);
|
||||
draftTimer = setTimeout(saveDraft, 2000); // 输入后2秒保存
|
||||
// 重新启动定时保存
|
||||
clearInterval(draftTimer);
|
||||
draftTimer = setInterval(saveDraft, 5000);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 停止自动保存
|
||||
function stopAutoSave() {
|
||||
if (draftTimer) {
|
||||
clearInterval(draftTimer);
|
||||
clearTimeout(draftTimer);
|
||||
draftTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function showAddModal(type) {
|
||||
// 设置类型
|
||||
document.getElementById('addType').value = type;
|
||||
@@ -1555,8 +1679,19 @@ function showAddModal(type) {
|
||||
// 清空表单
|
||||
document.getElementById('addForm').reset();
|
||||
|
||||
// 检查是否有草稿
|
||||
const restored = checkAndRestoreDraft(type);
|
||||
|
||||
// 打开弹窗
|
||||
new bootstrap.Modal(document.getElementById('addModal')).show();
|
||||
|
||||
// 启动自动保存
|
||||
startAutoSave();
|
||||
|
||||
// 弹框关闭时停止自动保存
|
||||
document.getElementById('addModal').addEventListener('hidden.bs.modal', () => {
|
||||
stopAutoSave();
|
||||
}, { once: true });
|
||||
}
|
||||
|
||||
// 添加条目
|
||||
@@ -1585,6 +1720,8 @@ async function addItem() {
|
||||
if (res.ok) {
|
||||
bootstrap.Modal.getInstance(document.getElementById('addModal')).hide();
|
||||
document.getElementById('addForm').reset();
|
||||
clearDraft(); // 成功添加后清除草稿
|
||||
stopAutoSave(); // 停止自动保存
|
||||
refreshData();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user