fix: 修复收藏夹列表加载失败 + 收藏按钮改为弹窗选择收藏夹

- settings/favorites 收藏夹列表加载失败:escapeHTML 在 energy Tab 闭包内,收藏夹 Tab 无法访问导致 ReferenceError,修复为本地 esc() 函数
- settings 创建/删除收藏夹请求缺少 CSRF Token(403),统一使用 fetch + X-CSRF-Token 头
- API 错误时显示实际错误信息而非吞掉显示"暂无收藏夹"
- 文章详情页收藏按钮:点击弹出选择收藏夹弹窗,列出所有收藏夹,已收藏的文章高亮显示
- 弹窗支持原地新建收藏夹并立即收藏(一键创建+收藏)
- 新增收藏夹弹窗 CSS 样式(.fav-overlay/.fav-modal/.fav-folder-option 等)
This commit is contained in:
2026-06-01 17:58:35 +08:00
parent 4d212a8f8a
commit ae98d33edf
3 changed files with 464 additions and 65 deletions

View File

@ -1032,11 +1032,31 @@
var createBtn = document.getElementById('createFolderBtn');
if (!listEl) return;
var csrfMeta = document.querySelector('meta[name="csrf-token"]');
var csrfToken = csrfMeta ? csrfMeta.getAttribute('content') : '';
function esc(s) {
var d = document.createElement('div');
d.textContent = s || '';
return d.innerHTML;
}
function api(method, url, data) {
var opts = { method: method, headers: { 'Content-Type': 'application/json' } };
if (csrfToken) opts.headers['X-CSRF-Token'] = csrfToken;
if (data) opts.body = JSON.stringify(data);
return fetch(url, opts).then(function (r) { return r.json(); });
}
function loadFolders() {
fetch('/api/folders')
.then(function (r) { return r.json(); })
listEl.innerHTML = '<div class="energy-log-empty">加载中...</div>';
api('GET', '/api/folders')
.then(function (d) {
if (!d.success || !d.data || !d.data.folders || d.data.folders.length === 0) {
if (!d.success) {
listEl.innerHTML = '<div class="energy-log-empty">' + esc(d.message || '加载失败') + '</div>';
return;
}
if (!d.data || !d.data.folders || d.data.folders.length === 0) {
listEl.innerHTML = '<div class="energy-log-empty">暂无收藏夹</div>';
return;
}
@ -1046,7 +1066,7 @@
var pub = f.is_public ? '公开' : '私密';
html += '<div class="fav-folder-card">'
+ '<div class="fav-folder-info">'
+ '<span class="fav-folder-name">' + escapeHTML(f.name) + isDefault + '</span>'
+ '<span class="fav-folder-name">' + esc(f.name) + isDefault + '</span>'
+ '<span class="fav-folder-meta">' + (f.item_count || 0) + ' 篇文章 · ' + pub + '</span>'
+ '</div>'
+ '<div class="fav-folder-actions">'
@ -1056,33 +1076,27 @@
});
listEl.innerHTML = html;
// 绑定删除按钮
listEl.querySelectorAll('.fav-folder-del').forEach(function (btn) {
btn.addEventListener('click', function () {
var id = btn.getAttribute('data-id');
if (!confirm('确定要删除此收藏夹?其中的收藏内容也将被清除。')) return;
var xhr = new XMLHttpRequest();
xhr.open('DELETE', '/api/folders/' + id, true);
xhr.onreadystatechange = function () {
if (xhr.readyState !== 4) return;
try {
var r = JSON.parse(xhr.responseText);
api('DELETE', '/api/folders/' + id)
.then(function (r) {
if (r.success) {
showSettingsToast('收藏夹已删除');
loadFolders();
} else {
showSettingsToast(r.message || '删除失败', true);
}
} catch (e) {
})
.catch(function () {
showSettingsToast('删除失败', true);
}
};
xhr.send();
});
});
});
})
.catch(function () {
listEl.innerHTML = '<div class="energy-log-empty">加载失败</div>';
listEl.innerHTML = '<div class="energy-log-empty">加载失败,请刷新页面重试</div>';
});
}
@ -1094,24 +1108,18 @@
showSettingsToast('收藏夹名称不能超过 50 个字符', true);
return;
}
var xhr = new XMLHttpRequest();
xhr.open('POST', '/api/folders', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onreadystatechange = function () {
if (xhr.readyState !== 4) return;
try {
var r = JSON.parse(xhr.responseText);
api('POST', '/api/folders', { name: name.trim(), description: '', is_public: false })
.then(function (r) {
if (r.success) {
showSettingsToast('收藏夹已创建');
loadFolders();
} else {
showSettingsToast(r.message || '创建失败', true);
}
} catch (e) {
})
.catch(function () {
showSettingsToast('创建失败', true);
}
};
xhr.send(JSON.stringify({ name: name.trim(), description: '', is_public: false }));
});
});
}