feat: 完善本地书库与发布更新流程
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
parent
de3e1d8a44
commit
b8c8d24107
+76
-35
@@ -12,6 +12,8 @@ const Browse = (() => {
|
||||
scrollY: 0,
|
||||
sourceList: [],
|
||||
aggToken: 0,
|
||||
gridToken: 0,
|
||||
detailToken: 0,
|
||||
activeSourceId: null,
|
||||
currentPostId: null,
|
||||
currentDetail: null
|
||||
@@ -127,6 +129,7 @@ const Browse = (() => {
|
||||
}
|
||||
|
||||
async function loadGrid() {
|
||||
const token = ++state.gridToken;
|
||||
state.aggToken++;
|
||||
showGrid();
|
||||
statusBar.textContent = '加载中...';
|
||||
@@ -136,14 +139,19 @@ const Browse = (() => {
|
||||
|
||||
if (isAgg()) return loadAggregate();
|
||||
|
||||
const res = state.mode === 'search'
|
||||
? await window.api.sources.search(state.sourceId, state.keyword, state.page)
|
||||
: await window.api.sources.browse(state.sourceId, state.page);
|
||||
const sourceId = state.sourceId;
|
||||
const mode = state.mode;
|
||||
const keyword = state.keyword;
|
||||
const page = state.page;
|
||||
const res = mode === 'search'
|
||||
? await window.api.sources.search(sourceId, keyword, page)
|
||||
: await window.api.sources.browse(sourceId, page);
|
||||
if (token !== state.gridToken) return;
|
||||
|
||||
grid.className = 'grid';
|
||||
|
||||
if (!res.ok) {
|
||||
const isAuth = state.sourceId === 'zlib' && /登录|登录|AUTH/i.test(res.error || '');
|
||||
const isAuth = sourceId === 'zlib' && /登录|AUTH/i.test(res.error || '');
|
||||
statusBar.innerHTML = `加载失败:${escapeHtml(res.error)} <button class="retry-btn" id="gridRetry">重试</button>`;
|
||||
if (isAuth) {
|
||||
grid.innerHTML = '<div class="empty">Z-Library 需要登录,请到"设置"页配置账号</div>';
|
||||
@@ -158,23 +166,23 @@ const Browse = (() => {
|
||||
state.maxPage = maxPage || 1;
|
||||
|
||||
if (!items.length) {
|
||||
grid.innerHTML = '<div class="empty">未找到相关结果</div>';
|
||||
statusBar.textContent = state.mode === 'search' ? `搜索:${escapeHtml(state.keyword)}` : '';
|
||||
grid.innerHTML = `<div class="empty">${escapeHtml(res.data.note || '未找到相关结果')}</div>`;
|
||||
statusBar.textContent = mode === 'search' ? `搜索:${keyword}` : '';
|
||||
return;
|
||||
}
|
||||
|
||||
statusBar.textContent = state.mode === 'search' ? `搜索:${escapeHtml(state.keyword)}(第 ${state.page} 页)` : '';
|
||||
statusBar.textContent = mode === 'search' ? `搜索:${keyword}(第 ${page} 页)` : '';
|
||||
|
||||
grid.innerHTML = items.map(cardHtml).join('');
|
||||
bindCards(grid, state.sourceId);
|
||||
bindCards(grid, sourceId);
|
||||
|
||||
$('pageInfo').textContent = `第 ${state.page} / ${state.maxPage} 页`;
|
||||
$('prevBtn').disabled = state.page <= 1;
|
||||
$('nextBtn').disabled = state.page >= state.maxPage;
|
||||
$('pageInfo').textContent = `第 ${page} / ${state.maxPage} 页`;
|
||||
$('prevBtn').disabled = page <= 1;
|
||||
$('nextBtn').disabled = page >= state.maxPage;
|
||||
const jump = $('jumpInput');
|
||||
jump.max = state.maxPage;
|
||||
jump.value = '';
|
||||
jump.placeholder = state.page;
|
||||
jump.placeholder = page;
|
||||
pager.classList.remove('hidden');
|
||||
}
|
||||
|
||||
@@ -280,21 +288,25 @@ const Browse = (() => {
|
||||
}
|
||||
|
||||
function showGrid() {
|
||||
state.detailToken++;
|
||||
detailView.classList.add('hidden');
|
||||
gridView.classList.remove('hidden');
|
||||
if (state.scrollY) mainEl.scrollTop = state.scrollY;
|
||||
}
|
||||
|
||||
async function openDetail(postId, sourceId) {
|
||||
const token = ++state.detailToken;
|
||||
state.scrollY = mainEl.scrollTop;
|
||||
state.currentPostId = postId;
|
||||
state.activeSourceId = sourceId || state.sourceId;
|
||||
const activeSourceId = sourceId || state.sourceId;
|
||||
state.activeSourceId = activeSourceId;
|
||||
gridView.classList.add('hidden');
|
||||
detailView.classList.remove('hidden');
|
||||
mainEl.scrollTop = 0;
|
||||
detailContent.innerHTML = '<div class="dl-loading">加载中...</div>';
|
||||
|
||||
const res = await window.api.sources.detail(state.activeSourceId, postId);
|
||||
const res = await window.api.sources.detail(activeSourceId, postId);
|
||||
if (token !== state.detailToken) return;
|
||||
if (!res.ok) {
|
||||
detailContent.innerHTML = `<div class="dl-error">加载失败:${escapeHtml(res.error)}<button class="retry-btn" id="detailRetry">重试</button></div>`;
|
||||
$('detailRetry').onclick = () => openDetail(postId, state.activeSourceId);
|
||||
@@ -302,7 +314,7 @@ const Browse = (() => {
|
||||
}
|
||||
state.currentDetail = res.data;
|
||||
renderDetail(res.data);
|
||||
loadDownload(postId);
|
||||
loadDownload(postId, activeSourceId, token);
|
||||
refreshAddButton();
|
||||
}
|
||||
|
||||
@@ -358,16 +370,7 @@ const Browse = (() => {
|
||||
async function addToLibrary() {
|
||||
const d = state.currentDetail;
|
||||
if (!d) return;
|
||||
const res = await window.api.library.add({
|
||||
title: d.title,
|
||||
authors: d.authors || [],
|
||||
cover: d.cover,
|
||||
date: d.date || '',
|
||||
brief: d.brief || '',
|
||||
url: d.url || '',
|
||||
sourceId: state.activeSourceId,
|
||||
sourcePostId: state.currentPostId
|
||||
});
|
||||
const res = await window.api.library.add(entryMeta());
|
||||
if (res.ok) {
|
||||
const btn = $('addLibBtn');
|
||||
btn.textContent = '已加入书库 ✓';
|
||||
@@ -377,15 +380,15 @@ const Browse = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDownload(postId) {
|
||||
async function loadDownload(postId, sourceId = state.activeSourceId, token = state.detailToken) {
|
||||
const box = $('downloadBox');
|
||||
if (!box) return;
|
||||
box.innerHTML = '<div class="dl-loading">下载信息获取中...</div>';
|
||||
const res = await window.api.sources.download(state.activeSourceId, postId);
|
||||
if (!box.isConnected) return;
|
||||
const res = await window.api.sources.download(sourceId, postId);
|
||||
if (!box.isConnected || token !== state.detailToken) return;
|
||||
if (!res.ok) {
|
||||
box.innerHTML = `<div class="dl-error">获取失败:${escapeHtml(res.error)}<button class="retry-btn" id="dlRetry">重试</button></div>`;
|
||||
$('dlRetry').onclick = () => loadDownload(postId);
|
||||
$('dlRetry').onclick = () => loadDownload(postId, sourceId, token);
|
||||
return;
|
||||
}
|
||||
const d = res.data;
|
||||
@@ -409,24 +412,62 @@ const Browse = (() => {
|
||||
box.querySelectorAll('[data-open]').forEach((a) => { a.onclick = (e) => { e.preventDefault(); window.api.openExternal(a.dataset.open); }; });
|
||||
}
|
||||
|
||||
// 当前条目的元数据,供下载时自动建库用
|
||||
function entryMeta() {
|
||||
const d = state.currentDetail || {};
|
||||
return {
|
||||
title: d.title || '未命名',
|
||||
authors: d.authors || [],
|
||||
cover: d.cover || '',
|
||||
date: d.date || '',
|
||||
brief: d.brief || '',
|
||||
url: d.url || '',
|
||||
sourceId: state.activeSourceId,
|
||||
sourcePostId: state.currentPostId
|
||||
};
|
||||
}
|
||||
|
||||
async function downloadFile(btn, url) {
|
||||
const orig = btn.textContent;
|
||||
btn.disabled = true;
|
||||
btn.textContent = '下载中...';
|
||||
|
||||
const lib = await window.api.library.findBySource(state.activeSourceId, state.currentPostId);
|
||||
const entryId = (lib.ok && lib.data) ? lib.data.id : undefined;
|
||||
const res = await window.api.downloadFile(url, btn.dataset.name || '', entryId);
|
||||
// 传 meta:条目还不在书库时由主进程自动建,避免下载完却找不到文件
|
||||
const res = await window.api.downloadFile(url, btn.dataset.name || '', entryId, undefined, entryMeta());
|
||||
|
||||
if (res.ok && res.data && res.data.canceled) {
|
||||
btn.textContent = orig; btn.disabled = false;
|
||||
return;
|
||||
}
|
||||
if (res.ok) {
|
||||
btn.textContent = '已保存 ✓';
|
||||
btn.classList.add('copied');
|
||||
setTimeout(() => { btn.textContent = orig; btn.classList.remove('copied'); btn.disabled = false; }, 2000);
|
||||
} else {
|
||||
if (!res.ok) {
|
||||
btn.textContent = '失败';
|
||||
btn.title = res.error || '';
|
||||
setTimeout(() => { btn.textContent = orig; btn.disabled = false; }, 2000);
|
||||
return;
|
||||
}
|
||||
|
||||
// 下载即入库,刷新"加入书库"按钮并就地提供打开入口
|
||||
if (window.Library) window.Library.markDirty();
|
||||
refreshAddButton();
|
||||
|
||||
const saved = res.data.path;
|
||||
btn.textContent = '打开';
|
||||
btn.disabled = false;
|
||||
btn.classList.add('copied');
|
||||
btn.onclick = async () => {
|
||||
const r = await window.api.openPath(saved);
|
||||
if (!r.ok) await confirmModal('打开失败', r.error || '无法打开该文件');
|
||||
};
|
||||
|
||||
const row = btn.closest('.dl-file-row');
|
||||
if (row && !row.querySelector('.reveal-btn')) {
|
||||
const reveal = document.createElement('button');
|
||||
reveal.className = 'copy-btn reveal-btn';
|
||||
reveal.textContent = '定位';
|
||||
reveal.onclick = () => window.api.showItem(saved);
|
||||
row.appendChild(reveal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user