feat: 完善本地书库与发布更新流程

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
lofyer
2026-07-28 21:55:16 +08:00
co-authored by factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
parent de3e1d8a44
commit b8c8d24107
22 changed files with 1579 additions and 359 deletions
+29 -7
View File
@@ -13,9 +13,21 @@ const Library = (() => {
grid = $('libGrid');
statusEl = $('libStatus');
$('addLocalBtn').onclick = addLocal;
$('rescanBtn').onclick = rescan;
window.api.library.onChanged(() => { dirty = true; refresh(true); });
}
async function rescan() {
const btn = $('rescanBtn');
btn.disabled = true;
btn.textContent = '扫描中...';
const r = await window.api.library.scan();
btn.textContent = r.ok && r.data && r.data.added ? `新增 ${r.data.added} 本 ✓` : '已是最新 ✓';
dirty = true;
await refresh(true);
setTimeout(() => { btn.textContent = '重新扫描'; btn.disabled = false; }, 2000);
}
function getSortMode() { return sortMode; }
function setSortMode(m) {
sortMode = m;
@@ -30,16 +42,20 @@ const Library = (() => {
dirty = false;
if (!res.ok) { statusEl.textContent = '加载失败:' + res.error; return; }
const items = res.data.slice().sort(SORTERS[sortMode] || SORTERS.added);
statusEl.textContent = `${items.length}`;
const missingCount = items.filter((it) => it.missing).length;
statusEl.textContent = `${items.length}` + (missingCount ? `${missingCount} 条文件缺失` : '');
if (!items.length) {
grid.innerHTML = '<div class="empty">书库为空,去「检索」页添加文献 / 图书吧</div>';
return;
}
grid.innerHTML = items.map((it) => {
const hasFile = (it.files || []).some((f) => f.path);
const badge = hasFile
// exists 由主进程按实际磁盘状态给出:文件被手动删掉时要如实反映
const openable = (it.files || []).some((f) => f.exists);
const badge = openable
? '<span class="card-badge">已下载</span>'
: '<span class="card-badge miss">未下载</span>';
: ((it.files || []).length
? '<span class="card-badge miss">文件缺失</span>'
: '<span class="card-badge miss">未下载</span>');
return `
<div class="card" data-id="${escapeHtml(it.id)}">
<div class="card-cover" style="${coverStyle(it.cover)}">${it.cover ? '' : `<div class="ph">${escapeHtml(it.title)}</div>`}</div>
@@ -47,7 +63,8 @@ const Library = (() => {
${(it.authors && it.authors.length) ? `<div class="card-sub">${escapeHtml(it.authors.slice(0, 2).join(', '))}</div>` : ''}
${badge}
<div class="lib-card-actions">
<button class="open-btn" data-act="open" ${hasFile ? '' : 'disabled'}>打开</button>
<button class="open-btn" data-act="open" ${openable ? '' : 'disabled'}>打开</button>
${openable ? '<button data-act="reveal">定位</button>' : ''}
${it.url ? '<button data-act="page">页面</button>' : ''}
<button data-act="remove">移除</button>
</div>
@@ -67,8 +84,13 @@ const Library = (() => {
if (!res.ok || !res.data) return;
const it = res.data;
if (act === 'open') {
const f = (it.files || []).find((x) => x.path);
if (f) window.api.openPath(f.path);
const f = (it.files || []).find((x) => x.exists) || (it.files || [])[0];
if (!f) return;
const r = await window.api.openPath(f.path);
if (!r.ok) await confirmModal('打开失败', r.error || '无法打开该文件');
} else if (act === 'reveal') {
const f = (it.files || []).find((x) => x.exists);
if (f) window.api.showItem(f.path);
} else if (act === 'page') {
if (it.url) window.api.openExternal(it.url);
} else if (act === 'remove') {