Files
bidding_watcher/widget/looker/frontend/src/App.vue
dmy bcd7af4e69 feat: 添加爬虫状态监控功能
新增爬虫统计信息展示组件,包括后端数据查询接口和前端展示界面。同时简化日期显示格式并添加刷新提示功能。
2026-01-14 09:26:04 +08:00

196 lines
4.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts" setup>
import { ref, onMounted, onUnmounted } from 'vue'
import PinnedBids from './components/PinnedBids.vue'
import AiRecommendations from './components/AiRecommendations.vue'
import WidgetCrawlInfo from './components/WidgetCrawlInfo.vue'
const activeTab = ref('pinned')
const pinnedBidsRef = ref<InstanceType<typeof PinnedBids>>()
const aiRecommendationsRef = ref<InstanceType<typeof AiRecommendations>>()
const widgetCrawlInfoRef = ref<InstanceType<typeof WidgetCrawlInfo>>()
let refreshTimer: number | null = null
const showToast = ref(false)
const toastMessage = ref('')
const tabs = [
{ id: 'pinned', label: '置顶项目' },
{ id: 'ai', label: 'AI 推荐' },
{ id: 'status', label: '状态' }
]
const handleRefresh = () => {
if (activeTab.value === 'pinned' && pinnedBidsRef.value) {
pinnedBidsRef.value.loadPinnedBids()
showToastMessage('置顶项目已刷新')
} else if (activeTab.value === 'ai' && aiRecommendationsRef.value) {
aiRecommendationsRef.value.loadRecommendations()
showToastMessage('AI 推荐已刷新')
} else if (activeTab.value === 'status' && widgetCrawlInfoRef.value) {
widgetCrawlInfoRef.value.loadCrawlStats()
showToastMessage('状态已刷新')
}
}
const showToastMessage = (message: string) => {
toastMessage.value = message
showToast.value = true
setTimeout(() => {
showToast.value = false
}, 2000)
}
const startAutoRefresh = () => {
// 每5分钟300000毫秒自动刷新
refreshTimer = window.setInterval(() => {
handleRefresh()
}, 300000)
}
const stopAutoRefresh = () => {
if (refreshTimer !== null) {
clearInterval(refreshTimer)
refreshTimer = null
}
}
onMounted(() => {
startAutoRefresh()
})
onUnmounted(() => {
stopAutoRefresh()
})
</script>
<template>
<div class="app-container">
<div class="tabs">
<button
v-for="tab in tabs"
:key="tab.id"
:class="['tab-button', { active: activeTab === tab.id }]"
@click="activeTab = tab.id"
>
{{ tab.label }}
</button>
<button class="refresh-button" @click="handleRefresh">
🔄 刷新
</button>
</div>
<div class="tab-content">
<PinnedBids v-if="activeTab === 'pinned'" ref="pinnedBidsRef"/>
<AiRecommendations v-else-if="activeTab === 'ai'" ref="aiRecommendationsRef"/>
<WidgetCrawlInfo v-else-if="activeTab === 'status'" ref="widgetCrawlInfoRef"/>
</div>
<div v-if="showToast" class="toast">
{{ toastMessage }}
</div>
</div>
</template>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: #f5f5f5;
font-size: 12px;
}
.app-container {
min-height: 100vh;
background: #f5f5f5;
}
.tabs {
display: flex;
background: #fff;
border-bottom: 1px solid #e0e0e0;
padding: 0 8px;
}
.tab-button {
padding: 8px 12px;
background: none;
border: none;
border-bottom: 2px solid transparent;
font-size: 12px;
font-weight: 500;
color: #666;
cursor: pointer;
transition: all 0.2s ease;
}
.tab-button:hover {
color: #333;
background: #f9f9f9;
}
.tab-button.active {
color: #3498db;
border-bottom-color: #3498db;
}
.tab-content {
padding: 8px;
}
.refresh-button {
margin-left: auto;
padding: 4px 10px;
background: #3498db;
color: #fff;
border: none;
border-radius: 3px;
font-size: 11px;
cursor: pointer;
transition: all 0.2s ease;
}
.refresh-button:hover {
background: #2980b9;
}
.refresh-button:active {
transform: scale(0.98);
}
.placeholder {
display: flex;
align-items: center;
justify-content: center;
min-height: 200px;
color: #999;
font-size: 12px;
}
.toast {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.8);
color: #fff;
padding: 12px 24px;
border-radius: 4px;
font-size: 12px;
z-index: 1000;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
</style>