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