Compare commits

...
1 Commits
Author SHA1 Message Date
kevin 422033fc6d 前端显示关键词 2026-04-10 20:41:25 +08:00
2 changed files with 104 additions and 9 deletions
+8
View File
@@ -96,3 +96,11 @@ export async function fetchCrawlStatus() {
return data
}
export async function fetchUrlKeywords(url) {
const { data } = await axios.get(`${BASE}/admin/url/keywords`, {
params: { url },
timeout: 5000,
})
return data
}
+96 -9
View File
@@ -1,6 +1,6 @@
<script setup>
import { ref, onMounted, onUnmounted, computed } from 'vue'
import { fetchRecent } from '../api.js'
import { fetchRecent, fetchUrlKeywords } from '../api.js'
const items = ref([])
const total = ref(0)
@@ -13,6 +13,11 @@ let refreshInterval = null
const limits = [20, 50, 100, 200]
const limit = ref(50)
// 关键词展开状态和缓存
const expandedUrls = ref(new Set())
const urlKeywords = ref({})
const loadingKeywords = ref(new Set())
onMounted(async () => {
await load()
})
@@ -81,6 +86,30 @@ function topLang(language) {
const sorted = Object.entries(language).sort((a, b) => b[1] - a[1])
return sorted[0]
}
async function toggleKeywords(url) {
if (expandedUrls.value.has(url)) {
expandedUrls.value.delete(url)
return
}
expandedUrls.value.add(url)
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)
}
}
</script>
<template>
@@ -170,6 +199,36 @@ function topLang(language) {
<div class="text-xs text-gray-600 mt-0.5 break-all line-clamp-1">{{ item.url }}</div>
</a>
<div v-if="item.description" class="text-xs text-gray-500 mt-1 line-clamp-1">{{ item.description }}</div>
<!-- 关键词展开/折叠 -->
<button
@click.prevent="toggleKeywords(item.url)"
class="mt-2 text-xs text-blue-400 hover:text-blue-300 flex items-center gap-1"
>
<span>{{ expandedUrls.has(item.url) ? '▼' : '▶' }} 关键词</span>
<span v-if="loadingKeywords.has(item.url)" class="text-gray-500">加载中...</span>
</button>
<!-- 关键词列表 -->
<div v-if="expandedUrls.has(item.url)" class="mt-2">
<template v-if="urlKeywords[item.url]?.length">
<div class="flex flex-wrap gap-1.5">
<span
v-for="kw in urlKeywords[item.url]"
:key="kw.word"
class="text-xs px-2 py-0.5 rounded bg-gray-800 text-gray-300 border border-gray-700"
:title="`权重: ${kw.weight.toFixed(4)}`"
>
{{ kw.word }}
<span class="text-gray-500 text-[10px] ml-0.5">{{ kw.weight.toFixed(2) }}</span>
</span>
</div>
<div class="text-xs text-gray-600 mt-1">
{{ urlKeywords[item.url].length }} 个关键词
</div>
</template>
<span v-else-if="!loadingKeywords.has(item.url)" class="text-xs text-gray-600">
暂无关键词服务重启后缓存已清空
</span>
</div>
</td>
<td class="px-5 py-3.5">
<span class="text-gray-400 text-xs font-mono">{{ item.domain }}</span>
@@ -202,17 +261,16 @@ function topLang(language) {
<!-- Mobile Cards -->
<div class="md:hidden divide-y divide-gray-800">
<a
<div
v-for="item in filtered"
:key="item.url"
:href="item.url"
target="_blank"
rel="noopener noreferrer"
class="block p-4 hover:bg-gray-800/40 transition-colors"
>
<div class="font-medium text-gray-200 text-sm line-clamp-2 mb-1">{{ item.title || '(无标题)' }}</div>
<div class="text-xs text-gray-500 break-all line-clamp-1 mb-2">{{ item.url }}</div>
<div class="flex items-center gap-2 text-xs">
<a :href="item.url" target="_blank" rel="noopener noreferrer" class="block">
<div class="font-medium text-gray-200 text-sm line-clamp-2 mb-1">{{ item.title || '(无标题)' }}</div>
<div class="text-xs text-gray-500 break-all line-clamp-1 mb-2">{{ item.url }}</div>
</a>
<div class="flex items-center gap-2 text-xs mb-2">
<span class="text-gray-400 font-mono">{{ item.domain }}</span>
<template v-if="topLang(item.language)">
<span
@@ -223,7 +281,36 @@ function topLang(language) {
</template>
<span class="text-gray-600 ml-auto">{{ fmtTime(item.crawled_at) }}</span>
</div>
</a>
<!-- 移动端关键词展开 -->
<button
@click.prevent="toggleKeywords(item.url)"
class="text-xs text-blue-400 hover:text-blue-300 flex items-center gap-1 mb-2"
>
<span>{{ expandedUrls.has(item.url) ? '▼' : '▶' }} 关键词</span>
<span v-if="loadingKeywords.has(item.url)" class="text-gray-500">加载中...</span>
</button>
<div v-if="expandedUrls.has(item.url)" class="mb-2">
<template v-if="urlKeywords[item.url]?.length">
<div class="flex flex-wrap gap-1">
<span
v-for="kw in urlKeywords[item.url]"
:key="kw.word"
class="text-[10px] px-1.5 py-0.5 rounded bg-gray-800 text-gray-300 border border-gray-700"
:title="`权重: ${kw.weight.toFixed(4)}`"
>
{{ kw.word }}
<span class="text-gray-500 text-[9px] ml-0.5">{{ kw.weight.toFixed(2) }}</span>
</span>
</div>
<div class="text-[10px] text-gray-600 mt-1">
{{ urlKeywords[item.url].length }}
</div>
</template>
<span v-else-if="!loadingKeywords.has(item.url)" class="text-xs text-gray-600">
暂无关键词
</span>
</div>
</div>
<div v-if="!filtered.length" class="p-8 text-center text-gray-600">
没有找到匹配的记录
</div>