diff --git a/src/api/ops/alertPolicy.ts b/src/api/ops/alertPolicy.ts index 79b2b31..419da19 100644 --- a/src/api/ops/alertPolicy.ts +++ b/src/api/ops/alertPolicy.ts @@ -72,7 +72,9 @@ export const createRule = (data: { threshold?: number; compare_op?: string; duration?: number; + baseline_config?: string; labels?: string; + annotations?: string; }) => request.post("/Alert/v1/rule/create", data); /** 更新 告警规则 */ @@ -88,7 +90,9 @@ export const updateRule = (data: { threshold?: number; compare_op?: string; duration?: number; + baseline_config?: string; labels?: string; + annotations?: string; }) => request.post("/Alert/v1/rule/update", data); /** 删除 告警规则 */ diff --git a/src/api/ops/alertRecord.ts b/src/api/ops/alertRecord.ts index b5f49d1..3701971 100644 --- a/src/api/ops/alertRecord.ts +++ b/src/api/ops/alertRecord.ts @@ -33,7 +33,8 @@ export const createAlertProcess = (data: { escalate_to?: string; root_cause?: string; solution?: string; - metadata?: Record; + // 按后端约定:metadata 为 JSON 字符串,例如 "{}" + metadata?: string; }) => request.post("/Alert/v1/process/create", data); /** 获取 告警处理记录列表 */ diff --git a/src/views/ops/pages/alert/history/components/HistoryDetailDialog.vue b/src/views/ops/pages/alert/history/components/HistoryDetailDialog.vue index f1c9073..d84d0e6 100644 --- a/src/views/ops/pages/alert/history/components/HistoryDetailDialog.vue +++ b/src/views/ops/pages/alert/history/components/HistoryDetailDialog.vue @@ -52,12 +52,6 @@ - - - {{ getProcessStatusText(recordDetail.process_status) }} - - - - {{ recordDetail.processed_by || '-' }} @@ -188,28 +182,6 @@ const getStatusText = (status: string) => { return textMap[status] || status } -// 获取处理状态颜色 -const getProcessStatusColor = (status: string) => { - const colorMap: Record = { - pending: 'gray', - processing: 'blue', - completed: 'green', - failed: 'red', - } - return colorMap[status] || 'gray' -} - -// 获取处理状态文本 -const getProcessStatusText = (status: string) => { - const textMap: Record = { - pending: '待处理', - processing: '处理中', - completed: '已完成', - failed: '失败', - } - return textMap[status] || status -} - // 加载告警记录详情 const loadRecordDetail = async () => { if (!props.recordId) return diff --git a/src/views/ops/pages/alert/history/components/HistoryProcessListDialog.vue b/src/views/ops/pages/alert/history/components/HistoryProcessListDialog.vue new file mode 100644 index 0000000..1ff5ef8 --- /dev/null +++ b/src/views/ops/pages/alert/history/components/HistoryProcessListDialog.vue @@ -0,0 +1,372 @@ + + + + + + diff --git a/src/views/ops/pages/alert/history/config/columns.ts b/src/views/ops/pages/alert/history/config/columns.ts index cc358dd..b44d04a 100644 --- a/src/views/ops/pages/alert/history/config/columns.ts +++ b/src/views/ops/pages/alert/history/config/columns.ts @@ -51,12 +51,6 @@ export const columns: TableColumnData[] = [ slotName: 'duration', width: 120, }, - { - title: '处理状态', - dataIndex: 'process_status', - slotName: 'process_status', - width: 100, - }, { title: '处理人', dataIndex: 'processed_by', diff --git a/src/views/ops/pages/alert/history/index.vue b/src/views/ops/pages/alert/history/index.vue index 55005e3..19ca96f 100644 --- a/src/views/ops/pages/alert/history/index.vue +++ b/src/views/ops/pages/alert/history/index.vue @@ -85,14 +85,6 @@ {{ formatDuration(record.duration) }} - - - @@ -122,13 +121,13 @@ import { ref, reactive, computed, onMounted } from 'vue' import { Message } from '@arco-design/web-vue' import type { FormItem } from '@/components/search-form/types' import SearchTable from '@/components/search-table/index.vue' -import { searchFormConfig } from './config/search-form' import { columns as columnsConfig } from './config/columns' import { fetchHistories, } from '@/api/ops/alertHistory' import { fetchAlertLevelList } from '@/api/ops/alertLevel' import HistoryDetailDialog from './components/HistoryDetailDialog.vue' +import HistoryProcessListDialog from './components/HistoryProcessListDialog.vue' // 状态管理 const loading = ref(false) @@ -222,6 +221,13 @@ const currentRecordId = ref(undefined) // 对话框可见性 const detailVisible = ref(false) +// 处理记录对话框可见性 +const processVisible = ref(false) + +// 当前用于“处理记录”的告警记录 +const currentProcessAlertRecord = ref(null) +const currentProcessAlertRecordId = computed(() => currentProcessAlertRecord.value?.id) + // 加载告警级别列表 const loadSeverityOptions = async () => { try { @@ -321,28 +327,6 @@ const getStatusText = (status: string) => { return textMap[status] || status } -// 获取处理状态颜色 -const getProcessStatusColor = (status: string) => { - const colorMap: Record = { - pending: 'gray', - processing: 'blue', - completed: 'green', - failed: 'red', - } - return colorMap[status] || 'gray' -} - -// 获取处理状态文本 -const getProcessStatusText = (status: string) => { - const textMap: Record = { - pending: '待处理', - processing: '处理中', - completed: '已完成', - failed: '失败', - } - return textMap[status] || status -} - // 搜索 const handleSearch = () => { pagination.current = 1 @@ -390,7 +374,8 @@ const handleDetail = (record: any) => { // 查看处理记录 const handleViewProcess = (record: any) => { - Message.info(`查看告警 ${record.id} 的处理记录功能待实现`) + currentProcessAlertRecord.value = record + processVisible.value = true } // 初始化加载数据 diff --git a/src/views/ops/pages/alert/notice/components/ChannelFormDialog.vue b/src/views/ops/pages/alert/notice/components/ChannelFormDialog.vue index e14f335..0c0c8f4 100644 --- a/src/views/ops/pages/alert/notice/components/ChannelFormDialog.vue +++ b/src/views/ops/pages/alert/notice/components/ChannelFormDialog.vue @@ -355,6 +355,9 @@ const handleOk = async () => { submitting.value = true try { + const quietHoursPayload = + form.value.quiet_hours && form.value.quiet_hours.trim() ? form.value.quiet_hours : '{}' + const data: any = { name: form.value.name, type: form.value.type, @@ -365,7 +368,7 @@ const handleOk = async () => { severity_filter: Array.isArray(form.value.severity_filter) ? form.value.severity_filter.join(',') : form.value.severity_filter, - quiet_hours: form.value.quiet_hours, + quiet_hours: quietHoursPayload, retry_times: form.value.retry_times, retry_interval: form.value.retry_interval, enabled: form.value.enabled, diff --git a/src/views/ops/pages/alert/notice/index.vue b/src/views/ops/pages/alert/notice/index.vue index 6ebd007..f7c4cca 100644 --- a/src/views/ops/pages/alert/notice/index.vue +++ b/src/views/ops/pages/alert/notice/index.vue @@ -100,7 +100,7 @@ const pagination = reactive({ }) const formModel = ref>({ - name: '', + keyword: '', type: '', }) @@ -111,7 +111,7 @@ const handleFormModelUpdate = (value: Record) => { // 表单项配置 const formItems = computed(() => [ { - field: 'name', + field: 'keyword', label: '渠道名称', type: 'input', placeholder: '请输入渠道名称', @@ -234,7 +234,7 @@ const handleSearch = () => { // 重置 const handleReset = () => { formModel.value = { - name: '', + keyword: '', type: '', } pagination.current = 1 diff --git a/src/views/ops/pages/alert/setting/components/PolicyCreateDialog.vue b/src/views/ops/pages/alert/setting/components/PolicyCreateDialog.vue index 8705dab..8c68d7a 100644 --- a/src/views/ops/pages/alert/setting/components/PolicyCreateDialog.vue +++ b/src/views/ops/pages/alert/setting/components/PolicyCreateDialog.vue @@ -373,8 +373,11 @@ const handleCancel = () => { } const handleSubmit = async () => { - const valid = await formRef.value?.validate() - if (!valid) return + try { + await formRef.value?.validate() + } catch { + return + } submitLoading.value = true try { @@ -391,7 +394,11 @@ const handleSubmit = async () => { dispatch_rule: formData.dispatch_rule || undefined, } - await createPolicy(data) + const res = await createPolicy(data) + // 后端是业务 code 约定;接口返回失败时不应继续展示成功提示 + if (res?.code !== 0) { + throw new Error(res?.message || '创建策略失败') + } Message.success('策略创建成功') emit('success') handleCancel() diff --git a/src/views/ops/pages/alert/setting/components/PolicyFormDialog.vue b/src/views/ops/pages/alert/setting/components/PolicyFormDialog.vue index bdbcf48..7a4c10c 100644 --- a/src/views/ops/pages/alert/setting/components/PolicyFormDialog.vue +++ b/src/views/ops/pages/alert/setting/components/PolicyFormDialog.vue @@ -510,13 +510,16 @@ const handleCancel = () => { resetForm() } -// 提交 +// 提交(Arco Form.validate() 校验通过时 resolve 为 undefined,不能写成 if (!valid)) const handleSubmit = async () => { - const valid = await formRef.value?.validate() - if (!valid) return - + try { + await formRef.value?.validate() + } catch { + return + } + submitLoading.value = true - + try { // 生成派单规则 formData.dispatch_rule = generateDispatchRule() diff --git a/src/views/ops/pages/alert/setting/components/RuleFormDialog.vue b/src/views/ops/pages/alert/setting/components/RuleFormDialog.vue index 0ecaf0b..504b264 100644 --- a/src/views/ops/pages/alert/setting/components/RuleFormDialog.vue +++ b/src/views/ops/pages/alert/setting/components/RuleFormDialog.vue @@ -29,11 +29,8 @@ 静态规则 - 动态规则 - PromQL @@ -89,33 +86,6 @@ /> - - - - - - - - @@ -316,7 +286,8 @@ const loadRuleDetail = async () => { const rule = res.details formData.name = rule.name - formData.rule_type = rule.rule_type || 'static' + // 当前产品只保留静态规则:强制按 static 渲染 + formData.rule_type = 'static' formData.enabled = rule.enabled formData.severity_id = rule.severity_id formData.metric_name = rule.metric_name || '' @@ -357,11 +328,14 @@ const handleCancel = () => { resetForm() } -// 提交 +// 提交(Arco Form.validate() 通过时 resolve 为 undefined,不可写成 if (!valid)) const handleSubmit = async () => { - const valid = await formRef.value?.validate() - if (!valid) return - + try { + await formRef.value?.validate() + } catch { + return + } + if (!props.policyId) { Message.warning('缺少策略 ID') return @@ -376,13 +350,24 @@ const handleSubmit = async () => { severity_id: formData.severity_id, // 确保 severity_id 是 number 类型 } + // 按契约:rule/create 不做 jsonb 默认值归一化。 + // 当 jsonb 字段无内容时,必须显式传 "{}"(否则可能反序列化为空串导致 SQLSTATE 22P02)。 + const createPayload = !isEdit.value + ? { + ...data, + baseline_config: '{}', + labels: '{}', + annotations: '{}', + } + : data + // 编辑模式 if (isEdit.value && props.ruleId) { await updateRule({ id: props.ruleId, ...data }) Message.success('规则更新成功') } else { // 新建模式 - await createRule(data) + await createRule(createPayload) Message.success('规则创建成功') } diff --git a/src/views/ops/pages/alert/tackle/components/AckDialog.vue b/src/views/ops/pages/alert/tackle/components/AckDialog.vue index eba5994..eb236ba 100644 --- a/src/views/ops/pages/alert/tackle/components/AckDialog.vue +++ b/src/views/ops/pages/alert/tackle/components/AckDialog.vue @@ -62,6 +62,7 @@ const handleOk = async () => { action: 'ack', operator: getCurrentUser(), comment: form.value.comment, + metadata: '{}', }) Message.success('确认成功') emit('success') diff --git a/src/views/ops/pages/alert/tackle/components/CommentDialog.vue b/src/views/ops/pages/alert/tackle/components/CommentDialog.vue index 97093ba..4ca32d3 100644 --- a/src/views/ops/pages/alert/tackle/components/CommentDialog.vue +++ b/src/views/ops/pages/alert/tackle/components/CommentDialog.vue @@ -67,6 +67,7 @@ const handleOk = async () => { action: 'comment', operator: getCurrentUser(), comment: form.value.comment, + metadata: '{}', }) Message.success('评论添加成功') emit('success') diff --git a/src/views/ops/pages/alert/tackle/components/DetailDialog.vue b/src/views/ops/pages/alert/tackle/components/DetailDialog.vue index 2be029d..3e11b7f 100644 --- a/src/views/ops/pages/alert/tackle/components/DetailDialog.vue +++ b/src/views/ops/pages/alert/tackle/components/DetailDialog.vue @@ -61,9 +61,6 @@ - - {{ record.process_status || '-' }} - {{ record.processed_by || '-' }} diff --git a/src/views/ops/pages/alert/tackle/components/ResolveDialog.vue b/src/views/ops/pages/alert/tackle/components/ResolveDialog.vue index 442f10b..ab03841 100644 --- a/src/views/ops/pages/alert/tackle/components/ResolveDialog.vue +++ b/src/views/ops/pages/alert/tackle/components/ResolveDialog.vue @@ -82,6 +82,7 @@ const handleOk = async () => { comment: form.value.comment, root_cause: form.value.root_cause, solution: form.value.solution, + metadata: '{}', }) Message.success('解决成功') emit('success') diff --git a/src/views/ops/pages/alert/tackle/components/SilenceDialog.vue b/src/views/ops/pages/alert/tackle/components/SilenceDialog.vue index 0329035..38b397a 100644 --- a/src/views/ops/pages/alert/tackle/components/SilenceDialog.vue +++ b/src/views/ops/pages/alert/tackle/components/SilenceDialog.vue @@ -93,6 +93,7 @@ const handleOk = async () => { silence_until: silenceUntil, silence_reason: form.value.silence_reason, comment: form.value.comment, + metadata: '{}', }) Message.success('屏蔽成功') emit('success') diff --git a/src/views/ops/pages/alert/tackle/config/columns.ts b/src/views/ops/pages/alert/tackle/config/columns.ts index cb2f974..ef4b068 100644 --- a/src/views/ops/pages/alert/tackle/config/columns.ts +++ b/src/views/ops/pages/alert/tackle/config/columns.ts @@ -57,12 +57,6 @@ export const columns: TableColumnData[] = [ slotName: 'duration', width: 120, }, - { - title: '处理状态', - dataIndex: 'process_status', - slotName: 'process_status', - width: 100, - }, { title: '处理人', dataIndex: 'processed_by', diff --git a/src/views/ops/pages/alert/tackle/index.vue b/src/views/ops/pages/alert/tackle/index.vue index e2ea0c6..8c8f3df 100644 --- a/src/views/ops/pages/alert/tackle/index.vue +++ b/src/views/ops/pages/alert/tackle/index.vue @@ -63,13 +63,6 @@ {{ formatDuration(record.duration) }} - - @@ -90,16 +83,36 @@