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/table/alert/alertMessageTable.vue

682 lines
22 KiB
Vue
Raw Normal View History

<style lang="scss">
2021-04-13 20:33:12 +08:00
@import '../../../charts/chart';
</style>
<template>
2021-04-13 20:33:12 +08:00
<div>
<!--表格-->
<el-table
2021-04-13 20:33:12 +08:00
id="alertMessageTable"
ref="dataTable"
:cell-class-name="labelsClassName"
:data="tableData"
2021-04-13 20:33:12 +08:00
:height="height"
border
2021-04-13 20:33:12 +08:00
@header-dragend="dragend"
@sort-change="tableDataSort"
@selection-change="selectionChange"
2021-05-25 15:37:33 +08:00
@row-dblclick="(row)=>{$emit('messageDetail', row)}"
>
<el-table-column
:resizable="false"
align="center"
2021-04-13 20:33:12 +08:00
type="selection"
width="55">
</el-table-column>
<el-table-column
2021-04-13 20:33:12 +08:00
v-for="(item, index) in customTableTitle"
v-if="item.show"
:key="`col-${index}`"
2021-04-13 20:33:12 +08:00
:fixed="item.fixed"
:label="item.label"
2021-04-13 20:33:12 +08:00
:min-width="`${item.minWidth}`"
:prop="item.prop"
:resizable="true"
:sort-orders="['ascending', 'descending']"
2021-05-08 09:40:53 +08:00
:sortable="item.sortable"
2021-04-13 20:33:12 +08:00
:width="`${item.width}`"
>
<template slot-scope="scope" :column="item">
2021-03-19 18:52:19 +08:00
<template v-if="item.prop === 'alertRule'">
2021-04-13 20:33:12 +08:00
<div v-if="scope.row.alertRule.name" >
<span
class="data-column__span"
2021-04-13 20:33:12 +08:00
@mouseenter="alertMessageHover(scope.row.alertRule, true, $event)"
@mouseleave="alertMessageHover(scope.row.alertRule, false)"
>{{scope.row.alertRule.name}}</span>
<alertRuleInfo v-if="scope.row.alertRule.loading" :id="scope.row.alertRule.id" :severity-data="severityData" :that="scope.row.alertRule"></alertRuleInfo>
2020-09-28 16:27:03 +08:00
</div>
<template v-else>-</template>
</template>
2021-03-19 18:52:19 +08:00
<template v-else-if="item.prop === 'summary'">
<template v-if="scope.row[item.prop]">{{scope.row[item.prop]}}</template>
<template v-else>-</template>
</template>
2021-03-19 18:52:19 +08:00
<template v-else-if="item.prop === 'description'">
<template v-if="scope.row[item.prop]">{{scope.row[item.prop]}}</template>
<span v-else>-</span>
</template>
2021-03-19 18:52:19 +08:00
<template v-else-if="item.prop === 'remark'">
<template v-if="scope.row[item.prop]">{{scope.row[item.prop]}}</template>
<span v-else>-</span>
</template>
<span v-else-if="item.prop === 'severityId'&&scope.row['severity']" class="severity">
<i class="nz-icon nz-icon-circle" :style="{color:scope.row['severity'].color,'font-size':'12px','margin-right':'5px'}"></i> {{scope.row['severity'].name}}
2020-09-28 16:27:03 +08:00
</span>
<span v-else-if="item.prop === 'startAt'">{{utcTimeToTimezoneStr(scope.row.startAt)}}</span>
2021-03-19 18:52:19 +08:00
<template v-else-if="item.prop === 'duration'">
2021-04-13 20:33:12 +08:00
<el-tooltip :disabled="!scope.row.endAt" effect="light" placement="right">
<div slot="content">
{{$t('config.terminallog.endTime')}}<br/>
{{utcTimeToTimezoneStr(scope.row.endAt)}}
</div>
<span>{{getDuration(scope.row)}}</span>
</el-tooltip>
</template>
2021-03-19 18:52:19 +08:00
<template v-else-if="item.prop === 'labels'" class="labels">
<span v-for="(item, i) in labelsSort(scope.row.labels)" :key="i">
<span
@mouseenter="labelHover(scope.row, item.label, true, $event)"
@mouseleave="labelHover(scope.row, item.label, false)">
2020-09-28 16:27:03 +08:00
<nz-alert-tag
2021-04-13 20:33:12 +08:00
v-if="item.label !== 'alertname' && item.label !== 'severity'" :key="item.label" :cursor-point="tagType(item.label) !== 'info'"
:label="item.label"
:type="tagType(item.label)"
style="margin: 5px 0 5px 5px;"
2020-09-28 16:27:03 +08:00
>
{{item.value}}
</nz-alert-tag>
</span>
<alertLabel
v-if="(item.label === 'asset' ||item.label === 'module' || item.label === 'project'||item.label === 'endpoint') && scope.row[item.label] && scope.row[item.label].loading"
2020-09-28 16:27:03 +08:00
:id="scope.row[item.label].id"
:that="scope.row[item.label]"
:type="item.label"
></alertLabel>
</span>
</template>
2021-03-19 18:52:19 +08:00
<span v-else-if="item.prop === 'state'" :class="{'green': scope.row['state'] == 2, 'red': scope.row['state'] == 1}">
{{scope.row['state'] == 1 ? "Pending" : ""}}
{{scope.row['state'] == 2 ? "Expired" : ""}}
</span>
<span v-else-if="scope.row[item.prop]">{{scope.row[item.prop]}}</span>
<template v-else>-</template>
</template>
</el-table-column>
2021-04-13 20:33:12 +08:00
<el-table-column
v-if="showOption"
2021-04-13 20:33:12 +08:00
:resizable="false"
:width="operationWidth"
fixed="right">
<div slot="header" class="table-operation-title">{{$t('overall.option')}}</div>
<div slot-scope="scope" class="table-operation-items">
2021-05-10 15:59:39 +08:00
<button class="table-operation-item" @click="$emit('messageDetail', scope.row)"><i class="nz-icon nz-icon-view1"></i></button>
2021-05-19 14:26:09 +08:00
<el-dropdown v-has="['alertMessage_expired']" size="medium" trigger="hover" @command="tableOperation">
2021-05-10 15:59:39 +08:00
<div class="table-operation-item table-operation-item--more">
2021-05-27 13:53:42 +08:00
<i class="nz-icon nz-icon-more3"></i>
2021-05-10 15:59:39 +08:00
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-has="'alertMessage_expired'" :command="['delete', scope.row]"><i class="nz-icon nz-icon-delete"></i><span class="operation-dropdown-text">{{$t('overall.delete')}}</span></el-dropdown-item>
<el-dropdown-item v-has="'alertSilence_add'" :command="['fastSilence', scope.row, 'alertMessage']"><i class="nz-icon nz-icon-fast-silence"></i><span class="operation-dropdown-text">{{$t('overall.silenceAlert')}}</span></el-dropdown-item>
2021-05-10 15:59:39 +08:00
</el-dropdown-menu>
</el-dropdown>
2021-04-13 20:33:12 +08:00
</div>
</el-table-column>
</el-table>
</div>
</template>
<script>
2021-04-13 20:33:12 +08:00
import bus from '../../../../libs/bus'
2021-03-19 18:52:19 +08:00
import axios from 'axios'
2021-04-13 20:33:12 +08:00
import table from '@/components/common/mixin/table'
import nzAlertTag from '../../../page/alert/nzAlertTag'
import chartDataFormat from '../../../charts/chartDataFormat'
import alertRuleInfo from '../../alert/alertRuleInfo'
import alertLabel from '../../alert/alertLabel'
2021-04-13 20:33:12 +08:00
import { calcDurationByStringTimeB } from '../../js/tools'
2021-03-19 18:52:19 +08:00
export default {
name: 'alertMessageTable',
components: {
2021-04-13 20:33:12 +08:00
nzAlertTag,
2021-03-19 18:52:19 +08:00
alertRuleInfo: alertRuleInfo,
alertLabel: alertLabel
2021-03-19 18:52:19 +08:00
},
props: {
2021-04-13 20:33:12 +08:00
nowTime: {
type: String
},
showOption: {
type: Boolean,
default: true
2021-04-13 20:33:12 +08:00
}
2021-03-19 18:52:19 +08:00
},
2021-04-13 20:33:12 +08:00
mixins: [table, bus],
2021-03-19 18:52:19 +08:00
data () {
return {
/* 二级列表相关 */
tabList: [], // 二级列表的标签
tabDetailList: [], // 多个详情
2021-03-19 18:52:19 +08:00
// 详情相关
graphShow: false,
chartDatas: [],
sameLabels: ['instance', 'module', 'project', 'asset', 'endpoint', 'datacenter'],
legend: [],
searchTime: [new Date().setHours(new Date().getHours() - 1), new Date()],
currentMsg: {},
chartUnit: 5,
severityData: [],
2021-03-19 18:52:19 +08:00
tableTitle: [
{
label: 'ID',
prop: 'id',
show: true,
2021-05-25 16:13:59 +08:00
width: 160,
sortable: 'custom'
2021-03-19 18:52:19 +08:00
}, {
2021-05-10 15:59:39 +08:00
label: this.$t('alert.rule'),
2021-03-19 18:52:19 +08:00
prop: 'alertRule',
show: true,
2021-05-08 09:40:53 +08:00
width: 180,
sortable: 'custom'
2021-03-19 18:52:19 +08:00
}, {
label: this.$t('alert.list.labels'),
prop: 'labels',
show: true,
NotSet: true,
2021-05-08 09:40:53 +08:00
minWidth: 250,
sortable: 'custom'
2021-03-19 18:52:19 +08:00
}, {
label: this.$t('alert.severity'),
prop: 'severityId',
2021-03-19 18:52:19 +08:00
show: true,
2021-05-08 09:40:53 +08:00
width: 110,
sortable: 'custom'
2021-03-19 18:52:19 +08:00
}, {
label: this.$t('alert.summary'),
prop: 'summary',
show: true,
minWidth: 200
}, {
label: this.$t('overall.remark'),
2021-03-19 18:52:19 +08:00
prop: 'description',
show: true,
minWidth: 200
}, {
2021-03-19 18:52:19 +08:00
label: this.$t('alert.startAt'),
prop: 'startAt',
show: true,
2021-05-08 09:40:53 +08:00
width: 150,
sortable: 'custom'
2021-04-13 20:33:12 +08:00
}, {
2021-03-19 18:52:19 +08:00
label: this.$t('config.terminallog.duration'),
prop: 'duration',
show: true,
width: 150
}
2021-03-19 18:52:19 +08:00
],
viewAssetState: false,
tableDataInitNum: 0
}
},
computed: {
tagType () {
return (key) => {
if (key == 'asset' || key == 'module' || key == 'project' || key == 'datacenter' || key == 'endpoint') {
return 'normal'
} else {
return 'info'
}
}
},
2021-03-19 18:52:19 +08:00
tagValue () {
return (key, value) => {
if (key == 'type') {
if (value == 1) {
value = this.$t('project.project.projectName')
2021-03-19 18:52:19 +08:00
} else if (value == 2) {
value = this.$t('module.module.module')
} else if (value == 3) {
value = this.$t('asset.asset')
}
}
2021-03-19 18:52:19 +08:00
return key + '' + value
}
},
getDuration () {
return function (record) {
if (record.endAt) {
2021-03-19 18:52:19 +08:00
return calcDurationByStringTimeB(record.startAt, record.endAt)
}
2021-03-19 18:52:19 +08:00
return calcDurationByStringTimeB(record.startAt, this.nowTime)
}
}
},
mounted () {
this.getSeverityData()
},
2021-03-19 18:52:19 +08:00
methods: {
getSeverityData () {
this.$get('alert/severity', { pageNo: 1, pageSize: -1 }).then(response => {
if (response.code == 200) {
this.severityData = response.data.list
}
})
},
2021-04-13 20:33:12 +08:00
labelsSort (obj) {
2021-03-19 18:52:19 +08:00
const buildIn = ['asset', 'endpoint', 'module', 'project', 'datacenter']
if (typeof obj === 'string') obj = JSON.parse(obj)
const labels = JSON.parse(JSON.stringify(obj))
const result = []
for (const key of buildIn) {
if (key in labels) {
result.push({ label: key, value: labels[key] })
delete labels[key]
}
2021-03-19 18:52:19 +08:00
}
2021-05-10 19:47:29 +08:00
/* Object.keys(labels).sort().forEach(key => {
2021-03-19 18:52:19 +08:00
result.push({ label: key, value: labels[key] })
2021-05-10 19:47:29 +08:00
}) */
2021-03-19 18:52:19 +08:00
return result
},
chartUnitChange: function (unit) {
this.chartUnit = unit
this.$nextTick(() => {
2021-05-08 11:25:40 +08:00
this.queryChartDate()
})
},
2021-04-13 20:33:12 +08:00
formatThreshold (value, unit) {
2021-03-19 18:52:19 +08:00
const unitMethod = chartDataFormat.getUnit(unit)
if (unitMethod && value) {
return unitMethod.compute(value, null, 2)
} else {
return value
}
},
queryChartDate () {
const $temp = this
const start = this.searchTime[0] ? this.searchTime[0] : this.getTime(-1, 'h')
const end = this.searchTime[1] ? this.searchTime[1] : this.getTime(0, 'h')
this.searchTime = [start, end]
const timeDiff = (new Date(end).getTime() - new Date(start).getTime()) / 1000 / (24 * 60 * 60)
let step = '15s'
if (timeDiff < 1) {
step = '15s'
} else if (timeDiff < 7) {
step = '5m'
} else if (timeDiff < 30) {
step = '10m'
} else {
step = '30m'
}
if (this.$refs.messageChart) {
this.$refs.messageChart.startLoading()
const axiosArr = []
const paramStr = JSON.stringify(this.promQueryParamConvert(this.currentMsg))
axiosArr.push(axios.get('/prom/api/v1/query_range?query=' + paramStr.substring(1, paramStr.length - 1).replace(/\+/g, '%2B').replace(/ /g, '%20').replace(/\\/g, '') + '&start=' + this.$stringTimeParseToUnix(start) + '&end=' + this.$stringTimeParseToUnix(end) + '&step=' + step))
this.legend = []
this.chartDatas = []
axios.all(axiosArr).then(res => {
try {
res.forEach((response, promIndex) => {
if (response.status == 200) {
if (response.data.status == 'success') {
const queryData = response.data.data.result[0]
if (queryData) {
const chartData = {
type: 'line',
symbol: 'none', // 去掉点
smooth: 0.2, // 曲线变平滑
name: '',
lineStyle: {
width: 1,
opacity: 0.9
},
markLine: {
silent: true,
symbol: ['circle', 'circle'],
label: {
distance: this.computeDistance(chartDataFormat.getUnit(this.currentMsg.alertRule.unit ? this.currentMsg.alertRule.unit : 2).compute(this.currentMsg.alertRule.threshold)),
2021-04-13 20:33:12 +08:00
formatter (params) {
2021-03-19 18:52:19 +08:00
return chartDataFormat.getUnit($temp.currentMsg.alertRule.unit ? $temp.currentMsg.alertRule.unit : 2).compute(params.value)
}
},
lineStyle: {
color: '#d64f40',
width: 2,
type: 'dotted'
},
data: [{
yAxis: Number(this.currentMsg.alertRule.threshold)
}]
},
markArea: {
itemStyle: {
color: '#d64f40',
opacity: 0.1
},
data: [this.returnMarkArea()]
}
}
if (this.currentMsg.alertRule.operator == '==' || this.currentMsg.alertRule.operator == '!=') {
delete chartData.markArea
}
let alias = chartData.name
chartData.name += '{'
alias += '{'
Object.keys(queryData.metric).forEach((item, index) => {
const label = item
const value = queryData.metric[label]
chartData.name += label + "='" + value + "',"
})
chartData.name = chartData.name.charAt(chartData.name.length - 1) == ',' ? chartData.name.substr(0, chartData.name.length - 1) : chartData.name
chartData.name += '}'
const legend = {
name: chartData.name,
alias: alias,
isGray: false
}
this.legend.push(legend)
chartData.data = queryData.values.map((dpsItem, dpsIndex) => {
return [dpsItem[0] * 1000, parseFloat(dpsItem[1]).toFixed(2)]
})
this.chartDatas.push(chartData)
}
} else {
this.$message.error(response.data.error)
}
}
})
this.$nextTick(() => {
this.$refs.messageChart.setRandomColors(this.chartDatas.length)
this.$refs.messageChart.setLegend(this.legend)
this.$refs.messageChart.setSeries(this.chartDatas)
this.$refs.messageChart.endLoading()
})
} catch (err) {
this.$message.error(err)
this.$refs.messageChart.endLoading()
}
2021-03-19 18:52:19 +08:00
})
}
},
2021-04-13 20:33:12 +08:00
computeDistance (str) {
2021-03-19 18:52:19 +08:00
let width = 0
const html = document.createElement('span')
html.innerText = str
html.className = 'getTextWidth'
document.querySelector('body').appendChild(html)
width = document.querySelector('.getTextWidth').offsetWidth
document.querySelector('.getTextWidth').remove()
return Number('-' + (width + 5))
},
2021-04-13 20:33:12 +08:00
returnMarkArea () {
2021-03-19 18:52:19 +08:00
if (this.currentMsg) {
if (this.currentMsg.alertRule.operator == '>' || this.currentMsg.alertRule.operator == '>=') {
return [{ yAxis: this.currentMsg.alertRule.threshold }, {}]
} else {
return [{}, { yAxis: this.currentMsg.alertRule.threshold }]
}
2021-03-19 18:52:19 +08:00
}
},
detail (obj) {
if (!obj.current) { return }
this.chartDatas = []
this.legend = []
this.graphShow = true
this.currentMsg = obj
this.chartUnit = obj.alertRule.unit ? obj.alertRule.unit : 5
this.$nextTick(() => {
this.queryChartDate()
})
},
dialogClose () {
this.graphShow = false
2021-05-08 15:04:17 +08:00
},
2021-03-19 18:52:19 +08:00
getAlertList () {
if (!this.scrollbarWrap) {
this.$nextTick(() => {
2021-03-19 18:52:19 +08:00
this.scrollbarWrap = this.$refs.alertListTable.bodyWrapper
this.toTopBtnHandler(this.scrollbarWrap)
})
}
},
promQueryParamConvert (obj) {
let r = '(' + obj.alertRule.expr + ')'
let intoLabels = false
if (Object.keys(obj.labels).length > 0) {
r += (function () {
let group = ' and ' + '(group({'
let by = ' by ('
2021-03-19 18:52:19 +08:00
for (const k in obj.labels) {
if (k != 'alertname' && k != 'severity') {
intoLabels = true
group += k
group += '='
group += ("'" + obj.labels[k] + "',")
by += k
by += ','
}
2021-03-19 18:52:19 +08:00
}
if (intoLabels) {
group = group.substring(0, group.length - 1)
by = by.substring(0, by.length - 1)
group += '})'
by += ')'
return group + by + ')'
} else {
return ''
}
}())
}
return r
},
// asset弹框控制
tabControl (data) {
if (data === 'close') {
this.viewAssetState = false
this.$refs.assetEditUnit.tabView = false
}
},
toDeleteMessage (obj) {
2021-04-13 20:33:12 +08:00
this.$emit('toDelete', obj)
2021-03-19 18:52:19 +08:00
},
selectionChange (objs) {
this.$emit('selectionChange', objs)
2021-03-19 18:52:19 +08:00
},
closeDialog () {
this.importBox.show = false
this.deleteBox.show = false
},
2021-04-13 20:33:12 +08:00
getTimeString () {
2021-03-19 18:52:19 +08:00
const split = '-'
const date = new Date()
const year = date.getFullYear()
const month = this.formatNum(date.getMonth() + 1)
const day = this.formatNum(date.getDate())
const hours = this.formatNum(date.getHours())
const minutes = this.formatNum(date.getMinutes())
const seconds = this.formatNum(date.getSeconds())
return year + split + month + split + day + ' ' + hours + split + minutes + split + seconds
},
2021-04-13 20:33:12 +08:00
formatNum (num) {
2021-03-19 18:52:19 +08:00
return num > 9 ? num : '0' + num
},
labelsClassName (row) {
if (row.column.label == this.$t('alert.list.labels')) {
return 'alert-message-list-labels'
} else if (row.column.label == this.$t('alert.list.state')) {
if (row.row.state == 1) {
return ''
2021-03-19 18:52:19 +08:00
} else if (row.row.state == 2) {
return ''
2020-09-28 16:27:03 +08:00
}
2021-03-19 18:52:19 +08:00
} else {
return ''
}
},
2021-04-13 20:33:12 +08:00
closeViews () {
2021-03-19 18:52:19 +08:00
this.$refs.alertConfigBox.show(false, false)
this.$refs.projectBox.show(false, false)
this.$refs.moduleBox.show(false, false)
this.viewAssetState = false
},
// label tooltip是否显示
labelToolTipDis (labelType) {
switch (labelType) {
case 'asset':
case 'module':
case 'endpoint':
case 'project':
return false
default: return true
}
},
2021-04-13 20:33:12 +08:00
// alertName鼠标划入
alertMessageHover (item, loading, e) {
2021-03-19 18:52:19 +08:00
if (e) {
const dom = e.currentTarget
const position = dom.getBoundingClientRect()
this.$set(item, 'position', position)
}
this.$set(item, 'loading', loading)
// item.loading = true;
// this.$set(this.tableData,index,item);// 调用父组件
},
// label 鼠标划入
labelHover (item, type, loading, e) {
if (this.labelToolTipDis(type)) {
return
}
if (e) {
const dom = e.currentTarget
const position = dom.getBoundingClientRect()
this.$set(item[type], 'position', position)
}
this.$set(item[type], 'loading', loading)
// this.$set(this.tableData,index,item);// 调用父组件
},
// Severity Label
returnSeverityLabel (key) {
return this.$CONSTANTS.alertMessage.severityData.find(s => { return s.value == key }).label
}
/* plpsscrolly(el,self){
2020-11-20 15:02:36 +08:00
if (el._ps_.scrollbarYTop > 50) {
self.tools.showTopBtn = true;
self.tools.tableHover = true;
2020-11-20 15:02:36 +08:00
} else {
self.tools.showTopBtn = false;
self.tools.tableHover = false;
2020-11-20 15:02:36 +08:00
}
2021-03-19 18:52:19 +08:00
}, */
}
2021-03-19 18:52:19 +08:00
}
</script>
2021-05-08 09:29:25 +08:00
<style scoped lang=scss>
2021-05-08 11:25:40 +08:00
.pointer{
background: #FA901c;
border-radius: 2px;
border: none;
width: 40px;
height: 22px;
box-sizing: border-box;
text-align:center;
color: #fff;
font-size: 12px;
margin-right: 20px;
}
.alertLabelInfo{
border: 1px solid #ebeef5;
border-bottom: none;
font-size: 13px;
line-height: 26px;
}
.alertLabelBox{
display: flex;
justify-content:space-between;
border-bottom: 1px solid #ebeef5;
}
.alertLabelTitle{
text-align: left;
width: 90px;
border-right: 1px solid #ebeef5;
color: #666;
padding: 0 3px 0 13px;
}
.alertLabelValue{
text-align: left;
width: 150px;
color: #1a1a1a;
padding: 0 3px 0 13px;
}
.danger{
background-color: #d64f40;
color: white;
padding: 2px 5px;
border-radius: 4px;
}
.success{
background-color: #50d050;
color: white;
padding: 2px 5px;
border-radius: 4px;
}
/deep/.active-icon{
margin-top: 0;
}
.contentTable{
height: calc(100% - 105px)
}
.contentProject{
height: calc(100% - 60px);
margin-bottom: 15px;
}
.tabelH100{
height: 100% !important;
}
/deep/.too-long-split{
vertical-align: bottom;
}
/deep/.el-tooltip{
display: inline-block;
}
/deep/.alert-clean-pop.el-popover{
top: -30px !important;
}
.pointer{
cursor: pointer;
}
.severity .P1{
background: #F5846A;
2020-10-12 11:02:30 +08:00
border-radius: 2px;
font-size: 12px;
color: #FFFFFF;
padding: 2px 6px;
}
.severity .P2{
background: #F7A54A;
2020-10-12 11:02:30 +08:00
border-radius: 2px;
font-size: 12px;
color: #FFFFFF;
padding: 2px 6px;
}
.severity .P3{
background: #F1C13D;
2020-10-12 11:02:30 +08:00
border-radius: 2px;
font-size: 12px;
color: #FFFFFF;
padding: 2px 6px;
}
.red,.green{
color: white;
padding: 2px 5px;
border-radius: 4px;
}
/deep/ .nz-message .el-textarea textarea{
height: 108px !important;
}
</style>