diff --git a/src/api/ops/alertLevel.ts b/src/api/ops/alertLevel.ts index 1ff6cde..7381c4b 100644 --- a/src/api/ops/alertLevel.ts +++ b/src/api/ops/alertLevel.ts @@ -10,6 +10,19 @@ export const fetchAlertLevelList = (data?: { return request.get("/Alert/v1/severity/list", { params: data || {} }); }; +/** 获取告警级别下拉选项(不分页),默认仅返回 enabled=true */ +export const fetchAlertLevelOptions = (data?: { + keyword?: string; + enabled?: 'true' | 'false'; +}) => { + return request.get("/Alert/v1/severity/options", { + params: { + keyword: data?.keyword || undefined, + enabled: data?.enabled ?? 'true', + }, + }) +} + /** 获取告警级别详情 */ export const fetchAlertLevelDetail = (id: number) => { return request.get(`/Alert/v1/severity/get/${id}`); diff --git a/src/api/ops/alertPolicy.ts b/src/api/ops/alertPolicy.ts index 419da19..8976ea6 100644 --- a/src/api/ops/alertPolicy.ts +++ b/src/api/ops/alertPolicy.ts @@ -112,6 +112,18 @@ export const fetchSeverityList = (data: { enabled?: string; }) => request.get("/Alert/v1/severity/list", { params: data }); +/** 获取告警级别下拉选项(不分页),支持 keyword 模糊搜索与 enabled=true 过滤 */ +export const fetchSeverityOptions = (data?: { + keyword?: string; + enabled?: 'true' | 'false'; +}) => + request.get("/Alert/v1/severity/options", { + params: { + keyword: data?.keyword || undefined, + enabled: data?.enabled ?? 'true', + }, + }); + /** 获取工单模板下拉选项 */ export const fetchFeedbackTemplateOptions = (data?: { status?: 'active' | 'inactive' diff --git a/src/api/ops/alertTemplate.ts b/src/api/ops/alertTemplate.ts index 026b7b8..28a5260 100644 --- a/src/api/ops/alertTemplate.ts +++ b/src/api/ops/alertTemplate.ts @@ -64,6 +64,12 @@ export interface AlertSeverity { name: string color?: string level?: number + description?: string + icon?: string + priority?: number + enabled?: boolean + is_default?: boolean + config?: any } // 指标元数据 @@ -188,6 +194,19 @@ export const fetchSeverityList = () => { return request.get('/Alert/v1/severity/list') } +/** 获取告警级别下拉选项(不分页),支持 keyword 模糊搜索与 enabled=true 过滤 */ +export const fetchSeverityOptions = (params?: { + keyword?: string + enabled?: 'true' | 'false' +}) => { + return request.get('/Alert/v1/severity/options', { + params: { + keyword: params?.keyword || undefined, + enabled: params?.enabled ?? 'true', + }, + }) +} + /** 按 code 获取告警级别 */ export const fetchSeverityByCode = (code: string) => { return request.get(`/Alert/v1/severity/get-by-code/${code}`) diff --git a/src/api/ops/logs.ts b/src/api/ops/logs.ts new file mode 100644 index 0000000..e517544 --- /dev/null +++ b/src/api/ops/logs.ts @@ -0,0 +1,157 @@ +import { request } from '@/api/request' + +const BASE = '/Logs/v1' + +/** 解析 bsm-sdk 风格响应,取出业务 data */ +export function unwrapLogsPayload(res: any): any { + if (res == null) return null + if (typeof res.code === 'number' && res.code !== 0) return null + return res.details ?? res.data ?? null +} + +export interface LogEvent { + id: number + created_at: string + source_kind: string + remote_addr: string + raw_payload: string + normalized_summary: string + normalized_detail: string + device_name: string + severity_code: string + trap_oid: string + alert_sent: boolean +} + +export interface LogEntriesParams { + page?: number + page_size?: number + source_kind?: string +} + +export interface LogEntriesResult { + total: number + page: number + page_size: number + items: LogEvent[] +} + +export interface SyslogRule { + id?: number + created_at?: string + updated_at?: string + name: string + enabled: boolean + priority: number + device_name_contains: string + keyword_regex: string + alert_name: string + severity_code: string + policy_id: number +} + +export interface TrapRule { + id?: number + created_at?: string + updated_at?: string + name: string + enabled: boolean + priority: number + oid_prefix: string + varbind_match_regex: string + alert_name: string + severity_code: string + policy_id: number +} + +export interface TrapDictionaryEntry { + id?: number + created_at?: string + updated_at?: string + oid_prefix: string + title: string + description: string + severity_code: string + recovery_message: string + enabled: boolean +} + +export interface TrapShield { + id?: number + created_at?: string + updated_at?: string + name: string + enabled: boolean + source_ip_cidr: string + oid_prefix: string + interface_hint: string + time_windows_json: string +} + +export function fetchLogEntries(params?: LogEntriesParams) { + return request.get(`${BASE}/entries`, { params }) +} + +export function fetchSyslogRules() { + return request.get(`${BASE}/syslog-rules`) +} + +export function createSyslogRule(data: Partial) { + return request.post(`${BASE}/syslog-rules`, data) +} + +export function updateSyslogRule(id: number, data: Partial) { + return request.put(`${BASE}/syslog-rules/${id}`, data) +} + +export function deleteSyslogRule(id: number) { + return request.delete(`${BASE}/syslog-rules/${id}`) +} + +export function fetchTrapRules() { + return request.get(`${BASE}/trap-rules`) +} + +export function createTrapRule(data: Partial) { + return request.post(`${BASE}/trap-rules`, data) +} + +export function updateTrapRule(id: number, data: Partial) { + return request.put(`${BASE}/trap-rules/${id}`, data) +} + +export function deleteTrapRule(id: number) { + return request.delete(`${BASE}/trap-rules/${id}`) +} + +export function fetchTrapDictionary() { + return request.get(`${BASE}/trap-dictionary`) +} + +export function createTrapDictionary(data: Partial) { + return request.post(`${BASE}/trap-dictionary`, data) +} + +export function updateTrapDictionary(id: number, data: Partial) { + return request.put(`${BASE}/trap-dictionary/${id}`, data) +} + +export function deleteTrapDictionary(id: number) { + return request.delete(`${BASE}/trap-dictionary/${id}`) +} + +export function fetchTrapSuppressions() { + return request.get(`${BASE}/trap-suppressions`) +} + +export function createTrapSuppression(data: Partial) { + return request.post(`${BASE}/trap-suppressions`, data) +} + +export function updateTrapSuppression(id: number, data: Partial) { + return request.put(`${BASE}/trap-suppressions/${id}`, data) +} + +export function deleteTrapSuppression(id: number) { + return request.delete(`${BASE}/trap-suppressions/${id}`) +} diff --git a/src/router/local-menu-flat.ts b/src/router/local-menu-flat.ts index abe6ed4..7a0280a 100644 --- a/src/router/local-menu-flat.ts +++ b/src/router/local-menu-flat.ts @@ -186,11 +186,12 @@ export const localMenuFlatItems: MenuItem[] = [ title: '日志监控', title_en: 'Log Monitoring', code: 'ops:综合监控:日志监控', - description: '综合监控 - 日志监控', + description: '综合监控 - 日志监控(对接 Logs)', app_id: 2, parent_id: 23, menu_path: '/monitor/log', menu_icon: 'appstore', + component: 'ops/pages/monitor/log', type: 1, sort_key: 13, created_at: '2025-12-26T13:23:51.907711+08:00', @@ -300,6 +301,91 @@ export const localMenuFlatItems: MenuItem[] = [ sort_key: 20, created_at: '2025-12-26T13:23:51.828777+08:00', }, + { + id: 12010, + identity: '019c7000-0001-7000-8000-000000000001', + title: '日志采集', + title_en: 'Log Ingest', + code: 'ops:日志采集', + description: 'Syslog / SNMP Trap(Logs 服务)', + app_id: 2, + menu_path: '/log-mgmt/', + menu_icon: 'File', + type: 1, + sort_key: 21, + created_at: '2026-03-30T10:00:00+08:00', + }, + { + id: 12011, + identity: '019c7000-0001-7000-8000-000000000011', + title: '日志查询', + title_en: 'Log Query', + code: 'ops:日志采集:日志查询', + app_id: 2, + parent_id: 12010, + menu_path: '/log-mgmt/entries', + menu_icon: 'List', + component: 'ops/pages/log-mgmt/entries', + type: 1, + sort_key: 22, + created_at: '2026-03-30T10:00:00+08:00', + }, + { + id: 12012, + identity: '019c7000-0001-7000-8000-000000000012', + title: 'Syslog 规则', + title_en: 'Syslog Rules', + code: 'ops:日志采集:syslog规则', + app_id: 2, + parent_id: 12010, + menu_path: '/log-mgmt/syslog-rules', + component: 'ops/pages/log-mgmt/syslog-rules', + type: 1, + sort_key: 23, + created_at: '2026-03-30T10:00:00+08:00', + }, + { + id: 12013, + identity: '019c7000-0001-7000-8000-000000000013', + title: 'Trap 规则', + title_en: 'Trap Rules', + code: 'ops:日志采集:trap规则', + app_id: 2, + parent_id: 12010, + menu_path: '/log-mgmt/trap-rules', + component: 'ops/pages/log-mgmt/trap-rules', + type: 1, + sort_key: 24, + created_at: '2026-03-30T10:00:00+08:00', + }, + { + id: 12014, + identity: '019c7000-0001-7000-8000-000000000014', + title: 'Trap 字典', + title_en: 'Trap Dictionary', + code: 'ops:日志采集:trap字典', + app_id: 2, + parent_id: 12010, + menu_path: '/log-mgmt/trap-dictionary', + component: 'ops/pages/log-mgmt/trap-dictionary', + type: 1, + sort_key: 25, + created_at: '2026-03-30T10:00:00+08:00', + }, + { + id: 12015, + identity: '019c7000-0001-7000-8000-000000000015', + title: 'Trap 屏蔽', + title_en: 'Trap Suppressions', + code: 'ops:日志采集:trap屏蔽', + app_id: 2, + parent_id: 12010, + menu_path: '/log-mgmt/trap-suppressions', + component: 'ops/pages/log-mgmt/trap-suppressions', + type: 1, + sort_key: 26, + created_at: '2026-03-30T10:00:00+08:00', + }, { id: 35, identity: '019b591d-021a-74a3-8092-c15b990f3c7e', @@ -311,7 +397,7 @@ export const localMenuFlatItems: MenuItem[] = [ menu_path: '/netarch/', menu_icon: 'Laptop', type: 1, - sort_key: 21, + sort_key: 30, created_at: '2025-12-26T13:23:51.969818+08:00', }, { @@ -899,10 +985,11 @@ export const localMenuFlatItems: MenuItem[] = [ title: '系统日志', title_en: 'System Logs', code: 'SystemSettingsSystemLogs', - description: '系统日志', + description: '系统日志(操作审计说明,演示见 index_bak)', app_id: 2, parent_id: 78, menu_path: '/system-settings/system-logs', + component: 'ops/pages/system-settings/system-logs', type: 1, sort_key: 59, created_at: '2025-12-27T12:31:02.556301+08:00', diff --git a/src/router/local-menu-items.ts b/src/router/local-menu-items.ts index 152dee3..73f600b 100644 --- a/src/router/local-menu-items.ts +++ b/src/router/local-menu-items.ts @@ -199,11 +199,12 @@ export const localMenuItems: MenuItem[] = [ title: '日志监控', title_en: 'Log Monitoring', code: 'ops:综合监控:日志监控', - description: '综合监控 - 日志监控', + description: '综合监控 - 日志监控(对接 Logs,同日志查询)', app_id: 2, parent_id: 23, menu_path: '/monitor/log', menu_icon: 'appstore', + component: 'ops/pages/monitor/log', type: 1, sort_key: 5, created_at: '2025-12-26T13:23:51.907711+08:00', @@ -323,6 +324,99 @@ export const localMenuItems: MenuItem[] = [ }, ], }, + { + id: 12010, + identity: '019c7000-0001-7000-8000-000000000001', + title: '日志采集', + title_en: 'Log Ingest', + code: 'ops:日志采集', + description: 'Syslog / SNMP Trap 采集与规则(Logs 服务)', + app_id: 2, + menu_path: '/log-mgmt/', + menu_icon: 'File', + type: 1, + sort_key: 5, + created_at: '2026-03-30T10:00:00+08:00', + children: [ + { + id: 12011, + identity: '019c7000-0001-7000-8000-000000000011', + title: '日志查询', + title_en: 'Log Query', + code: 'ops:日志采集:日志查询', + app_id: 2, + parent_id: 12010, + menu_path: '/log-mgmt/entries', + menu_icon: 'List', + component: 'ops/pages/log-mgmt/entries', + type: 1, + sort_key: 1, + created_at: '2026-03-30T10:00:00+08:00', + children: [], + }, + { + id: 12012, + identity: '019c7000-0001-7000-8000-000000000012', + title: 'Syslog 规则', + title_en: 'Syslog Rules', + code: 'ops:日志采集:syslog规则', + app_id: 2, + parent_id: 12010, + menu_path: '/log-mgmt/syslog-rules', + menu_icon: 'Code', + component: 'ops/pages/log-mgmt/syslog-rules', + type: 1, + sort_key: 2, + created_at: '2026-03-30T10:00:00+08:00', + children: [], + }, + { + id: 12013, + identity: '019c7000-0001-7000-8000-000000000013', + title: 'Trap 规则', + title_en: 'Trap Rules', + code: 'ops:日志采集:trap规则', + app_id: 2, + parent_id: 12010, + menu_path: '/log-mgmt/trap-rules', + component: 'ops/pages/log-mgmt/trap-rules', + type: 1, + sort_key: 3, + created_at: '2026-03-30T10:00:00+08:00', + children: [], + }, + { + id: 12014, + identity: '019c7000-0001-7000-8000-000000000014', + title: 'Trap 字典', + title_en: 'Trap Dictionary', + code: 'ops:日志采集:trap字典', + app_id: 2, + parent_id: 12010, + menu_path: '/log-mgmt/trap-dictionary', + component: 'ops/pages/log-mgmt/trap-dictionary', + type: 1, + sort_key: 4, + created_at: '2026-03-30T10:00:00+08:00', + children: [], + }, + { + id: 12015, + identity: '019c7000-0001-7000-8000-000000000015', + title: 'Trap 屏蔽', + title_en: 'Trap Suppressions', + code: 'ops:日志采集:trap屏蔽', + app_id: 2, + parent_id: 12010, + menu_path: '/log-mgmt/trap-suppressions', + component: 'ops/pages/log-mgmt/trap-suppressions', + type: 1, + sort_key: 5, + created_at: '2026-03-30T10:00:00+08:00', + children: [], + }, + ], + }, { id: 35, identity: '019b591d-021a-74a3-8092-c15b990f3c7e', @@ -968,10 +1062,11 @@ export const localMenuItems: MenuItem[] = [ title: '系统日志', title_en: 'System Logs', code: 'SystemSettingsSystemLogs', - description: '系统日志', + description: '系统日志(操作审计说明页,演示数据见 index_bak)', app_id: 2, parent_id: 78, menu_path: '/system-settings/system-logs', + component: 'ops/pages/system-settings/system-logs', type: 1, sort_key: 13, created_at: '2025-12-27T12:31:02.556301+08:00', diff --git a/src/views/ops/pages/alert/history/index.vue b/src/views/ops/pages/alert/history/index.vue index 19ca96f..cc05a07 100644 --- a/src/views/ops/pages/alert/history/index.vue +++ b/src/views/ops/pages/alert/history/index.vue @@ -125,7 +125,7 @@ import { columns as columnsConfig } from './config/columns' import { fetchHistories, } from '@/api/ops/alertHistory' -import { fetchAlertLevelList } from '@/api/ops/alertLevel' +import { fetchAlertLevelOptions } from '@/api/ops/alertLevel' import HistoryDetailDialog from './components/HistoryDetailDialog.vue' import HistoryProcessListDialog from './components/HistoryProcessListDialog.vue' @@ -231,10 +231,11 @@ const currentProcessAlertRecordId = computed(() => currentProcessAlertRecord.val // 加载告警级别列表 const loadSeverityOptions = async () => { try { - const res = await fetchAlertLevelList({ page: 1, page_size: 1000, enabled: 'true' }) - if (res.code === 0 && res.details?.data) { - severityOptions.value = res.details.data - } + const res: any = await fetchAlertLevelOptions({ enabled: 'true' }) + const list = Array.isArray(res?.details) + ? res.details + : res?.details?.data || res?.details?.list || res?.data || res + severityOptions.value = Array.isArray(list) ? list : [] } catch (error) { console.error('获取告警级别列表失败:', error) } diff --git a/src/views/ops/pages/alert/notice/components/ChannelFormDialog.vue b/src/views/ops/pages/alert/notice/components/ChannelFormDialog.vue index 0c0c8f4..718f0a4 100644 --- a/src/views/ops/pages/alert/notice/components/ChannelFormDialog.vue +++ b/src/views/ops/pages/alert/notice/components/ChannelFormDialog.vue @@ -92,6 +92,10 @@ placeholder="请选择告警级别(可多选)" multiple allow-clear + allow-search + :filter-option="false" + :loading="severityLoading" + @search="handleSeveritySearch" > !!props.channel?.id) -// 获取告警级别列表 -const fetchSeverityLevels = async () => { +const severityLoading = ref(false) +const baseSeverityLevels = ref([]) + +// 获取告警级别列表(支持 keyword 搜索) +const fetchSeverityLevels = async (keyword?: string) => { + severityLoading.value = true try { - const res = await fetchAlertLevelList({ - page: 1, - page_size: 100, + const res: any = await fetchAlertLevelOptions({ + keyword: keyword || undefined, enabled: 'true', }) - if (res.code === 0 && res.details?.data) { - severityLevels.value = res.details.data + + const list = Array.isArray(res?.details) + ? res.details + : res?.details?.data || res?.details?.list || res?.data || res + const fetchedOptions = Array.isArray(list) ? list : [] + + // keyword 为空时缓存“全量可用选项” + const isBaseLoad = !keyword || keyword.trim().length === 0 + if (isBaseLoad) { + baseSeverityLevels.value = fetchedOptions } + + // 防止搜索后缩小 options 导致已选项标签无法展示 + const selectedCodes = Array.isArray(form.value.severity_filter) ? form.value.severity_filter : [] + const selectedOptions = baseSeverityLevels.value.filter((s) => selectedCodes.includes(s.code)) + + const mergedMap = new Map() + ;[...fetchedOptions, ...selectedOptions].forEach((item) => { + if (item?.code) { + mergedMap.set(item.code, item) + } + }) + + severityLevels.value = Array.from(mergedMap.values()) } catch (error) { console.error('获取告警级别列表失败:', error) + } finally { + severityLoading.value = false } } +let severitySearchTimer: ReturnType | null = null +const handleSeveritySearch = (keyword: string) => { + if (severitySearchTimer) { + clearTimeout(severitySearchTimer) + } + + severitySearchTimer = setTimeout(() => { + fetchSeverityLevels(keyword) + }, 300) +} + // 处理静默时间段变化 const handleQuietHoursChange = () => { if (quietHoursEnabled.value) { diff --git a/src/views/ops/pages/alert/setting/components/PolicyCreateDialog.vue b/src/views/ops/pages/alert/setting/components/PolicyCreateDialog.vue index 8c68d7a..da125ee 100644 --- a/src/views/ops/pages/alert/setting/components/PolicyCreateDialog.vue +++ b/src/views/ops/pages/alert/setting/components/PolicyCreateDialog.vue @@ -164,7 +164,7 @@ import { ref, reactive, watch } from 'vue' import { Message } from '@arco-design/web-vue' import type { FormInstance } from '@arco-design/web-vue' -import { createPolicy, fetchTemplateList, fetchSeverityList, fetchFeedbackTemplateOptions } from '@/api/ops/alertPolicy' +import { createPolicy, fetchTemplateList, fetchSeverityOptions, fetchFeedbackTemplateOptions } from '@/api/ops/alertPolicy' import { fetchUserList } from '@/api/ops/rbac2' interface Props { @@ -241,11 +241,12 @@ const loadTemplateOptions = async () => { const loadSeverityList = async () => { try { - const res = await fetchSeverityList({ page: 1, page_size: 100 }) - if (res.code === 0 && res.details?.data) { - severityList.value = res.details.data - initDispatchRuleData() - } + const res: any = await fetchSeverityOptions({ enabled: 'true' }) + const list = Array.isArray(res?.details) + ? res.details + : res?.details?.data || res?.details?.list || res?.data || res + severityList.value = Array.isArray(list) ? list : [] + initDispatchRuleData() } catch (error) { console.error('获取告警级别失败:', error) } diff --git a/src/views/ops/pages/alert/setting/components/PolicyFormDialog.vue b/src/views/ops/pages/alert/setting/components/PolicyFormDialog.vue index 7a4c10c..91dd81f 100644 --- a/src/views/ops/pages/alert/setting/components/PolicyFormDialog.vue +++ b/src/views/ops/pages/alert/setting/components/PolicyFormDialog.vue @@ -226,7 +226,7 @@ import { fetchPolicyDetail, updatePolicy, fetchTemplateList, - fetchSeverityList, + fetchSeverityOptions, fetchFeedbackTemplateOptions, } from '@/api/ops/alertPolicy' import { fetchUserList } from '@/api/ops/rbac2' @@ -323,10 +323,11 @@ const loadTemplateOptions = async () => { // 加载告警级别 const loadSeverityList = async () => { try { - const res = await fetchSeverityList({ page: 1, page_size: 100 }) - if (res.code === 0 && res.details?.data) { - severityList.value = res.details.data - } + const res: any = await fetchSeverityOptions({ enabled: 'true' }) + const list = Array.isArray(res?.details) + ? res.details + : res?.details?.data || res?.details?.list || res?.data || res + severityList.value = Array.isArray(list) ? list : [] } catch (error) { console.error('获取告警级别失败:', error) } diff --git a/src/views/ops/pages/alert/setting/components/RuleFormDialog.vue b/src/views/ops/pages/alert/setting/components/RuleFormDialog.vue index 504b264..1d12ef0 100644 --- a/src/views/ops/pages/alert/setting/components/RuleFormDialog.vue +++ b/src/views/ops/pages/alert/setting/components/RuleFormDialog.vue @@ -59,6 +59,9 @@ v-model="formData.severity_id" placeholder="请选择告警级别" :loading="severityLoading" + allow-search + :filter-option="false" + @search="handleSeveritySearch" > ([]) +const baseSeverityOptions = ref([]) // 加载告警级别列表 -const loadSeverityOptions = async () => { +const loadSeverityOptions = async (keyword?: string) => { severityLoading.value = true try { - const res = await fetchSeverityList({ page: 1, page_size: 100 }) - if (res.code === 0 && res.details?.data) { - severityOptions.value = res.details.data + const res: any = await fetchSeverityOptions({ + keyword, + enabled: 'true', + }) + + const list = Array.isArray(res?.details) + ? res.details + : res?.details?.data || res?.details?.list || res?.data || res + const fetchedOptions = Array.isArray(list) ? list : [] + + // 首次/keyword 为空时缓存“全量可用选项” + const isBaseLoad = !keyword || keyword.trim().length === 0 + if (isBaseLoad) { + baseSeverityOptions.value = fetchedOptions } + + // 防止搜索后缩小 options 导致当前已选项标签无法展示 + const selectedId = formData.severity_id + const selectedOptions = baseSeverityOptions.value.filter((s) => s?.id === selectedId) + + const mergedMap = new Map() + ;[...fetchedOptions, ...selectedOptions].forEach((item) => { + if (typeof item?.id === 'number') { + mergedMap.set(item.id, item) + } + }) + + severityOptions.value = Array.from(mergedMap.values()) } catch (error) { console.error('获取告警级别失败:', error) } finally { @@ -261,6 +289,17 @@ const loadSeverityOptions = async () => { } } +let severitySearchTimer: ReturnType | null = null +const handleSeveritySearch = (keyword: string) => { + if (severitySearchTimer) { + clearTimeout(severitySearchTimer) + } + + severitySearchTimer = setTimeout(() => { + loadSeverityOptions(keyword) + }, 300) +} + // 规则类型变化处理 const handleRuleTypeChange = (value: string) => { // 根据规则类型清空相关字段 diff --git a/src/views/ops/pages/alert/setting/components/RuleManageDialog.vue b/src/views/ops/pages/alert/setting/components/RuleManageDialog.vue index f9d12db..d25a37b 100644 --- a/src/views/ops/pages/alert/setting/components/RuleManageDialog.vue +++ b/src/views/ops/pages/alert/setting/components/RuleManageDialog.vue @@ -121,11 +121,7 @@ import { ref, reactive, watch } from 'vue' import { Message } from '@arco-design/web-vue' import { IconPlus } from '@arco-design/web-vue/es/icon' -import { - fetchRuleList, - deleteRule, - fetchSeverityList, -} from '@/api/ops/alertPolicy' +import { fetchRuleList, deleteRule } from '@/api/ops/alertPolicy' import RuleFormDialog from './RuleFormDialog.vue' // Props diff --git a/src/views/ops/pages/alert/tackle/index.vue b/src/views/ops/pages/alert/tackle/index.vue index 8c8f3df..e410573 100644 --- a/src/views/ops/pages/alert/tackle/index.vue +++ b/src/views/ops/pages/alert/tackle/index.vue @@ -169,7 +169,7 @@ import SearchTable from '@/components/search-table/index.vue' import { searchFormConfig } from './config/search-form' import { columns } from './config/columns' import { fetchAlertRecords } from '@/api/ops/alertRecord' -import { fetchAlertLevelList } from '@/api/ops/alertLevel' +import { fetchAlertLevelOptions } from '@/api/ops/alertLevel' import AckDialog from './components/AckDialog.vue' import ResolveDialog from './components/ResolveDialog.vue' @@ -223,9 +223,11 @@ onMounted(async () => { const loadSeverityOptions = async () => { try { - const res = await fetchAlertLevelList({ page: 1, page_size: 100 }) - const list = res.details?.data ?? (res as any).data ?? [] - severityOptions.value = list.map((item: any) => ({ + const res: any = await fetchAlertLevelOptions({ enabled: 'true' }) + const list = Array.isArray(res?.details) + ? res.details + : res?.details?.data || res?.details?.list || res?.data || res || [] + severityOptions.value = (Array.isArray(list) ? list : []).map((item: any) => ({ label: item.name || item.code, value: item.id, })) diff --git a/src/views/ops/pages/alert/template/components/RuleEditor.vue b/src/views/ops/pages/alert/template/components/RuleEditor.vue index 3b96073..3619504 100644 --- a/src/views/ops/pages/alert/template/components/RuleEditor.vue +++ b/src/views/ops/pages/alert/template/components/RuleEditor.vue @@ -55,8 +55,17 @@ - - + + {{ item.name }} @@ -125,6 +134,7 @@ import { ref, watch } from 'vue' import type { FormInstance } from '@arco-design/web-vue' import { + fetchSeverityOptions, fetchMetricsMeta, DATA_SOURCES, COMPARE_OPERATORS, @@ -155,6 +165,11 @@ const ruleFormRef = ref(null) // 本地规则数据(深拷贝) const localRules = ref([]) +// 告警级别下拉选项(支持搜索后局部刷新) +const baseSeverityOptions = ref([]) +const localSeverityOptions = ref([]) +const severityLoading = ref(false) + // 监听 props 变化,深拷贝到本地 watch( () => props.rules, @@ -164,6 +179,70 @@ watch( { immediate: true, deep: true } ) +// 初始化/同步告警级别选项 +watch( + () => props.severityOptions, + (val) => { + baseSeverityOptions.value = val || [] + localSeverityOptions.value = val || [] + }, + { immediate: true }, +) + +let severitySearchTimer: ReturnType | null = null + +const loadSeverityOptions = async (keyword?: string) => { + severityLoading.value = true + try { + const res: any = await fetchSeverityOptions({ + keyword: keyword || undefined, + enabled: 'true', + }) + + const list = Array.isArray(res?.details) + ? res.details + : res?.details?.data || res?.details?.list || res?.data || res + const fetchedOptions = Array.isArray(list) ? list : [] + + // 合并当前已选值,防止搜索后 options 缩小导致已选项标签无法展示 + const selectedCodes = new Set( + localRules.value + .map((r) => r?.severity_code) + .filter((v): v is string => typeof v === 'string' && v.length > 0), + ) + const selectedOptions = baseSeverityOptions.value.filter((s) => selectedCodes.has(s.code)) + + const mergedMap = new Map() + ;[...fetchedOptions, ...selectedOptions].forEach((item) => { + if (item?.code) { + mergedMap.set(item.code, item) + } + }) + + localSeverityOptions.value = Array.from(mergedMap.values()) + } catch (error) { + console.error('加载告警级别下拉选项失败:', error) + } finally { + severityLoading.value = false + } +} + +const handleSeverityDropdownChange = (visible: boolean) => { + if (visible && localSeverityOptions.value.length === 0) { + loadSeverityOptions() + } +} + +const handleSeveritySearch = (keyword: string) => { + if (severitySearchTimer) { + clearTimeout(severitySearchTimer) + } + + severitySearchTimer = setTimeout(() => { + loadSeverityOptions(keyword) + }, 300) +} + // 动态生成验证规则 const getRuleFieldRules = (field: string) => { const messages: Record = { diff --git a/src/views/ops/pages/alert/template/edit/index.vue b/src/views/ops/pages/alert/template/edit/index.vue index 14ddd03..b70eb52 100644 --- a/src/views/ops/pages/alert/template/edit/index.vue +++ b/src/views/ops/pages/alert/template/edit/index.vue @@ -163,7 +163,7 @@ import { fetchTemplateDetail, fetchChannelList, fetchSuppressionList, - fetchSeverityList, + fetchSeverityOptions, fetchMetricsMeta, TEMPLATE_CATEGORIES, type AlertTemplate, @@ -330,10 +330,12 @@ const loadMetricsForRule = async (index: number) => { // 加载告警级别 const loadSeverityOptions = async () => { try { - const res: any = await fetchSeverityList() - if (res.code === 0) { - severityOptions.value = res.details?.data || res.details?.list || [] - } + const res: any = await fetchSeverityOptions({ enabled: 'true' }) + // 兼容:后端可能返回数组,也可能是 { code, details: { data } } + const list = Array.isArray(res?.details) + ? res.details + : res?.details?.data || res?.details?.list || res?.data || res + severityOptions.value = Array.isArray(list) ? list : [] } catch (error) { console.error('加载告警级别失败:', error) } diff --git a/src/views/ops/pages/log-mgmt/entries/index.vue b/src/views/ops/pages/log-mgmt/entries/index.vue new file mode 100644 index 0000000..6092cdd --- /dev/null +++ b/src/views/ops/pages/log-mgmt/entries/index.vue @@ -0,0 +1,245 @@ + + + + + + + diff --git a/src/views/ops/pages/log-mgmt/syslog-rules/index.vue b/src/views/ops/pages/log-mgmt/syslog-rules/index.vue new file mode 100644 index 0000000..45f162c --- /dev/null +++ b/src/views/ops/pages/log-mgmt/syslog-rules/index.vue @@ -0,0 +1,455 @@ + + + + + + + diff --git a/src/views/ops/pages/log-mgmt/trap-dictionary/index.vue b/src/views/ops/pages/log-mgmt/trap-dictionary/index.vue new file mode 100644 index 0000000..7dfddf8 --- /dev/null +++ b/src/views/ops/pages/log-mgmt/trap-dictionary/index.vue @@ -0,0 +1,353 @@ + + + + + + + diff --git a/src/views/ops/pages/log-mgmt/trap-rules/index.vue b/src/views/ops/pages/log-mgmt/trap-rules/index.vue new file mode 100644 index 0000000..86ecd6c --- /dev/null +++ b/src/views/ops/pages/log-mgmt/trap-rules/index.vue @@ -0,0 +1,447 @@ + + + + + + + diff --git a/src/views/ops/pages/log-mgmt/trap-suppressions/index.vue b/src/views/ops/pages/log-mgmt/trap-suppressions/index.vue new file mode 100644 index 0000000..0c79222 --- /dev/null +++ b/src/views/ops/pages/log-mgmt/trap-suppressions/index.vue @@ -0,0 +1,290 @@ + + + + + + + diff --git a/src/views/ops/pages/monitor/log/index.vue b/src/views/ops/pages/monitor/log/index.vue index d8ad738..61478e8 100644 --- a/src/views/ops/pages/monitor/log/index.vue +++ b/src/views/ops/pages/monitor/log/index.vue @@ -1,542 +1,11 @@ - - \ No newline at end of file diff --git a/src/views/ops/pages/monitor/log/index_bak.vue b/src/views/ops/pages/monitor/log/index_bak.vue new file mode 100644 index 0000000..d8ad738 --- /dev/null +++ b/src/views/ops/pages/monitor/log/index_bak.vue @@ -0,0 +1,542 @@ + + + + + + + \ No newline at end of file diff --git a/src/views/ops/pages/system-settings/system-logs/index.vue b/src/views/ops/pages/system-settings/system-logs/index.vue index 22d5a66..ea47920 100644 --- a/src/views/ops/pages/system-settings/system-logs/index.vue +++ b/src/views/ops/pages/system-settings/system-logs/index.vue @@ -2,331 +2,48 @@
- - + + + +