This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
nezha-nezha-fronted/nezha-fronted/src/components/common/rightBox/alertRuleBox.vue
2022-06-23 16:39:20 +08:00

678 lines
26 KiB
Vue

<template>
<div v-clickoutside="{obj: editAlertRule, func: clickOutside}" class="right-box right-box-alert-rule">
<div class="right-box__header">
<!-- begin--标题-->
<div class="header__title">{{alertRule.id ? $t("alert.config.editAlertConfig") : $t("overall.createAlertRule")}}</div>
<!-- end--标题-->
<div class="header__operation">
<span v-cancel="{obj: editAlertRule, func: esc}"><i class="nz-icon nz-icon-close"></i></span>
</div>
</div>
<!-- begin--表单-->
<div class="right-box__container">
<div class="container__form">
<el-form ref="alertRuleForm" :model="editAlertRule" :rules="rules" label-position = "top" label-width="120px">
<!--name-->
<el-form-item :label='$t("overall.name")' prop="name">
<el-input id="alert-box-input-name" ref="alertName" v-model="editAlertRule.name" maxlength="64" placeholder="" show-word-limit size="small"></el-input>
</el-form-item>
<!--type-->
<el-form-item :label="$t('overall.type')" prop="type" class="half-form-item">
<el-select
v-model="editAlertRule.type"
class="right-box__select half-form-item"
popper-class="right-box-select-top prevent-clickoutside"
size="small"
:disabled="showTypeSelect"
@change="selectAlertRuleMetric">
<el-option
v-for="item in searchMetrics"
:key="item.value"
:label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
<!--severity-->
<el-form-item :label="$t('alert.severity')" class="severity-box half-form-item" prop="severityId">
<el-select id="alert-box-input-severity" v-model="editAlertRule.severityId" class="right-box__select" placeholder="" popper-class="right-box-select-top prevent-clickoutside" size="small">
<el-option v-for="item in severityData" :id="'alert-severity-'+item.value" :key="item.id" :label="item.name" :value="item.id">
<div style="display: flex;justify-content: space-between;padding: 5px;line-height: 23px">
<div><i :style="{color:item.color,'font-size':'12px'}" class="nz-icon nz-icon-circle"></i> {{item.name}}</div>
<div class="severity-item text-ellipsis" :title="item.remark">{{item.remark}}</div>
</div>
</el-option>
</el-select>
<i v-if="editAlertRule.severityId" :style="{color:severityData.length > 0 && severityData.find(severity => severity.id === editAlertRule.severityId).color,'font-size':'12px'}" class="nz-icon nz-icon-circle severity-circle"></i>
</el-form-item>
<!--expr-->
<el-form-item v-if="showSnmpTrap" :label='$t("config.exprTemp.expression")' prop="expr">
<template v-if="showMetrics">
<el-row style="line-height: 32px;">
<promql-input
:from-father-data="true"
:metricOptionsParent="metricOptions"
id="alert-box-input-promql"
ref="promql"
:expression-list.sync="expressions"
:index="0"
:plugins="['metric-selector','metric-input']"
:required="true"
type="metric"
:styleType="3"
@change="metricChange"
></promql-input>
</el-row>
</template>
<template v-else>
<el-row style="line-height: 32px;">
<promql-input
id="alert-box-input-promql"
ref="promql"
:expression-list.sync="expressions"
:index="0"
:plugins="['metric-selector','metric-input']"
:required="true"
type="log"
:styleType="3"
@change="metricChange"
></promql-input>
</el-row>
</template>
</el-form-item>
<el-form-item label="OID" prop="expr" v-if="!showSnmpTrap">
<el-input id="alert-box-input-oid" v-model="editAlertRule.expr" size="small" type="text"></el-input>
</el-form-item>
<!--threshold-->
<el-form-item
v-if="showSnmpTrap"
:label="$t('alert.config.threshold')"
prop="threshold" class="half-form-item alert-box-threshold"
style="display: inline-block;"
:rules="[
{ required: this.editAlertRule.type !== 3, message: this.$t('validate.required'), trigger: 'blur' },
{ validator: nzNumber, trigger: 'blur' }
]">
<el-input id="alert-box-input-threshold" v-model="editAlertRule.threshold" placeholder="" size="small" type="text" :disabled="!showSnmpTrap">
<el-select id="alert-box-input-operator" slot="prepend" v-model="editAlertRule.operator" class="hide-icon" popper-class="prevent-clickoutside" size="small" :disabled="!showSnmpTrap">
<el-option v-for="item in operators" :id="'operator-'+item.key" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-input>
</el-form-item>
<!--unit-->
<el-form-item v-if="showSnmpTrap" :label="$t('dashboard.panel.chartForm.unit')" class="half-form-item" prop="unit" :rules="[{ required: this.editAlertRule.type !== 3, message: this.$t('validate.required'), trigger: 'blur' }]">
<el-cascader id="alert-box-input-unit" v-model="editAlertRule.unit" :options="unitOptions" :props="{ expandTrigger: 'click',emitPath:false }" :show-all-levels="false" filterable
placeholder=""
popper-class="no-style-class dc-dropdown unit-popper-class"
size="small"
:disabled="!showSnmpTrap"
style="width: 100%"
>
</el-cascader>
</el-form-item>
<!--inr-->
<el-form-item v-if="showSnmpTrap" :label="$t('alert.config.inr')" prop="inr" class="half-form-item">
<el-input-number id="alert-box-input-inr" :controls="false" v-model="editAlertRule.inr" :placeholder="$t('alert.config.inrPlaceholder')" size="small" type="text" :disabled="!showSnmpTrap"></el-input-number>
</el-form-item>
<!--last-->
<el-form-item v-if="showSnmpTrap" :label="$t('alert.config.for')" prop="last" class="half-form-item alert-box-duration" :rules=" [
{ required: showSnmpTrap, message: this.$t('validate.required'), trigger: 'change' },
]">
<el-input id="alert-box-input-last" :controls="false" v-model.number="editAlertRule.last" placeholder="" size="small" :disabled="!showSnmpTrap" type="text">
<template slot="append">{{$t('alert.config.second')}}</template>
</el-input>
</el-form-item>
<!--autoExpired-->
<el-form-item :label="$t('alert.config.autoExpired')" prop="autoExpired" class="half-form-item">
<el-select
id="alert-box-input-autoExpired"
v-model="editAlertRule.autoExpired"
class="right-box__select half-form-item"
placeholder=""
popper-class="prevent-clickoutside right-box-select-top "
size="small"
>
<el-option
:label="$t('overall.enabled')"
:value="1">
</el-option>
<el-option
:label="$t('overall.disabled')"
:value="0">
</el-option>
</el-select>
</el-form-item>
<!--timeout-->
<el-form-item :label="$t('alert.config.timeout')" prop="timeout" class="half-form-item">
<el-input id="alert-box-input-timeout" v-model.number="editAlertRule.timeout" placeholder="" size="small" type="text"></el-input>
<template slot="append">{{$t('alert.config.second')}}</template>
</el-form-item>
<!--summary-->
<el-form-item :label="$t('alert.summary')" prop="summary">
<el-input id="alert-box-input-summary" v-model="editAlertRule.summary" maxlength="512" placeholder="" rows="2" show-word-limit size="small" type="textarea"></el-input>
</el-form-item>
<!--description-->
<el-form-item :label="$t('overall.remark')" prop="description">
<el-input id="alert-box-input-description" v-model="editAlertRule.description" maxlength="256" placeholder="" rows="2" show-word-limit size="small" type="textarea"></el-input>
</el-form-item>
<div class="alert-rule-split-title">{{ $t('alert.config.effectiveConfig') }}</div>
<!--state-->
<el-form-item :label="$t('overall.state')" prop="state" class="half-form-item">
<el-select
id="alert-box-input-state"
v-model="editAlertRule.state"
class="right-box__select half-form-item"
placeholder=""
:popper-append-to-body="false"
popper-class="prevent-clickoutside right-box-select-top"
size="small"
>
<el-option
:label="$t('overall.enabled')"
:value="1">
</el-option>
<el-option
:label="$t('overall.disabled')"
:value="0">
</el-option>
</el-select>
</el-form-item>
<!--schedEnable-->
<el-form-item :label="$t('alert.config.schedEnable')" prop="schedEnable" class="half-form-item">
<el-select
id="alert-box-input-schedEnable"
v-model="editAlertRule.schedEnable"
class="right-box__select half-form-item"
placeholder=""
:popper-append-to-body="false"
popper-class="prevent-clickoutside right-box-select-top"
size="small"
>
<el-option
:label="$t('overall.enabled')"
:value="1">
</el-option>
<el-option
:label="$t('overall.disabled')"
:value="0">
</el-option>
</el-select>
</el-form-item>
<!--schedDays-->
<el-form-item :label="$t('alert.config.schedDays')" prop="schedDays" :rules="[{ required: editAlertRule.schedEnable, message: this.$t('validate.required'), trigger: 'blur' }]">
<el-select
id="alert-box-input-schedDays"
style="width: 100%"
v-model="editAlertRule.schedDays"
class="right-box__select half-form-item"
placeholder=""
multiple
@change="$refs.alertRuleForm.validateField('schedDays')"
popper-class="prevent-clickoutside right-box-select-top"
size="small"
>
<el-option
v-for="item in weekList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item style="margin-top: -15px;">
<el-time-picker
style="width: calc(50% - 5px);display: inline-block"
:value-format="'HH:mm'"
:format="'HH:mm'"
v-model="editAlertRule.schedStime"
size="small"
popper-class="right-box-select-top"
:clearable = 'false'
:picker-options="{
}">
</el-time-picker>
<el-time-picker
style="width: calc(50% - 5px);display: inline-block"
:value-format="'HH:mm'"
:format="'HH:mm'"
size="small"
popper-class="right-box-select-top"
:clearable = 'false'
v-model="editAlertRule.schedEtime"
:picker-options="{
minTime: editAlertRule.schedStime
}">
</el-time-picker>
</el-form-item>
<div class="alert-rule-split-title">{{ $t('alert.config.notificationConfig') }}</div>
<!--notifyActive-->
<el-form-item :label="$t('alert.config.notifyActive')" prop="notifyActive" class="half-form-item">
<el-select
id="alert-box-input-notifyActive"
v-model="editAlertRule.notifyActive"
class="right-box__select half-form-item"
placeholder=""
:popper-append-to-body="false"
popper-class="prevent-clickoutside right-box-select-top"
size="small"
@change="receiverAndNotifyValidate"
>
<el-option
:label="$t('overall.enabled')"
:value="1">
</el-option>
<el-option
:label="$t('overall.disabled')"
:value="0">
</el-option>
</el-select>
</el-form-item>
<!--notifyExpired-->
<el-form-item :label="$t('alert.config.notifyExpired')" prop="notifyExpired" class="half-form-item">
<el-select
id="alert-box-input-notifyExpired"
v-model="editAlertRule.notifyExpired"
class="right-box__select half-form-item"
placeholder=""
:popper-append-to-body="false"
popper-class="prevent-clickoutside right-box-select-top"
size="small"
@change="receiverAndNotifyValidate"
>
<el-option
:label="$t('overall.enabled')"
:value="1">
</el-option>
<el-option
:label="$t('overall.disabled')"
:value="0">
</el-option>
</el-select>
</el-form-item>
<!--receiver-->
<el-form-item :label="$t('alert.receiver')" prop="receiver" :rules="[{ required: editAlertRule.notifyExpired || editAlertRule.notifyActive, message: this.$t('validate.required'), trigger: 'blur' }]">
<el-select
id="alert-box-input-receiver"
v-model.trim="editAlertRule.receiverShow"
class="right-box__select"
filterable
multiple
placeholder=""
popper-class="prevent-clickoutside right-box-select-top"
size="small"
value-key="userId"
@change="receiverShowChange"
>
<el-option
style="width: 620px"
v-for="item in userData"
:key="item.id"
:label="item.name"
:value="item.id">
<span class="user-name" :title="item.name">{{item.name}}</span><span class="user-username" :title="item.username">@{{item.username}}</span>
</el-option>
</el-select>
</el-form-item>
<!--notify-->
<el-form-item :label="$t('alert.notify')" :rules="[{ required: editAlertRule.notifyExpired || editAlertRule.notifyActive, message: this.$t('validate.required'), trigger: 'change' }]" class="notify-box" prop="method" >
<el-select id="alert-box-input-notify" v-model="editAlertRule.method" class="right-box__select" multiple placeholder="" :popper-append-to-body="false" popper-class="right-box-select-top prevent-clickoutside" size="small">
<el-option v-for="item in notifyData" :id="'alert-severity-'+item.value" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
</el-form-item>
<div class="alert-rule-split-title">{{ $t('overall.more') }}</div>
<el-form-item :label="$t('alert.config.trbShot')" prop="trbShot">
<rich-text-editor ref="richTextEditor" :edit-data="editAlertRule.trbShot" @after-init="afterInitRich"></rich-text-editor>
</el-form-item>
</el-form>
</div>
</div>
<!-- end--表单-->
<!--底部按钮-->
<div class="right-box__footer">
<button id="alert-box-esc" v-cancel="{obj:editAlertRule,func:esc}" class="footer__btn footer__btn--light">
<span>{{$t('overall.cancel')}}</span>
</button>
<button id="alert-box-save" :class="{'nz-btn-disabled':prevent_opt.save}" :disabled="prevent_opt.save" class="footer__btn" @click="save">
<span>{{$t('overall.save')}}</span>
</button>
</div>
</div>
</template>
<script>
import chartDataFormat from '../../chart/chartDataFormat'
import promqlInput from '../../page/dashboard/explore/promqlInput'
import { noSpecialChar, nzNumber } from '../js/validate'
import editRigthBox from '../mixin/editRigthBox'
import richTextEditor from '@/components/chart/richTextEditor'
import promqlInputMixin from '@/components/common/mixin/promqlInput'
export default {
name: 'alertRuleBox',
props: {
alertRule: Object
},
components: {
'promql-input': promqlInput,
richTextEditor
},
mixins: [editRigthBox, promqlInputMixin],
data () {
const nzOid = (rule, value, callback) => {
if (this.editAlertRule.type === 3) {
const Oid = /^(\d{1,9}\.){0,}[\d]+$/
if (Oid.test(value)) {
callback()
} else {
callback(new Error(this.$t('overall.oid')))
}
} else {
callback()
}
}
const nzInr = (rule, value, callback) => {
if (!this.showSnmpTrap || (!value && isNaN(value))) {
callback()
}
if (value < 15) {
callback(new Error(this.$t('alert.config.inrError')))
} else {
callback()
}
}
return {
promqlCount: 1,
promqlKeys: [0],
expressions: [''],
legends: [''],
searchMetrics: [
{
value: 1,
label: this.$t('overall.metric')
},
{
value: 2,
label: this.$t('overall.logs')
},
{
value: 3,
label: 'SNMP trap'
}
],
showMetrics: true,
showSnmpTrap: true,
showTypeSelect: false,
editAlertRule: {},
rules: {
name: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' },
{ validator: noSpecialChar, trigger: 'change' }
],
expr: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' },
{ validator: nzOid, trigger: 'blur' }
],
severityId: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
],
summary: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
],
operator: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
],
inr: [
{ validator: nzInr, trigger: 'blur' }
]
},
operators: [
{
label: '==',
value: '=='
},
{
label: '!=',
value: '!='
},
{
label: '>',
value: '>'
},
{
label: '<',
value: '<'
},
{
label: '>=',
value: '>='
},
{
label: '<=',
value: '<='
}
],
unitOptions: chartDataFormat.unitOptions(),
userData: [],
severityData: [],
notifyData: [],
MetricsType: 1,
weekList: [
{
value: 1,
label: this.$t('week.Sun')
}, {
value: 2,
label: this.$t('week.Mon')
}, {
value: 3,
label: this.$t('week.Tue')
}, {
value: 4,
label: this.$t('week.Wed')
}, {
value: 5,
label: this.$t('week.Thu')
}, {
value: 6,
label: this.$t('week.Fri')
}, {
value: 7,
label: this.$t('week.Sat')
}
]
}
},
methods: {
nzNumber: nzNumber,
clickOutside () {
this.esc(false)
},
esc (refresh) {
this.prevent_opt.save = false
this.$emit('close', refresh)
},
save () {
if (this.prevent_opt.save) { return } ;
this.prevent_opt.save = true
if (this.editAlertRule.type !== 3) {
this.editAlertRule.expr = this.expressions[0]
}
const params = {
...this.editAlertRule,
method: this.editAlertRule.method.join(','),
type: this.editAlertRule.type
}
this.$refs.alertRuleForm.validate((valid) => {
if (valid) {
params.receiver = this.editAlertRule.receiverShow.join(',')
params.schedDays = this.editAlertRule.schedDays.join(',')
params.trbShot = this.$refs.richTextEditor.getContent()
if (this.editAlertRule.id) {
this.$put('alert/rule', params).then(response => {
this.prevent_opt.save = false
if (response.code === 200) {
this.$message({ duration: 1000, type: 'success', message: this.$t('tip.saveSuccess') })
this.esc(true)
} else {
this.$message.error(response.msg)
}
})
} else {
this.$post('alert/rule', params).then(response => {
this.prevent_opt.save = false
if (response.code === 200) {
this.$message({ duration: 1000, type: 'success', message: this.$t('tip.saveSuccess') })
this.esc(true)
} else {
this.$message.error(response.msg)
}
})
}
} else {
this.prevent_opt.save = false
return false
}
})
},
del () {
if (this.prevent_opt.save) { return } ;
this.prevent_opt.save = true
this.$confirm(this.$t('tip.confirmDelete'), {
confirmButtonText: this.$t('tip.yes'),
cancelButtonText: this.$t('tip.no'),
type: 'warning'
}).then(() => {
this.$delete('alert/rule?ids=' + this.editAlertRule.id).then(response => {
this.prevent_opt.save = false
if (response.code === 200) {
this.$message({ type: 'success', message: this.$t('tip.deleteSuccess') })
this.esc(true)
} else {
this.$message.error(response.msg)
}
})
}).catch(() => {
this.prevent_opt.save = false
})
},
getUserList () {
this.$get('sys/user', { pageNo: 1, pageSize: -1 }).then(response => {
if (response.code == 200) {
this.userData = response.data.list
}
})
},
metricChange (val) {
this.editAlertRule.expr = val
},
getSeverityData () {
this.$get('alert/severity', { pageNo: 1, pageSize: -1 }).then(response => {
if (response.code == 200) {
this.severityData = response.data.list
if (!this.editAlertRule.id && this.severityData.length > 0) {
this.editAlertRule.severityId = this.severityData[0].id
}
}
})
},
getNotifyData () {
this.$get('alert/notify/method', { pageNo: 1, pageSize: -1 }).then(response => {
if (response.code == 200) {
this.notifyData = response.data.list
}
})
},
receiverShowChange (val) {
this.editAlertRule.receiver = [...this.editAlertRule.receiverShow].join(',')
this.$refs.alertRuleForm.validateField('receiver')
},
validateNotify (rule, value, callback) {
if (this.editAlertRule.receiverShow.length && value.length === 0) {
callback(new Error(this.$t('validate.required')))
} else {
callback()
}
},
receiverAndNotifyValidate () {
if (!this.editAlertRule.notifyExpired && !this.editAlertRule.notifyActive) {
this.$refs.alertRuleForm.clearValidate('receiver')
this.$refs.alertRuleForm.clearValidate('method')
}
},
selectAlertRuleMetric (val) {
if (!this.editAlertRule.operator) {
this.editAlertRule.operator = '>'
}
if (val === 1) {
this.showSnmpTrap = true // showSnmpTrap 为 true 时显示 expr,threshold,unit
this.showMetrics = true
this.$refs.alertRuleForm.clearValidate('expr') // 移除from表单的 expr 验证
} else if (val === 2) {
this.showMetrics = false // showMetrics 为 false 时,展示 Logs label
this.showSnmpTrap = true // showSnmpTrap 为 true 时显示 expr,threshold,unit
this.$refs.alertRuleForm.clearValidate('expr') // 移除from表单的 expr 验证
} else if (val === 3) {
this.showSnmpTrap = false // showSnmpTrap 为 false 时,展示 OID
this.showMetrics = false
// this.editAlertRule.inr = ''
// this.editAlertRule.last = '15'
// this.editAlertRule.unit = 2
// this.editAlertRule.operator = '>'
// this.editAlertRule.threshold = ''
if (!this.editAlertRule.last) {
this.editAlertRule.last = 60
}
}
},
afterInitRich () {
this.$refs.alertName.focus()
}
},
mounted () {
this.getUserList()
this.getSeverityData()
this.getNotifyData()
},
watch: {
alertRule: {
deep: true,
immediate: true,
handler (n, o) {
this.isEdit = true
this.editAlertRule = JSON.parse(JSON.stringify(n))
if (this.editAlertRule.id || this.editAlertRule.name) {
this.expressions = [this.editAlertRule.expr]
this.showTypeSelect = true // 当 edit 时禁用 type下拉框
if (n.type === 1) {
this.editAlertRule.type = 1
} else if (n.type === 2) {
this.showMetrics = false // showMetrics 为 false 时,展示 Logs label
this.editAlertRule.type = 2
} else if (n.type === 3) {
this.showSnmpTrap = false // showSnmpTrap 为 false 时,展示 OID
this.editAlertRule.inr = ''
this.editAlertRule.type = 3
return
}
this.$nextTick(() => {
this.expressions.forEach((ex, index) => {
if (ex) {
this.$refs.promql.metricChange(ex)
}
})
})
}
}
}
}
}
</script>
<style>
.hide-icon .el-input__suffix {
display: none;
}
</style>