From 9f1c3eb660b5770a483577b3fbf1d802574b0872 Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Sun, 5 Jul 2026 17:48:49 +0800 Subject: [PATCH 01/14] feat: add snmp oid editor state helpers --- .../device-collect/components/FormDialog.vue | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue index 389ea3f..e191900 100644 --- a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue +++ b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue @@ -211,6 +211,18 @@ const confirmLoading = ref(false) const policyOptions = ref([]) const roomOptions = ref([]) +type SnmpOidEditMode = 'table' | 'json' + +interface SnmpOidRow { + oid: string + metric_name: string + metric_unit: string + type: string +} + +const snmpOidEditMode = ref('table') +const snmpOidRows = ref([]) + const isEdit = computed(() => !!props.record?.id) const formData = reactive({ @@ -246,6 +258,53 @@ const rules = { device_category: [{ required: true, message: '请选择设备分类' }], } +const createEmptySnmpOidRow = (): SnmpOidRow => ({ + oid: '', + metric_name: '', + metric_unit: '', + type: '', +}) + +const normalizeSnmpOidRows = (rows: SnmpOidRow[]) => + rows + .map((row) => ({ + oid: row.oid.trim(), + metric_name: row.metric_name.trim(), + metric_unit: row.metric_unit.trim(), + type: row.type.trim(), + })) + .filter((row) => row.oid || row.metric_name || row.metric_unit || row.type) + +const stringifySnmpOidRows = (rows: SnmpOidRow[]) => { + const normalizedRows = normalizeSnmpOidRows(rows) + return normalizedRows.length ? JSON.stringify(normalizedRows, null, 2) : '' +} + +const parseSnmpOidRows = (raw: string): SnmpOidRow[] => { + const text = raw.trim() + if (!text) { + return [] + } + const parsed = JSON.parse(text) + if (!Array.isArray(parsed)) { + throw new Error('SNMP OID 配置必须是 JSON 数组') + } + return parsed.map((item) => ({ + oid: String(item?.oid ?? ''), + metric_name: String(item?.metric_name ?? ''), + metric_unit: String(item?.metric_unit ?? ''), + type: String(item?.type ?? ''), + })) +} + +const syncSnmpOidRowsFromJson = () => { + snmpOidRows.value = parseSnmpOidRows(formData.snmp_oids || '') +} + +const syncSnmpOidJsonFromRows = () => { + formData.snmp_oids = stringifySnmpOidRows(snmpOidRows.value) +} + const loadPolicyOptions = async () => { try { const response: any = await fetchPolicyOptions({ enabled: true }) From 99a5f8bd16a502c82cb4f3d467a26b23c92930d8 Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Sun, 5 Jul 2026 17:54:23 +0800 Subject: [PATCH 02/14] fix: trim parsed snmp oid rows --- .../ops/pages/dc/device-collect/components/FormDialog.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue index e191900..927480e 100644 --- a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue +++ b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue @@ -290,10 +290,10 @@ const parseSnmpOidRows = (raw: string): SnmpOidRow[] => { throw new Error('SNMP OID 配置必须是 JSON 数组') } return parsed.map((item) => ({ - oid: String(item?.oid ?? ''), - metric_name: String(item?.metric_name ?? ''), - metric_unit: String(item?.metric_unit ?? ''), - type: String(item?.type ?? ''), + oid: String(item?.oid ?? '').trim(), + metric_name: String(item?.metric_name ?? '').trim(), + metric_unit: String(item?.metric_unit ?? '').trim(), + type: String(item?.type ?? '').trim(), })) } From fafdb0d830fc0639744b956351407f92a37daaf2 Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Sun, 5 Jul 2026 18:00:17 +0800 Subject: [PATCH 03/14] feat: initialize snmp oid editor state --- .../device-collect/components/FormDialog.vue | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue index 927480e..290b6bd 100644 --- a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue +++ b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue @@ -368,6 +368,13 @@ watch( collect_interval: props.record.collect_interval || 60, policy_ids: props.record.policy_ids || [], }) + snmpOidEditMode.value = 'table' + try { + syncSnmpOidRowsFromJson() + } catch { + snmpOidEditMode.value = 'json' + snmpOidRows.value = [] + } } else { Object.assign(formData, { name: '', @@ -395,11 +402,44 @@ watch( collect_interval: 60, policy_ids: [], }) + snmpOidEditMode.value = 'table' + snmpOidRows.value = [] } } } ) +const addSnmpOidRow = () => { + if (snmpOidEditMode.value !== 'table') { + return + } + snmpOidRows.value.push(createEmptySnmpOidRow()) + syncSnmpOidJsonFromRows() +} + +const removeSnmpOidRow = (index: number) => { + snmpOidRows.value.splice(index, 1) + syncSnmpOidJsonFromRows() +} + +const handleSnmpOidRowChange = () => { + syncSnmpOidJsonFromRows() +} + +const switchSnmpOidEditMode = () => { + if (snmpOidEditMode.value === 'table') { + syncSnmpOidJsonFromRows() + snmpOidEditMode.value = 'json' + return + } + try { + syncSnmpOidRowsFromJson() + snmpOidEditMode.value = 'table' + } catch { + Message.warning('SNMP OID 配置必须是合法 JSON 数组,修正后才能切换到表格模式') + } +} + const handleOk = async () => { try { await formRef.value?.validate() From fc5d653433a92c4b089612a545e01fca90973235 Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Sun, 5 Jul 2026 18:07:51 +0800 Subject: [PATCH 04/14] feat: add snmp oid table editor ui --- .../device-collect/components/FormDialog.vue | 71 ++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue index 290b6bd..5e5fd7a 100644 --- a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue +++ b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue @@ -140,10 +140,59 @@ - + + + + + @@ -573,3 +622,21 @@ onMounted(() => { loadRoomOptions() }) + + From 93e8f3349f2686b697da1554af19a1aff2fe0aee Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Sun, 5 Jul 2026 18:15:45 +0800 Subject: [PATCH 05/14] feat: validate snmp oid table rows --- .../device-collect/components/FormDialog.vue | 43 ++++++++++++++++--- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue index 5e5fd7a..0c943c0 100644 --- a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue +++ b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue @@ -489,6 +489,40 @@ const switchSnmpOidEditMode = () => { } } +const validateSnmpOidConfigBeforeSubmit = () => { + if (snmpOidEditMode.value === 'table') { + const rows = normalizeSnmpOidRows(snmpOidRows.value) + const invalidIndex = rows.findIndex((row) => !row.oid || !row.metric_name) + if (invalidIndex >= 0) { + Message.warning(`SNMP OID 第 ${invalidIndex + 1} 行请填写 OID 和指标名称`) + return false + } + formData.snmp_oids = rows.length ? JSON.stringify(rows, null, 2) : '' + return true + } + + if (!formData.snmp_oids?.trim()) { + snmpOidRows.value = [] + return true + } + + try { + const rows = parseSnmpOidRows(formData.snmp_oids) + const normalizedRows = normalizeSnmpOidRows(rows) + const invalidIndex = normalizedRows.findIndex((row) => !row.oid || !row.metric_name) + if (invalidIndex >= 0) { + Message.warning(`SNMP OID 第 ${invalidIndex + 1} 行请填写 OID 和指标名称`) + return false + } + snmpOidRows.value = normalizedRows + formData.snmp_oids = normalizedRows.length ? JSON.stringify(normalizedRows, null, 2) : '' + return true + } catch { + Message.warning('SNMP OID 配置必须是合法 JSON 数组') + return false + } +} + const handleOk = async () => { try { await formRef.value?.validate() @@ -518,13 +552,8 @@ const handleOk = async () => { Message.warning('SNMP v2c 模式下请填写 community') return } - if (formData.snmp_oids?.trim()) { - try { - JSON.parse(formData.snmp_oids) - } catch { - Message.warning('SNMP OID 配置必须是合法 JSON') - return - } + if (!validateSnmpOidConfigBeforeSubmit()) { + return } } From c6d0df6fd2bdfc150e26416d044c62f9f715a009 Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Sun, 5 Jul 2026 18:18:47 +0800 Subject: [PATCH 06/14] fix: normalize blank snmp oid json --- src/views/ops/pages/dc/device-collect/components/FormDialog.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue index 0c943c0..63262c4 100644 --- a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue +++ b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue @@ -503,6 +503,7 @@ const validateSnmpOidConfigBeforeSubmit = () => { if (!formData.snmp_oids?.trim()) { snmpOidRows.value = [] + formData.snmp_oids = '' return true } From c3f38f5e2a371a7a27e084780e1fea5229a0e959 Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Sun, 5 Jul 2026 18:22:25 +0800 Subject: [PATCH 07/14] fix: preserve snmp oid validation row numbers --- .../device-collect/components/FormDialog.vue | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue index 63262c4..c67a6dd 100644 --- a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue +++ b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue @@ -324,6 +324,19 @@ const normalizeSnmpOidRows = (rows: SnmpOidRow[]) => })) .filter((row) => row.oid || row.metric_name || row.metric_unit || row.type) +const findIncompleteSnmpOidRowIndex = (rows: SnmpOidRow[]) => + rows.findIndex((row) => { + const normalizedRow = { + oid: row.oid.trim(), + metric_name: row.metric_name.trim(), + metric_unit: row.metric_unit.trim(), + type: row.type.trim(), + } + const hasAnyValue = + normalizedRow.oid || normalizedRow.metric_name || normalizedRow.metric_unit || normalizedRow.type + return Boolean(hasAnyValue && (!normalizedRow.oid || !normalizedRow.metric_name)) + }) + const stringifySnmpOidRows = (rows: SnmpOidRow[]) => { const normalizedRows = normalizeSnmpOidRows(rows) return normalizedRows.length ? JSON.stringify(normalizedRows, null, 2) : '' @@ -491,12 +504,12 @@ const switchSnmpOidEditMode = () => { const validateSnmpOidConfigBeforeSubmit = () => { if (snmpOidEditMode.value === 'table') { - const rows = normalizeSnmpOidRows(snmpOidRows.value) - const invalidIndex = rows.findIndex((row) => !row.oid || !row.metric_name) + const invalidIndex = findIncompleteSnmpOidRowIndex(snmpOidRows.value) if (invalidIndex >= 0) { Message.warning(`SNMP OID 第 ${invalidIndex + 1} 行请填写 OID 和指标名称`) return false } + const rows = normalizeSnmpOidRows(snmpOidRows.value) formData.snmp_oids = rows.length ? JSON.stringify(rows, null, 2) : '' return true } @@ -509,12 +522,12 @@ const validateSnmpOidConfigBeforeSubmit = () => { try { const rows = parseSnmpOidRows(formData.snmp_oids) - const normalizedRows = normalizeSnmpOidRows(rows) - const invalidIndex = normalizedRows.findIndex((row) => !row.oid || !row.metric_name) + const invalidIndex = findIncompleteSnmpOidRowIndex(rows) if (invalidIndex >= 0) { Message.warning(`SNMP OID 第 ${invalidIndex + 1} 行请填写 OID 和指标名称`) return false } + const normalizedRows = normalizeSnmpOidRows(rows) snmpOidRows.value = normalizedRows formData.snmp_oids = normalizedRows.length ? JSON.stringify(normalizedRows, null, 2) : '' return true From 83b713890bfa7ade09ef436105d282b5d49a5ab3 Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Sun, 5 Jul 2026 18:31:47 +0800 Subject: [PATCH 08/14] fix: preserve advanced snmp oid json fields --- .../device-collect/components/FormDialog.vue | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue index c67a6dd..2a947b1 100644 --- a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue +++ b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue @@ -314,6 +314,8 @@ const createEmptySnmpOidRow = (): SnmpOidRow => ({ type: '', }) +const snmpOidTableFields = ['oid', 'metric_name', 'metric_unit', 'type'] + const normalizeSnmpOidRows = (rows: SnmpOidRow[]) => rows .map((row) => ({ @@ -337,6 +339,28 @@ const findIncompleteSnmpOidRowIndex = (rows: SnmpOidRow[]) => return Boolean(hasAnyValue && (!normalizedRow.oid || !normalizedRow.metric_name)) }) +const parseSnmpOidJsonArray = (raw: string) => { + const text = raw.trim() + if (!text) { + return [] + } + const parsed = JSON.parse(text) + if (!Array.isArray(parsed)) { + throw new Error('SNMP OID 配置必须是 JSON 数组') + } + return parsed +} + +const hasSnmpOidTableUnsupportedFields = (raw: string) => { + const rows = parseSnmpOidJsonArray(raw) + return rows.some((item) => { + if (!item || typeof item !== 'object' || Array.isArray(item)) { + return Boolean(item) + } + return Object.keys(item).some((key) => !snmpOidTableFields.includes(key)) + }) +} + const stringifySnmpOidRows = (rows: SnmpOidRow[]) => { const normalizedRows = normalizeSnmpOidRows(rows) return normalizedRows.length ? JSON.stringify(normalizedRows, null, 2) : '' @@ -347,10 +371,7 @@ const parseSnmpOidRows = (raw: string): SnmpOidRow[] => { if (!text) { return [] } - const parsed = JSON.parse(text) - if (!Array.isArray(parsed)) { - throw new Error('SNMP OID 配置必须是 JSON 数组') - } + const parsed = parseSnmpOidJsonArray(text) return parsed.map((item) => ({ oid: String(item?.oid ?? '').trim(), metric_name: String(item?.metric_name ?? '').trim(), @@ -432,7 +453,12 @@ watch( }) snmpOidEditMode.value = 'table' try { - syncSnmpOidRowsFromJson() + if (hasSnmpOidTableUnsupportedFields(formData.snmp_oids || '')) { + snmpOidEditMode.value = 'json' + snmpOidRows.value = [] + } else { + syncSnmpOidRowsFromJson() + } } catch { snmpOidEditMode.value = 'json' snmpOidRows.value = [] @@ -495,6 +521,10 @@ const switchSnmpOidEditMode = () => { return } try { + if (hasSnmpOidTableUnsupportedFields(formData.snmp_oids)) { + Message.warning('当前 SNMP OID JSON 包含表格不支持的字段,请在 JSON 模式下编辑') + return + } syncSnmpOidRowsFromJson() snmpOidEditMode.value = 'table' } catch { @@ -521,6 +551,7 @@ const validateSnmpOidConfigBeforeSubmit = () => { } try { + const parsedRows = parseSnmpOidJsonArray(formData.snmp_oids) const rows = parseSnmpOidRows(formData.snmp_oids) const invalidIndex = findIncompleteSnmpOidRowIndex(rows) if (invalidIndex >= 0) { @@ -529,7 +560,7 @@ const validateSnmpOidConfigBeforeSubmit = () => { } const normalizedRows = normalizeSnmpOidRows(rows) snmpOidRows.value = normalizedRows - formData.snmp_oids = normalizedRows.length ? JSON.stringify(normalizedRows, null, 2) : '' + formData.snmp_oids = JSON.stringify(parsedRows, null, 2) return true } catch { Message.warning('SNMP OID 配置必须是合法 JSON 数组') From 7a203295941c83fb7c8a1b9ef5f468eff1a91d15 Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Sun, 5 Jul 2026 18:35:01 +0800 Subject: [PATCH 09/14] fix: validate advanced snmp oid json rows --- .../device-collect/components/FormDialog.vue | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue index 2a947b1..dae7be5 100644 --- a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue +++ b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue @@ -355,12 +355,21 @@ const hasSnmpOidTableUnsupportedFields = (raw: string) => { const rows = parseSnmpOidJsonArray(raw) return rows.some((item) => { if (!item || typeof item !== 'object' || Array.isArray(item)) { - return Boolean(item) + return true } return Object.keys(item).some((key) => !snmpOidTableFields.includes(key)) }) } +const findInvalidSnmpOidJsonRowIndex = (rows: unknown[]) => + rows.findIndex((item) => { + if (!item || typeof item !== 'object' || Array.isArray(item)) { + return true + } + const row = item as Record + return !String(row.oid ?? '').trim() || !String(row.metric_name ?? '').trim() + }) + const stringifySnmpOidRows = (rows: SnmpOidRow[]) => { const normalizedRows = normalizeSnmpOidRows(rows) return normalizedRows.length ? JSON.stringify(normalizedRows, null, 2) : '' @@ -552,12 +561,12 @@ const validateSnmpOidConfigBeforeSubmit = () => { try { const parsedRows = parseSnmpOidJsonArray(formData.snmp_oids) - const rows = parseSnmpOidRows(formData.snmp_oids) - const invalidIndex = findIncompleteSnmpOidRowIndex(rows) - if (invalidIndex >= 0) { - Message.warning(`SNMP OID 第 ${invalidIndex + 1} 行请填写 OID 和指标名称`) + const invalidJsonIndex = findInvalidSnmpOidJsonRowIndex(parsedRows) + if (invalidJsonIndex >= 0) { + Message.warning(`SNMP OID 第 ${invalidJsonIndex + 1} 行请填写 OID 和指标名称`) return false } + const rows = parseSnmpOidRows(formData.snmp_oids) const normalizedRows = normalizeSnmpOidRows(rows) snmpOidRows.value = normalizedRows formData.snmp_oids = JSON.stringify(parsedRows, null, 2) From f5d0bae89bb13a7dd8d4473a0d536e292e004cf9 Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Sun, 5 Jul 2026 22:13:39 +0800 Subject: [PATCH 10/14] feat: add shared snmp oid editor --- .../ops/pages/dc/components/SnmpOidEditor.vue | 338 ++++++++++++++++++ 1 file changed, 338 insertions(+) create mode 100644 src/views/ops/pages/dc/components/SnmpOidEditor.vue diff --git a/src/views/ops/pages/dc/components/SnmpOidEditor.vue b/src/views/ops/pages/dc/components/SnmpOidEditor.vue new file mode 100644 index 0000000..8653d2b --- /dev/null +++ b/src/views/ops/pages/dc/components/SnmpOidEditor.vue @@ -0,0 +1,338 @@ + + + + + From aa6db97457967639cad79906fbccdb5061cdc039 Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Sun, 5 Jul 2026 22:18:52 +0800 Subject: [PATCH 11/14] fix: validate snmp oid json field types --- .../ops/pages/dc/components/SnmpOidEditor.vue | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/views/ops/pages/dc/components/SnmpOidEditor.vue b/src/views/ops/pages/dc/components/SnmpOidEditor.vue index 8653d2b..4bd1f5c 100644 --- a/src/views/ops/pages/dc/components/SnmpOidEditor.vue +++ b/src/views/ops/pages/dc/components/SnmpOidEditor.vue @@ -144,7 +144,12 @@ const findInvalidJsonRowIndex = (items: unknown[]) => if (!isJsonRecord(item)) { return true } - return !String(item.oid ?? '').trim() || !String(item.metric_name ?? '').trim() + return ( + typeof item.oid !== 'string' || + typeof item.metric_name !== 'string' || + !item.oid.trim() || + !item.metric_name.trim() + ) }) const normalizeJsonItems = (items: unknown[]) => @@ -163,10 +168,10 @@ const parseRows = (raw: string): SnmpOidRow[] => parseJsonArray(raw).map((item) => { const record = isJsonRecord(item) ? item : {} return { - oid: String(record.oid ?? '').trim(), - metric_name: String(record.metric_name ?? '').trim(), - metric_unit: String(record.metric_unit ?? '').trim(), - type: String(record.type ?? '').trim(), + oid: typeof record.oid === 'string' ? record.oid.trim() : '', + metric_name: typeof record.metric_name === 'string' ? record.metric_name.trim() : '', + metric_unit: typeof record.metric_unit === 'string' ? record.metric_unit.trim() : '', + type: typeof record.type === 'string' ? record.type.trim() : '', } }) From 58365870ef1e07cbee21a3823b5b7dcb2400ca27 Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Sun, 5 Jul 2026 23:02:09 +0800 Subject: [PATCH 12/14] refactor: reuse snmp oid editor in device collect form --- .../device-collect/components/FormDialog.vue | 273 +----------------- 1 file changed, 11 insertions(+), 262 deletions(-) diff --git a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue index dae7be5..9813909 100644 --- a/src/views/ops/pages/dc/device-collect/components/FormDialog.vue +++ b/src/views/ops/pages/dc/device-collect/components/FormDialog.vue @@ -140,62 +140,11 @@ - - - - - - - + @@ -243,6 +192,7 @@ import { } from '@/api/ops/room-device' import { fetchPolicyOptions, type PolicyOptionItem } from '@/api/ops/alertPolicy' import { fetchRoomOptions, type RoomOptionItem } from '@/api/ops/room' +import SnmpOidEditor from '../../components/SnmpOidEditor.vue' interface Props { visible: boolean @@ -256,22 +206,12 @@ const props = withDefaults(defineProps(), { const emit = defineEmits(['update:visible', 'success']) const formRef = ref() +const snmpOidEditorRef = ref>() +const snmpOidEditorResetKey = ref(0) const confirmLoading = ref(false) const policyOptions = ref([]) const roomOptions = ref([]) -type SnmpOidEditMode = 'table' | 'json' - -interface SnmpOidRow { - oid: string - metric_name: string - metric_unit: string - type: string -} - -const snmpOidEditMode = ref('table') -const snmpOidRows = ref([]) - const isEdit = computed(() => !!props.record?.id) const formData = reactive({ @@ -307,96 +247,6 @@ const rules = { device_category: [{ required: true, message: '请选择设备分类' }], } -const createEmptySnmpOidRow = (): SnmpOidRow => ({ - oid: '', - metric_name: '', - metric_unit: '', - type: '', -}) - -const snmpOidTableFields = ['oid', 'metric_name', 'metric_unit', 'type'] - -const normalizeSnmpOidRows = (rows: SnmpOidRow[]) => - rows - .map((row) => ({ - oid: row.oid.trim(), - metric_name: row.metric_name.trim(), - metric_unit: row.metric_unit.trim(), - type: row.type.trim(), - })) - .filter((row) => row.oid || row.metric_name || row.metric_unit || row.type) - -const findIncompleteSnmpOidRowIndex = (rows: SnmpOidRow[]) => - rows.findIndex((row) => { - const normalizedRow = { - oid: row.oid.trim(), - metric_name: row.metric_name.trim(), - metric_unit: row.metric_unit.trim(), - type: row.type.trim(), - } - const hasAnyValue = - normalizedRow.oid || normalizedRow.metric_name || normalizedRow.metric_unit || normalizedRow.type - return Boolean(hasAnyValue && (!normalizedRow.oid || !normalizedRow.metric_name)) - }) - -const parseSnmpOidJsonArray = (raw: string) => { - const text = raw.trim() - if (!text) { - return [] - } - const parsed = JSON.parse(text) - if (!Array.isArray(parsed)) { - throw new Error('SNMP OID 配置必须是 JSON 数组') - } - return parsed -} - -const hasSnmpOidTableUnsupportedFields = (raw: string) => { - const rows = parseSnmpOidJsonArray(raw) - return rows.some((item) => { - if (!item || typeof item !== 'object' || Array.isArray(item)) { - return true - } - return Object.keys(item).some((key) => !snmpOidTableFields.includes(key)) - }) -} - -const findInvalidSnmpOidJsonRowIndex = (rows: unknown[]) => - rows.findIndex((item) => { - if (!item || typeof item !== 'object' || Array.isArray(item)) { - return true - } - const row = item as Record - return !String(row.oid ?? '').trim() || !String(row.metric_name ?? '').trim() - }) - -const stringifySnmpOidRows = (rows: SnmpOidRow[]) => { - const normalizedRows = normalizeSnmpOidRows(rows) - return normalizedRows.length ? JSON.stringify(normalizedRows, null, 2) : '' -} - -const parseSnmpOidRows = (raw: string): SnmpOidRow[] => { - const text = raw.trim() - if (!text) { - return [] - } - const parsed = parseSnmpOidJsonArray(text) - return parsed.map((item) => ({ - oid: String(item?.oid ?? '').trim(), - metric_name: String(item?.metric_name ?? '').trim(), - metric_unit: String(item?.metric_unit ?? '').trim(), - type: String(item?.type ?? '').trim(), - })) -} - -const syncSnmpOidRowsFromJson = () => { - snmpOidRows.value = parseSnmpOidRows(formData.snmp_oids || '') -} - -const syncSnmpOidJsonFromRows = () => { - formData.snmp_oids = stringifySnmpOidRows(snmpOidRows.value) -} - const loadPolicyOptions = async () => { try { const response: any = await fetchPolicyOptions({ enabled: true }) @@ -460,18 +310,7 @@ watch( collect_interval: props.record.collect_interval || 60, policy_ids: props.record.policy_ids || [], }) - snmpOidEditMode.value = 'table' - try { - if (hasSnmpOidTableUnsupportedFields(formData.snmp_oids || '')) { - snmpOidEditMode.value = 'json' - snmpOidRows.value = [] - } else { - syncSnmpOidRowsFromJson() - } - } catch { - snmpOidEditMode.value = 'json' - snmpOidRows.value = [] - } + snmpOidEditorResetKey.value += 1 } else { Object.assign(formData, { name: '', @@ -499,84 +338,12 @@ watch( collect_interval: 60, policy_ids: [], }) - snmpOidEditMode.value = 'table' - snmpOidRows.value = [] + snmpOidEditorResetKey.value += 1 } } } ) -const addSnmpOidRow = () => { - if (snmpOidEditMode.value !== 'table') { - return - } - snmpOidRows.value.push(createEmptySnmpOidRow()) - syncSnmpOidJsonFromRows() -} - -const removeSnmpOidRow = (index: number) => { - snmpOidRows.value.splice(index, 1) - syncSnmpOidJsonFromRows() -} - -const handleSnmpOidRowChange = () => { - syncSnmpOidJsonFromRows() -} - -const switchSnmpOidEditMode = () => { - if (snmpOidEditMode.value === 'table') { - syncSnmpOidJsonFromRows() - snmpOidEditMode.value = 'json' - return - } - try { - if (hasSnmpOidTableUnsupportedFields(formData.snmp_oids)) { - Message.warning('当前 SNMP OID JSON 包含表格不支持的字段,请在 JSON 模式下编辑') - return - } - syncSnmpOidRowsFromJson() - snmpOidEditMode.value = 'table' - } catch { - Message.warning('SNMP OID 配置必须是合法 JSON 数组,修正后才能切换到表格模式') - } -} - -const validateSnmpOidConfigBeforeSubmit = () => { - if (snmpOidEditMode.value === 'table') { - const invalidIndex = findIncompleteSnmpOidRowIndex(snmpOidRows.value) - if (invalidIndex >= 0) { - Message.warning(`SNMP OID 第 ${invalidIndex + 1} 行请填写 OID 和指标名称`) - return false - } - const rows = normalizeSnmpOidRows(snmpOidRows.value) - formData.snmp_oids = rows.length ? JSON.stringify(rows, null, 2) : '' - return true - } - - if (!formData.snmp_oids?.trim()) { - snmpOidRows.value = [] - formData.snmp_oids = '' - return true - } - - try { - const parsedRows = parseSnmpOidJsonArray(formData.snmp_oids) - const invalidJsonIndex = findInvalidSnmpOidJsonRowIndex(parsedRows) - if (invalidJsonIndex >= 0) { - Message.warning(`SNMP OID 第 ${invalidJsonIndex + 1} 行请填写 OID 和指标名称`) - return false - } - const rows = parseSnmpOidRows(formData.snmp_oids) - const normalizedRows = normalizeSnmpOidRows(rows) - snmpOidRows.value = normalizedRows - formData.snmp_oids = JSON.stringify(parsedRows, null, 2) - return true - } catch { - Message.warning('SNMP OID 配置必须是合法 JSON 数组') - return false - } -} - const handleOk = async () => { try { await formRef.value?.validate() @@ -606,7 +373,7 @@ const handleOk = async () => { Message.warning('SNMP v2c 模式下请填写 community') return } - if (!validateSnmpOidConfigBeforeSubmit()) { + if (!snmpOidEditorRef.value?.validate()) { return } } @@ -705,21 +472,3 @@ onMounted(() => { loadRoomOptions() }) - - From 3f4d50b3950357f30ec5dfa1ddcd6da109829024 Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Sun, 5 Jul 2026 23:04:21 +0800 Subject: [PATCH 13/14] refactor: reuse snmp oid editor in security form --- .../dc/security/components/FormDialog.vue | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/views/ops/pages/dc/security/components/FormDialog.vue b/src/views/ops/pages/dc/security/components/FormDialog.vue index 9524491..912ba8d 100644 --- a/src/views/ops/pages/dc/security/components/FormDialog.vue +++ b/src/views/ops/pages/dc/security/components/FormDialog.vue @@ -157,13 +157,11 @@ - - - + @@ -214,6 +212,7 @@ import type { FormInstance } from '@arco-design/web-vue' import { createSecurityService, updateSecurityService, SECURITY_TYPE_OPTIONS, type SecurityServiceFormData } from '@/api/ops/security' import { fetchPolicyOptions, type PolicyOptionItem } from '@/api/ops/alertPolicy' import { fetchServerList, type ServerItem } from '@/api/ops/server' +import SnmpOidEditor from '../../components/SnmpOidEditor.vue' interface Props { visible: boolean @@ -227,6 +226,8 @@ const props = withDefaults(defineProps(), { const emit = defineEmits(['update:visible', 'success']) const formRef = ref() +const snmpOidEditorRef = ref>() +const snmpOidEditorResetKey = ref(0) const confirmLoading = ref(false) const policyOptions = ref([]) const serverOptions = ref([]) @@ -336,6 +337,7 @@ watch( extra: props.record.extra || '', policy_ids: props.record.policy_ids || [], }) + snmpOidEditorResetKey.value += 1 } else { Object.assign(formData, { service_identity: '', @@ -368,6 +370,7 @@ watch( extra: '', policy_ids: [], }) + snmpOidEditorResetKey.value += 1 } } } @@ -402,13 +405,8 @@ const handleOk = async () => { Message.warning('SNMP v2c 模式下请填写 community') return } - if (formData.snmp_oids?.trim()) { - try { - JSON.parse(formData.snmp_oids) - } catch { - Message.warning('SNMP OID配置必须是合法 JSON') - return - } + if (!snmpOidEditorRef.value?.validate()) { + return } } From ee101984f0852d4c7aa7db267219ce09e0cce282 Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Sun, 5 Jul 2026 23:06:18 +0800 Subject: [PATCH 14/14] refactor: reuse snmp oid editor in storage form --- .../storage/components/StorageFormDialog.vue | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/views/ops/pages/dc/storage/components/StorageFormDialog.vue b/src/views/ops/pages/dc/storage/components/StorageFormDialog.vue index 27dc1ed..fc90e7f 100644 --- a/src/views/ops/pages/dc/storage/components/StorageFormDialog.vue +++ b/src/views/ops/pages/dc/storage/components/StorageFormDialog.vue @@ -173,13 +173,11 @@ - - - + @@ -208,6 +206,7 @@ import type { FormInstance } from '@arco-design/web-vue' import { createStorage, updateStorage } from '@/api/ops/storage' import type { StorageCreateData, StorageItem } from '@/api/ops/storage' import { fetchServerList, type ServerItem } from '@/api/ops/server' +import SnmpOidEditor from '../../components/SnmpOidEditor.vue' interface Props { visible: boolean @@ -221,6 +220,8 @@ const props = withDefaults(defineProps(), { const emit = defineEmits(['update:visible', 'success']) const formRef = ref() +const snmpOidEditorRef = ref>() +const snmpOidEditorResetKey = ref(0) const confirmLoading = ref(false) const serverOptions = ref([]) @@ -297,6 +298,7 @@ watch( collect_args: props.record.collect_args || '', collect_interval: props.record.collect_interval || 60, }) + snmpOidEditorResetKey.value += 1 } else { Object.assign(formData, { name: '', @@ -328,6 +330,7 @@ watch( collect_args: '', collect_interval: 60, }) + snmpOidEditorResetKey.value += 1 } } } @@ -362,13 +365,8 @@ const handleOk = async () => { Message.warning('SNMP v2c 模式下请填写 community') return } - if (formData.snmp_oids?.trim()) { - try { - JSON.parse(formData.snmp_oids) - } catch { - Message.warning('SNMP OID 配置必须是合法 JSON') - return - } + if (!snmpOidEditorRef.value?.validate()) { + return } }