feat: add disk monitor target editors
This commit is contained in:
@@ -0,0 +1,141 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog :model-value="props.visible" :title="props.title" width="880px" @close="closeDialog">
|
||||||
|
<el-form label-width="120px" class="target-form">
|
||||||
|
<el-form-item label="盘符">
|
||||||
|
<el-input
|
||||||
|
:model-value="props.modelValue.driveLetter"
|
||||||
|
placeholder="例如 C:"
|
||||||
|
maxlength="10"
|
||||||
|
@update:model-value="value => patchTarget({ driveLetter: String(value).trim().toUpperCase() })"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="启用监控">
|
||||||
|
<el-switch
|
||||||
|
:model-value="props.modelValue.monitorEnabled"
|
||||||
|
@update:model-value="value => patchTarget({ monitorEnabled: Boolean(value) })"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="预警阈值">
|
||||||
|
<el-input-number
|
||||||
|
:model-value="props.modelValue.warningUsagePercent"
|
||||||
|
:min="1"
|
||||||
|
:max="100"
|
||||||
|
:controls="false"
|
||||||
|
@update:model-value="handleWarningChange($event)"
|
||||||
|
/>
|
||||||
|
<span class="suffix-text">%</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="告警阈值">
|
||||||
|
<el-input-number
|
||||||
|
:model-value="props.modelValue.alarmUsagePercent"
|
||||||
|
:min="1"
|
||||||
|
:max="100"
|
||||||
|
:controls="false"
|
||||||
|
@update:model-value="handleAlarmChange($event)"
|
||||||
|
/>
|
||||||
|
<span class="suffix-text">%</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="路径通知">
|
||||||
|
<el-switch
|
||||||
|
:model-value="props.modelValue.notifyPathEnabled"
|
||||||
|
@update:model-value="value => patchTarget({ notifyPathEnabled: Boolean(value) })"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="props.modelValue.notifyPathEnabled" label="路径通知配置">
|
||||||
|
<NotificationPathEditor
|
||||||
|
:model-value="props.modelValue.notifyPathList"
|
||||||
|
@update:model-value="value => patchTarget({ notifyPathList: value })"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="HTTP 通知">
|
||||||
|
<el-switch
|
||||||
|
:model-value="props.modelValue.notifyHttpEnabled"
|
||||||
|
@update:model-value="value => patchTarget({ notifyHttpEnabled: Boolean(value) })"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="props.modelValue.notifyHttpEnabled" label="HTTP 通知配置">
|
||||||
|
<NotificationHttpEditor
|
||||||
|
:model-value="props.modelValue.notifyHttpList"
|
||||||
|
@update:model-value="value => patchTarget({ notifyHttpList: value })"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注">
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
:model-value="props.modelValue.remark"
|
||||||
|
placeholder="可选"
|
||||||
|
@update:model-value="value => patchTarget({ remark: String(value) })"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="closeDialog">取消</el-button>
|
||||||
|
<el-button type="primary" @click="emit('confirm')">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { DiskMonitor } from '@/api/system/diskMonitor/interface'
|
||||||
|
import NotificationHttpEditor from './NotificationHttpEditor.vue'
|
||||||
|
import NotificationPathEditor from './NotificationPathEditor.vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'DiskMonitorTargetDialog'
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
visible: boolean
|
||||||
|
modelValue: DiskMonitor.TargetItem
|
||||||
|
title: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:visible': [value: boolean]
|
||||||
|
'update:modelValue': [value: DiskMonitor.TargetItem]
|
||||||
|
confirm: []
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const closeDialog = () => {
|
||||||
|
emit('update:visible', false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleWarningChange = (value: number | undefined) => {
|
||||||
|
patchTarget({
|
||||||
|
warningUsagePercent: Number(value || 0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAlarmChange = (value: number | undefined) => {
|
||||||
|
patchTarget({
|
||||||
|
alarmUsagePercent: Number(value || 0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const patchTarget = (patch: Partial<DiskMonitor.TargetItem>) => {
|
||||||
|
emit('update:modelValue', {
|
||||||
|
...props.modelValue,
|
||||||
|
...patch
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.target-form :deep(.el-form-item__content) {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suffix-text {
|
||||||
|
margin-left: 8px;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
<template>
|
||||||
|
<div class="target-table-card">
|
||||||
|
<div class="table-header">
|
||||||
|
<div>
|
||||||
|
<h3 class="table-title">监控目标</h3>
|
||||||
|
<p class="table-description">维护需要监控的盘符与通知目标</p>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" @click="emit('add')">新增目标</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table :data="rows" border stripe>
|
||||||
|
<el-table-column prop="driveLetter" label="盘符" min-width="90" />
|
||||||
|
<el-table-column label="是否监控" min-width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.monitorEnabled ? '是' : '否' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="预警使用率" min-width="110">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.warningUsagePercent }}%
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="告警使用率" min-width="110">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.alarmUsagePercent }}%
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="当前状态" min-width="110">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="getStatusType(row.lastStatus)" effect="light">
|
||||||
|
{{ getStatusLabel(row.lastStatus) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="最近扫描时间" min-width="170">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ formatScanTime(row.lastScanTime) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="最近使用率" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ formatUsedPercent(row.lastUsedPercent) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="140" fixed="right">
|
||||||
|
<template #default="{ row, $index }">
|
||||||
|
<el-button link type="primary" @click="emit('edit', row, $index)">编辑</el-button>
|
||||||
|
<el-button link type="danger" @click="emit('remove', $index)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import type { DiskMonitor } from '@/api/system/diskMonitor/interface'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'DiskMonitorTargetTable'
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
rows: DiskMonitor.TargetItem[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
add: []
|
||||||
|
edit: [row: DiskMonitor.TargetItem, index: number]
|
||||||
|
remove: [index: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const getStatusType = (status: DiskMonitor.MonitorStatus) => {
|
||||||
|
if (status === 'NORMAL') return 'success'
|
||||||
|
if (status === 'WARNING') return 'warning'
|
||||||
|
if (status === 'ALARM') return 'danger'
|
||||||
|
return 'info'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStatusLabel = (status: DiskMonitor.MonitorStatus) => {
|
||||||
|
if (status === 'NORMAL') return '正常'
|
||||||
|
if (status === 'WARNING') return '预警'
|
||||||
|
if (status === 'ALARM') return '告警'
|
||||||
|
return '未知'
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatScanTime = (value?: string | null) => {
|
||||||
|
if (!value) return '--'
|
||||||
|
return dayjs(value).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatUsedPercent = (value?: number | null) => {
|
||||||
|
if (value === null || value === undefined) return '--'
|
||||||
|
return `${value}%`
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.target-table-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 16px;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-title {
|
||||||
|
margin: 0 0 6px;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #111827;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-description {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
<template>
|
||||||
|
<div class="notification-editor">
|
||||||
|
<div class="editor-header">
|
||||||
|
<span class="editor-title">HTTP 通知目标</span>
|
||||||
|
<el-button type="primary" link @click="handleAdd">新增 HTTP 目标</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-if="!props.modelValue.length" class="empty-text">暂无 HTTP 通知目标</div>
|
||||||
|
<div v-else class="editor-list">
|
||||||
|
<div v-for="(item, index) in props.modelValue" :key="index" class="editor-row">
|
||||||
|
<el-input
|
||||||
|
:model-value="item.name"
|
||||||
|
placeholder="名称"
|
||||||
|
@update:model-value="value => patchRow(index, 'name', String(value))"
|
||||||
|
/>
|
||||||
|
<el-input
|
||||||
|
:model-value="item.url"
|
||||||
|
placeholder="通知 URL"
|
||||||
|
@update:model-value="value => patchRow(index, 'url', String(value))"
|
||||||
|
/>
|
||||||
|
<el-select
|
||||||
|
:model-value="item.method"
|
||||||
|
@update:model-value="value => patchRow(index, 'method', value as DiskMonitor.NotifyHttpTarget['method'])"
|
||||||
|
>
|
||||||
|
<el-option label="POST" value="POST" />
|
||||||
|
</el-select>
|
||||||
|
<el-input-number
|
||||||
|
:model-value="item.timeoutMs"
|
||||||
|
:min="100"
|
||||||
|
:step="100"
|
||||||
|
:controls="false"
|
||||||
|
@update:model-value="handleTimeoutChange(index, $event)"
|
||||||
|
/>
|
||||||
|
<el-switch
|
||||||
|
:model-value="item.enabled"
|
||||||
|
@update:model-value="value => patchRow(index, 'enabled', Boolean(value))"
|
||||||
|
/>
|
||||||
|
<el-button type="danger" link @click="handleRemove(index)">删除</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { DiskMonitor } from '@/api/system/diskMonitor/interface'
|
||||||
|
import { createEmptyHttpTarget } from '../utils/form'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'NotificationHttpEditor'
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: DiskMonitor.NotifyHttpTarget[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:modelValue': [value: DiskMonitor.NotifyHttpTarget[]]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const updateList = (nextList: DiskMonitor.NotifyHttpTarget[]) => {
|
||||||
|
emit('update:modelValue', nextList)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAdd = () => {
|
||||||
|
updateList([...props.modelValue, createEmptyHttpTarget()])
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRemove = (index: number) => {
|
||||||
|
updateList(props.modelValue.filter((_, rowIndex) => rowIndex !== index))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTimeoutChange = (index: number, value: number | undefined) => {
|
||||||
|
patchRow(index, 'timeoutMs', Number(value || 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
const patchRow = <K extends keyof DiskMonitor.NotifyHttpTarget>(
|
||||||
|
index: number,
|
||||||
|
key: K,
|
||||||
|
value: DiskMonitor.NotifyHttpTarget[K]
|
||||||
|
) => {
|
||||||
|
const nextList = props.modelValue.map((item, rowIndex) =>
|
||||||
|
rowIndex === index
|
||||||
|
? {
|
||||||
|
...item,
|
||||||
|
[key]: value
|
||||||
|
}
|
||||||
|
: item
|
||||||
|
)
|
||||||
|
updateList(nextList)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.notification-editor {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #111827;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr) 100px 120px auto auto;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
.editor-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
<template>
|
||||||
|
<div class="notification-editor">
|
||||||
|
<div class="editor-header">
|
||||||
|
<span class="editor-title">路径通知目标</span>
|
||||||
|
<el-button type="primary" link @click="handleAdd">新增路径</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-if="!props.modelValue.length" class="empty-text">暂无路径通知目标</div>
|
||||||
|
<div v-else class="editor-list">
|
||||||
|
<div v-for="(item, index) in props.modelValue" :key="index" class="editor-row">
|
||||||
|
<el-input
|
||||||
|
:model-value="item.name"
|
||||||
|
placeholder="名称"
|
||||||
|
@update:model-value="value => patchRow(index, 'name', String(value))"
|
||||||
|
/>
|
||||||
|
<el-input
|
||||||
|
:model-value="item.path"
|
||||||
|
placeholder="通知路径"
|
||||||
|
@update:model-value="value => patchRow(index, 'path', String(value))"
|
||||||
|
/>
|
||||||
|
<el-switch
|
||||||
|
:model-value="item.enabled"
|
||||||
|
@update:model-value="value => patchRow(index, 'enabled', Boolean(value))"
|
||||||
|
/>
|
||||||
|
<el-button type="danger" link @click="handleRemove(index)">删除</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { DiskMonitor } from '@/api/system/diskMonitor/interface'
|
||||||
|
import { createEmptyPathTarget } from '../utils/form'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'NotificationPathEditor'
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: DiskMonitor.NotifyPathTarget[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:modelValue': [value: DiskMonitor.NotifyPathTarget[]]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const updateList = (nextList: DiskMonitor.NotifyPathTarget[]) => {
|
||||||
|
emit('update:modelValue', nextList)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAdd = () => {
|
||||||
|
updateList([...props.modelValue, createEmptyPathTarget()])
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRemove = (index: number) => {
|
||||||
|
updateList(props.modelValue.filter((_, rowIndex) => rowIndex !== index))
|
||||||
|
}
|
||||||
|
|
||||||
|
const patchRow = <K extends keyof DiskMonitor.NotifyPathTarget>(
|
||||||
|
index: number,
|
||||||
|
key: K,
|
||||||
|
value: DiskMonitor.NotifyPathTarget[K]
|
||||||
|
) => {
|
||||||
|
const nextList = props.modelValue.map((item, rowIndex) =>
|
||||||
|
rowIndex === index
|
||||||
|
? {
|
||||||
|
...item,
|
||||||
|
[key]: value
|
||||||
|
}
|
||||||
|
: item
|
||||||
|
)
|
||||||
|
updateList(nextList)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.notification-editor {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #111827;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) minmax(0, 2fr) auto auto;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.editor-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -18,6 +18,15 @@
|
|||||||
@save="handleSave"
|
@save="handleSave"
|
||||||
@run="handleRun"
|
@run="handleRun"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<DiskMonitorTargetTable :rows="targetList" @add="openAddTarget" @edit="openEditTarget" @remove="removeTarget" />
|
||||||
|
|
||||||
|
<DiskMonitorTargetDialog
|
||||||
|
v-model:visible="targetDialogVisible"
|
||||||
|
v-model="editingTarget"
|
||||||
|
:title="editingTargetIndex >= 0 ? '编辑监控目标' : '新增监控目标'"
|
||||||
|
@confirm="confirmTarget"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -34,7 +43,9 @@ import {
|
|||||||
import type { DiskMonitor } from '@/api/system/diskMonitor/interface'
|
import type { DiskMonitor } from '@/api/system/diskMonitor/interface'
|
||||||
import DiskMonitorPolicyForm from './components/DiskMonitorPolicyForm.vue'
|
import DiskMonitorPolicyForm from './components/DiskMonitorPolicyForm.vue'
|
||||||
import DiskMonitorSummary from './components/DiskMonitorSummary.vue'
|
import DiskMonitorSummary from './components/DiskMonitorSummary.vue'
|
||||||
import { createDefaultPolicy, validatePolicy } from './utils/form'
|
import DiskMonitorTargetDialog from './components/DiskMonitorTargetDialog.vue'
|
||||||
|
import DiskMonitorTargetTable from './components/DiskMonitorTargetTable.vue'
|
||||||
|
import { createDefaultPolicy, createEmptyTarget, validatePolicy, validateTarget } from './utils/form'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'DiskMonitorPage'
|
name: 'DiskMonitorPage'
|
||||||
@@ -44,6 +55,9 @@ const router = useRouter()
|
|||||||
|
|
||||||
const policyForm = ref<DiskMonitor.PolicyItem>(createDefaultPolicy())
|
const policyForm = ref<DiskMonitor.PolicyItem>(createDefaultPolicy())
|
||||||
const targetList = ref<DiskMonitor.TargetItem[]>([])
|
const targetList = ref<DiskMonitor.TargetItem[]>([])
|
||||||
|
const targetDialogVisible = ref(false)
|
||||||
|
const editingTargetIndex = ref(-1)
|
||||||
|
const editingTarget = ref<DiskMonitor.TargetItem>(createEmptyTarget())
|
||||||
const latestJob = ref<DiskMonitor.JobListItem | null>(null)
|
const latestJob = ref<DiskMonitor.JobListItem | null>(null)
|
||||||
const loading = reactive({
|
const loading = reactive({
|
||||||
init: false,
|
init: false,
|
||||||
@@ -57,6 +71,53 @@ const handleBack = async () => {
|
|||||||
await router.push('/systemMonitor')
|
await router.push('/systemMonitor')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cloneTarget = (target: DiskMonitor.TargetItem): DiskMonitor.TargetItem => ({
|
||||||
|
...target,
|
||||||
|
notifyPathList: target.notifyPathList.map(item => ({ ...item })),
|
||||||
|
notifyHttpList: target.notifyHttpList.map(item => ({ ...item }))
|
||||||
|
})
|
||||||
|
|
||||||
|
const openAddTarget = () => {
|
||||||
|
editingTargetIndex.value = -1
|
||||||
|
editingTarget.value = createEmptyTarget()
|
||||||
|
targetDialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const openEditTarget = (row: DiskMonitor.TargetItem, index: number) => {
|
||||||
|
editingTargetIndex.value = index
|
||||||
|
// 编辑时克隆当前行,避免未确认前直接污染列表数据
|
||||||
|
editingTarget.value = cloneTarget(row)
|
||||||
|
targetDialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmTarget = () => {
|
||||||
|
// 提交前统一规范盘符并做去重、阈值关系校验
|
||||||
|
const normalizedDriveLetter = editingTarget.value.driveLetter.trim().toUpperCase()
|
||||||
|
const payload: DiskMonitor.TargetItem = {
|
||||||
|
...editingTarget.value,
|
||||||
|
driveLetter: normalizedDriveLetter
|
||||||
|
}
|
||||||
|
const exists = targetList.value
|
||||||
|
.filter((_, index) => index !== editingTargetIndex.value)
|
||||||
|
.map(item => item.driveLetter.trim().toUpperCase())
|
||||||
|
const errorMessage = validateTarget(payload, exists)
|
||||||
|
if (errorMessage) {
|
||||||
|
ElMessage.warning(errorMessage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (editingTargetIndex.value >= 0) {
|
||||||
|
targetList.value.splice(editingTargetIndex.value, 1, payload)
|
||||||
|
} else {
|
||||||
|
targetList.value.push(payload)
|
||||||
|
}
|
||||||
|
targetDialogVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeTarget = (index: number) => {
|
||||||
|
targetList.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
const loadPolicyDetail = async () => {
|
const loadPolicyDetail = async () => {
|
||||||
const response = await getDiskMonitorPolicyDetail()
|
const response = await getDiskMonitorPolicyDetail()
|
||||||
const detail = response.data
|
const detail = response.data
|
||||||
|
|||||||
Reference in New Issue
Block a user