From ded160083ff5dfd413b85203ab325ce9af147174 Mon Sep 17 00:00:00 2001 From: kevin Date: Sun, 12 Apr 2026 00:02:20 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=88=86=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api.js | 3 +- src/views/KeywordsCache.vue | 116 +++++++++++++++--------------------- 2 files changed, 50 insertions(+), 69 deletions(-) diff --git a/src/api.js b/src/api.js index 7690d25..782bd77 100644 --- a/src/api.js +++ b/src/api.js @@ -111,8 +111,9 @@ export async function fetchUrlKeywordsStats() { return data } -export async function fetchUrlKeywordsList() { +export async function fetchUrlKeywordsList({ page = 1, page_size = 50 } = {}) { const { data } = await axios.get(`${BASE}/admin/url/keywords/list`, { + params: { page, page_size }, timeout: 10000, }) return data diff --git a/src/views/KeywordsCache.vue b/src/views/KeywordsCache.vue index 06aa95b..ea46a36 100644 --- a/src/views/KeywordsCache.vue +++ b/src/views/KeywordsCache.vue @@ -4,11 +4,13 @@ import { fetchUrlKeywordsList, fetchUrlKeywords, fetchUrlKeywordsStats } from '. const loading = ref(true) const error = ref(null) -const stats = ref({ size: 0, max_size: 10000, items: [] }) +const stats = ref({ size: 0, max_size: 10000 }) +const items = ref([]) // 当前页数据 const expandedUrls = ref(new Set()) const urlKeywords = ref({}) const loadingKeywords = ref(new Set()) const search = ref('') +const total = ref(0) // 分页 const currentPage = ref(1) @@ -18,7 +20,6 @@ let statsInterval = null onMounted(async () => { await load() - // 容量卡片每 5 秒自动刷新 statsInterval = setInterval(loadStats, 5000) }) @@ -26,36 +27,36 @@ onUnmounted(() => { if (statsInterval) clearInterval(statsInterval) }) -// 只刷新 stats(容量数据),不刷新列表 async function loadStats() { try { const s = await fetchUrlKeywordsStats() stats.value.size = s.size || 0 stats.value.max_size = s.max_size || 10000 } catch { - // 静默失败,不影响用户体验 + // 静默 } } async function load() { loading.value = true error.value = null - currentPage.value = 1 // 重置到第一页 try { - const data = await fetchUrlKeywordsList() + const data = await fetchUrlKeywordsList({ page: currentPage.value, page_size: pageSize.value }) stats.value.size = data.size || 0 stats.value.max_size = data.max_size || 10000 - // items 数组:{ url, title, snippet, keywords },反转显示(最新在前) - stats.value.items = (data.items || []).reverse() + total.value = data.total || 0 + items.value = data.items || [] + + // 默认全部展开 + items.value.forEach(item => expandedUrls.value.add(item.url)) + // 预加载当前页关键词 + items.value.forEach(item => { + if (item.keywords?.length) { + urlKeywords.value[item.url] = item.keywords + } + }) } catch (e) { - try { - const s = await fetchUrlKeywordsStats() - stats.value.size = s.size || 0 - stats.value.max_size = s.max_size || 10000 - stats.value.items = [] - } catch { - error.value = '无法加载缓存数据' - } + error.value = '无法加载缓存数据' console.error(e) } finally { loading.value = false @@ -67,41 +68,32 @@ const usage = computed(() => { return stats.value.size / stats.value.max_size }) -const filteredItems = computed(() => { - if (!search.value) return stats.value.items - const q = search.value.toLowerCase() - return stats.value.items.filter(item => - item.url.toLowerCase().includes(q) || - (item.title && item.title.toLowerCase().includes(q)) || - (item.snippet && item.snippet.toLowerCase().includes(q)) - ) -}) - -const totalPages = computed(() => { - return Math.max(1, Math.ceil(filteredItems.value.length / pageSize.value)) -}) - -const paginatedItems = computed(() => { - const start = (currentPage.value - 1) * pageSize.value - const end = start + pageSize.value - return filteredItems.value.slice(start, end) -}) +const totalPages = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value))) function prevPage() { - if (currentPage.value > 1) currentPage.value-- + if (currentPage.value > 1) { + currentPage.value-- + load() + } } function nextPage() { - if (currentPage.value < totalPages.value) currentPage.value++ + if (currentPage.value < totalPages.value) { + currentPage.value++ + load() + } } function goToPage(p) { - if (p >= 1 && p <= totalPages.value) currentPage.value = p + if (p >= 1 && p <= totalPages.value && p !== currentPage.value) { + currentPage.value = p + load() + } } -// 搜索时回到第一页 function onSearch() { currentPage.value = 1 + load() } async function toggleKeywords(url) { @@ -112,16 +104,13 @@ async function toggleKeywords(url) { expandedUrls.value.add(url) - if (urlKeywords.value[url]) { - return - } + if (urlKeywords.value[url]) return loadingKeywords.value.add(url) try { const data = await fetchUrlKeywords(url) urlKeywords.value[url] = data.keywords || [] } catch (e) { - console.error('Failed to load keywords:', e) urlKeywords.value[url] = [] } finally { loadingKeywords.value.delete(url) @@ -214,7 +203,7 @@ function truncateSnippet(text, maxLen = 200) { -
+
📭
缓存为空
爬取页面时会自动填充此缓存
@@ -224,29 +213,26 @@ function truncateSnippet(text, maxLen = 200) {
-
+
+ + {{ expandedUrls.has(item.url) ? '▼' : '▶' }} + {{ truncateUrl(item.url) }} -
@@ -261,10 +247,10 @@ function truncateSnippet(text, maxLen = 200) {
-