diff --git a/src/zotero_kb/templates/index.html b/src/zotero_kb/templates/index.html index 708d0cf..0231bf9 100644 --- a/src/zotero_kb/templates/index.html +++ b/src/zotero_kb/templates/index.html @@ -418,6 +418,10 @@ : [] ), visibleCollectionItems: [], + collectionItemsLoaded: false, + loadingCollectionKey: null, + nextCollectionItemsRequestToken: 0, + activeCollectionItemsRequestToken: 0, }; const elements = { @@ -599,7 +603,7 @@ syncCurrentProjectLabels(); elements.importSelectedItemsButton.disabled = !hasProject || !hasSelection; elements.importSelectedItemsButton.title = !hasProject ? "请先选择项目" : (hasSelection ? "" : "请先选择文献"); - elements.selectDescendantsButton.disabled = !state.visibleCollectionItems.length; + elements.selectDescendantsButton.disabled = !state.collectionItemsLoaded || !state.visibleCollectionItems.length; elements.clearSelectionButton.disabled = !state.selectedItemKeys.size; renderSelectedCount(); } @@ -719,6 +723,11 @@ updateImportActionState(); return; } + if (!state.collectionItemsLoaded) { + elements.collectionItems.innerHTML = '
正在加载当前 collection 文献。
'; + updateImportActionState(); + return; + } if (!state.visibleCollectionItems.length) { elements.collectionItems.innerHTML = '
当前目录及子目录下没有可导入文献。
'; updateImportActionState(); @@ -759,14 +768,21 @@ return; } state.visibleCollectionItems = []; + state.collectionItemsLoaded = false; + state.loadingCollectionKey = null; renderCollectionItems(); setStatus(elements.collectionStatus, ""); } async function loadCollectionItems(collectionKey) { + if (!state.collectionItemsLoaded && state.loadingCollectionKey === collectionKey) { + return; + } state.selectedCollectionKey = collectionKey; if (normalizeSelectedCollectionKey() !== collectionKey) { state.visibleCollectionItems = []; + state.collectionItemsLoaded = false; + state.loadingCollectionKey = null; renderCollectionTree(); renderCollectionItems(); setStatus(elements.collectionStatus, state.selectedCollectionKey ? "已切换到可用 collection。" : ""); @@ -778,9 +794,20 @@ ensureSelectedCollectionExpanded(); persistImportSessionState(); renderCollectionTree(); + state.collectionItemsLoaded = false; + state.loadingCollectionKey = collectionKey; + const requestToken = state.nextCollectionItemsRequestToken + 1; + state.nextCollectionItemsRequestToken = requestToken; + state.activeCollectionItemsRequestToken = requestToken; + renderCollectionItems(); setStatus(elements.collectionStatus, "正在加载文献..."); const payload = await api(`/api/zotero/collections/${collectionKey}/items`); + if (state.activeCollectionItemsRequestToken !== requestToken || state.selectedCollectionKey !== collectionKey) { + return; + } state.visibleCollectionItems = Array.isArray(payload.items) ? payload.items : []; + state.collectionItemsLoaded = true; + state.loadingCollectionKey = null; renderCollectionItems(); const visibleCount = state.visibleCollectionItems.length; setStatus( @@ -847,7 +874,10 @@ } catch (error) { setStatus(elements.collectionStatus, error.message, true); } - } else if (state.selectedCollectionKey && !state.visibleCollectionItems.length) { + } else if ( + state.selectedCollectionKey && + (!state.collectionItemsLoaded && state.loadingCollectionKey !== state.selectedCollectionKey) + ) { try { await loadCollectionItems(state.selectedCollectionKey); } catch (error) { diff --git a/tests/test_ui.py b/tests/test_ui.py index eae301e..f595b7a 100644 --- a/tests/test_ui.py +++ b/tests/test_ui.py @@ -42,6 +42,8 @@ def test_index_contains_import_modal_controls(tmp_path) -> None: assert 'const ABSTRACT_PREVIEW_DELAY_MS = 3000;' in html assert 'isImportModalOpen: false' in html assert "currentProjectName: null" in html + assert "collectionItemsLoaded: false" in html + assert "nextCollectionItemsRequestToken: 0" in html assert "function openImportModal()" in html assert "function closeImportModal()" in html assert "function updateImportActionState()" in html