feat(前端UI): 实现运维监控前端界面

- 新增帮助提示组件(HelpTooltip.vue)
- 更新侧边栏添加 ops 监控菜单项
- 扩展设置视图集成 ops 配置面板
- 新增 ops 监控视图目录(dashboard, alerts, realtime, settings 等)
This commit is contained in:
IanShaw027
2026-01-09 21:00:04 +08:00
parent fc32b57798
commit 8ae75e7f6e
21 changed files with 5362 additions and 6 deletions

View File

@@ -665,6 +665,59 @@
</div>
</div>
<!-- Ops Monitoring -->
<div class="card">
<div class="border-b border-gray-100 px-6 py-4 dark:border-dark-700">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">
{{ t('admin.settings.opsMonitoring.title') }}
</h2>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
{{ t('admin.settings.opsMonitoring.description') }}
</p>
</div>
<div class="p-6">
<div class="flex items-center justify-between">
<div>
<label class="font-medium text-gray-900 dark:text-white">{{
t('admin.settings.opsMonitoring.enabled')
}}</label>
<p class="text-sm text-gray-500 dark:text-gray-400">
{{ t('admin.settings.opsMonitoring.enabledHint') }}
</p>
</div>
<Toggle v-model="form.ops_monitoring_enabled" />
</div>
<div v-if="form.ops_monitoring_enabled" class="mt-5 flex items-center justify-between">
<div>
<label class="font-medium text-gray-900 dark:text-white">{{
t('admin.settings.opsMonitoring.realtimeEnabled')
}}</label>
<p class="text-sm text-gray-500 dark:text-gray-400">
{{ t('admin.settings.opsMonitoring.realtimeEnabledHint') }}
</p>
</div>
<Toggle v-model="form.ops_realtime_monitoring_enabled" />
</div>
<div v-if="form.ops_monitoring_enabled" class="mt-5 flex items-center justify-between">
<div>
<label class="font-medium text-gray-900 dark:text-white">{{
t('admin.settings.opsMonitoring.queryMode')
}}</label>
<p class="text-sm text-gray-500 dark:text-gray-400">
{{ t('admin.settings.opsMonitoring.queryModeHint') }}
</p>
</div>
<Select
v-model="form.ops_query_mode_default"
:options="opsQueryModeOptions"
class="w-[220px]"
/>
</div>
</div>
</div>
<!-- Save Button -->
<div class="flex justify-end">
<button type="submit" :disabled="saving" class="btn btn-primary">
@@ -687,22 +740,34 @@
</button>
</div>
</form>
<!-- Ops settings and management (only show when enabled) -->
<div v-if="showOpsSettings" class="space-y-6">
<OpsRuntimeSettingsCard />
<OpsEmailNotificationCard />
<OpsAlertRulesCard />
</div>
</div>
</AppLayout>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { ref, reactive, onMounted, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { adminAPI } from '@/api'
import type { SystemSettings, UpdateSettingsRequest } from '@/api/admin/settings'
import AppLayout from '@/components/layout/AppLayout.vue'
import Icon from '@/components/icons/Icon.vue'
import Toggle from '@/components/common/Toggle.vue'
import { useAppStore } from '@/stores'
import Select from '@/components/common/Select.vue'
import { useAdminSettingsStore, useAppStore } from '@/stores'
import OpsAlertRulesCard from '@/views/admin/ops/components/OpsAlertRulesCard.vue'
import OpsEmailNotificationCard from '@/views/admin/ops/components/OpsEmailNotificationCard.vue'
import OpsRuntimeSettingsCard from '@/views/admin/ops/components/OpsRuntimeSettingsCard.vue'
const { t } = useI18n()
const appStore = useAppStore()
const adminSettingsStore = useAdminSettingsStore()
const loading = ref(true)
const saving = ref(false)
@@ -747,11 +812,29 @@ const form = reactive<SettingsForm>({
turnstile_site_key: '',
turnstile_secret_key: '',
turnstile_secret_key_configured: false,
// Model fallback
enable_model_fallback: false,
fallback_model_anthropic: 'claude-3-5-sonnet-20241022',
fallback_model_openai: 'gpt-4o',
fallback_model_gemini: 'gemini-2.5-pro',
fallback_model_antigravity: 'gemini-2.5-pro',
// Identity patch (Claude -> Gemini)
enable_identity_patch: true,
identity_patch_prompt: ''
identity_patch_prompt: '',
// Ops Monitoring (vNext)
ops_monitoring_enabled: true,
ops_realtime_monitoring_enabled: true,
ops_query_mode_default: 'auto'
})
const opsQueryModeOptions = computed(() => [
{ value: 'auto', label: t('admin.settings.opsMonitoring.queryModeAuto') },
{ value: 'raw', label: t('admin.settings.opsMonitoring.queryModeRaw') },
{ value: 'preagg', label: t('admin.settings.opsMonitoring.queryModePreagg') }
])
const showOpsSettings = computed(() => !!form.ops_monitoring_enabled)
function handleLogoUpload(event: Event) {
const input = event.target as HTMLInputElement
const file = input.files?.[0]
@@ -829,12 +912,25 @@ async function saveSettings() {
smtp_use_tls: form.smtp_use_tls,
turnstile_enabled: form.turnstile_enabled,
turnstile_site_key: form.turnstile_site_key,
turnstile_secret_key: form.turnstile_secret_key || undefined
turnstile_secret_key: form.turnstile_secret_key || undefined,
enable_model_fallback: form.enable_model_fallback,
fallback_model_anthropic: form.fallback_model_anthropic,
fallback_model_openai: form.fallback_model_openai,
fallback_model_gemini: form.fallback_model_gemini,
fallback_model_antigravity: form.fallback_model_antigravity,
enable_identity_patch: form.enable_identity_patch,
identity_patch_prompt: form.identity_patch_prompt,
ops_monitoring_enabled: form.ops_monitoring_enabled,
ops_realtime_monitoring_enabled: form.ops_realtime_monitoring_enabled,
ops_query_mode_default: form.ops_query_mode_default
}
const updated = await adminAPI.settings.updateSettings(payload)
Object.assign(form, updated)
form.smtp_password = ''
form.turnstile_secret_key = ''
adminSettingsStore.setOpsMonitoringEnabledLocal(!!updated.ops_monitoring_enabled)
adminSettingsStore.setOpsRealtimeMonitoringEnabledLocal(!!updated.ops_realtime_monitoring_enabled)
adminSettingsStore.setOpsQueryModeDefaultLocal(updated.ops_query_mode_default || 'auto')
// Refresh cached public settings so sidebar/header update immediately
await appStore.fetchPublicSettings(true)
appStore.showSuccess(t('admin.settings.settingsSaved'))