feat(ops): 添加自动刷新配置功能

功能特性:
- 支持配置启用/禁用自动刷新
- 可配置刷新间隔(15秒/30秒/60秒)
- 实时倒计时显示,用户可见下次刷新时间
- 手动刷新自动重置倒计时
- 页面卸载时自动清理定时器

用户体验:
- 默认禁用,用户可根据需求开启
- 与现有 OpsConcurrencyCard 5秒刷新保持一致
- 倒计时带旋转动画,视觉反馈清晰
- 配置修改后立即生效,无需刷新页面

技术实现:
- ops.ts: 添加 auto_refresh_enabled 和 auto_refresh_interval_seconds 配置
- OpsSettingsDialog.vue: 添加自动刷新配置界面
- OpsDashboard.vue: 实现主刷新逻辑和双定时器设计
- OpsDashboardHeader.vue: 倒计时显示组件

配置说明:
- auto_refresh_enabled: 是否启用(默认 false)
- auto_refresh_interval_seconds: 刷新间隔(默认 30 秒,范围 15-300 秒)
This commit is contained in:
IanShaw027
2026-01-12 16:50:53 +08:00
parent 345a965fa3
commit b98fb013ae
4 changed files with 135 additions and 1 deletions

View File

@@ -734,6 +734,9 @@ export interface OpsAlertRuntimeSettings {
export interface OpsAdvancedSettings {
data_retention: OpsDataRetentionSettings
aggregation: OpsAggregationSettings
ignore_count_tokens_errors: boolean
auto_refresh_enabled: boolean
auto_refresh_interval_seconds: number
}
export interface OpsDataRetentionSettings {

View File

@@ -20,6 +20,8 @@
:loading="loading"
:last-updated="lastUpdated"
:thresholds="metricThresholds"
:auto-refresh-enabled="autoRefreshEnabled"
:auto-refresh-countdown="autoRefreshCountdown"
@update:time-range="onTimeRangeChange"
@update:platform="onPlatformChange"
@update:group="onGroupChange"
@@ -104,7 +106,7 @@
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import { useDebounceFn } from '@vueuse/core'
import { useDebounceFn, useIntervalFn } from '@vueuse/core'
import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
import AppLayout from '@/components/layout/AppLayout.vue'
@@ -287,6 +289,45 @@ const requestDetailsPreset = ref<OpsRequestDetailsPreset>({
const showSettingsDialog = ref(false)
const showAlertRulesCard = ref(false)
// Auto refresh settings
const autoRefreshEnabled = ref(false)
const autoRefreshIntervalMs = ref(30000) // default 30 seconds
const autoRefreshCountdown = ref(0)
// Auto refresh timer
const { pause: pauseAutoRefresh, resume: resumeAutoRefresh, isActive: isAutoRefreshActive } = useIntervalFn(
() => {
if (autoRefreshEnabled.value && opsEnabled.value && !loading.value) {
fetchData()
}
},
autoRefreshIntervalMs,
{ immediate: false }
)
// Countdown timer (updates every second)
const { pause: pauseCountdown, resume: resumeCountdown } = useIntervalFn(
() => {
if (autoRefreshEnabled.value && autoRefreshCountdown.value > 0) {
autoRefreshCountdown.value--
}
},
1000,
{ immediate: false }
)
// Load auto refresh settings from backend
async function loadAutoRefreshSettings() {
try {
const settings = await opsAPI.getAdvancedSettings()
autoRefreshEnabled.value = settings.auto_refresh_enabled
autoRefreshIntervalMs.value = settings.auto_refresh_interval_seconds * 1000
autoRefreshCountdown.value = settings.auto_refresh_interval_seconds
} catch (err) {
console.error('[OpsDashboard] Failed to load auto refresh settings', err)
}
}
function handleThroughputSelectPlatform(nextPlatform: string) {
platform.value = nextPlatform || ''
groupId.value = null
@@ -510,6 +551,10 @@ async function fetchData() {
])
if (fetchSeq !== dashboardFetchSeq) return
lastUpdated.value = new Date()
// Reset auto refresh countdown after successful fetch
if (autoRefreshEnabled.value) {
autoRefreshCountdown.value = Math.floor(autoRefreshIntervalMs.value / 1000)
}
} catch (err) {
if (!isOpsDisabledError(err)) {
console.error('[ops] failed to fetch dashboard data', err)
@@ -567,9 +612,18 @@ onMounted(async () => {
// Load thresholds configuration
loadThresholds()
// Load auto refresh settings
await loadAutoRefreshSettings()
if (opsEnabled.value) {
await fetchData()
}
// Start auto refresh if enabled
if (autoRefreshEnabled.value) {
resumeAutoRefresh()
resumeCountdown()
}
})
async function loadThresholds() {
@@ -584,5 +638,27 @@ async function loadThresholds() {
onUnmounted(() => {
abortDashboardFetch()
pauseAutoRefresh()
pauseCountdown()
})
// Watch auto refresh settings changes
watch(autoRefreshEnabled, (enabled) => {
if (enabled) {
autoRefreshCountdown.value = Math.floor(autoRefreshIntervalMs.value / 1000)
resumeAutoRefresh()
resumeCountdown()
} else {
pauseAutoRefresh()
pauseCountdown()
autoRefreshCountdown.value = 0
}
})
// Reload auto refresh settings after settings dialog is closed
watch(showSettingsDialog, async (show) => {
if (!show) {
await loadAutoRefreshSettings()
}
})
</script>

View File

@@ -23,6 +23,8 @@ interface Props {
loading: boolean
lastUpdated: Date | null
thresholds?: OpsMetricThresholds | null // 阈值配置
autoRefreshEnabled?: boolean
autoRefreshCountdown?: number
}
interface Emits {
@@ -839,6 +841,17 @@ function handleToolbarRefresh() {
<span>·</span>
<span>{{ t('common.refresh') }}: {{ updatedAtLabel }}</span>
<template v-if="props.autoRefreshEnabled && props.autoRefreshCountdown !== undefined">
<span>·</span>
<span class="flex items-center gap-1">
<svg class="h-3 w-3 animate-spin text-blue-500" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<span>自动刷新: {{ props.autoRefreshCountdown }}s</span>
</span>
</template>
<template v-if="systemMetrics">
<span>·</span>
<span>

View File

@@ -487,6 +487,48 @@ async function saveAllSettings() {
<Toggle v-model="advancedSettings.aggregation.aggregation_enabled" />
</div>
</div>
<!-- 错误过滤 -->
<div class="space-y-3">
<h5 class="text-xs font-semibold text-gray-700 dark:text-gray-300">错误过滤</h5>
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">忽略 count_tokens 错误</label>
<p class="mt-1 text-xs text-gray-500">
启用后count_tokens 请求的错误将不计入运维监控的统计和告警中但仍会存储在数据库中
</p>
</div>
<Toggle v-model="advancedSettings.ignore_count_tokens_errors" />
</div>
</div>
<!-- 自动刷新 -->
<div class="space-y-3">
<h5 class="text-xs font-semibold text-gray-700 dark:text-gray-300">自动刷新</h5>
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">启用自动刷新</label>
<p class="mt-1 text-xs text-gray-500">
自动刷新仪表板数据启用后会定期拉取最新数据
</p>
</div>
<Toggle v-model="advancedSettings.auto_refresh_enabled" />
</div>
<div v-if="advancedSettings.auto_refresh_enabled">
<label class="input-label">刷新间隔</label>
<Select
v-model="advancedSettings.auto_refresh_interval_seconds"
:options="[
{ value: 15, label: '15 秒' },
{ value: 30, label: '30 秒' },
{ value: 60, label: '60 秒' }
]"
/>
</div>
</div>
</div>
</details>
</div>