Commit c1620424 authored by 张耀's avatar 张耀

feat:

预警规则设置接口对接
parent 0629c24b
// 通知方式
export const METHODS = {
1: '钉钉',
2: '短信'
dingtalk: '钉钉',
sms: '短信'
}
// 可以新增的数量
......
......@@ -3,7 +3,7 @@
:model="state.form"
ref="form_ref"
:rules="state.rules"
label-width="110px"
:label-width="labelWidth"
:disabled="disabled"
style="width: 100%">
<el-form-item :label="methodLabel || '通知方式'" prop="method">
......@@ -97,6 +97,10 @@ const props = defineProps({
type: Object,
default: () => ({}),
},
labelWidth: {
type: String,
default: "110px",
},
});
const form_ref = ref(null);
// 表单数据
......
......@@ -129,13 +129,42 @@ const rule_label = [
],
];
const rule_data = ref({});
const ruleTypeOptions = {
empty: "",
1: "百分比范围",
2: "毫秒范围",
3: "秒范围",
4: "个范围",
5: "温度范围",
const ruleTypeOptions = ref({});
const getRuleTypeOptions = () => {
let arr = [
{
id: "empty",
label: "",
},
{
id: "1",
label: "百分比范围",
unit: "%",
},
{
id: "2",
label: "毫秒范围",
unit: "ms",
},
{
id: "3",
label: "秒范围",
unit: "s",
},
{
id: "4",
label: "个范围",
unit: "",
},
{
id: "5",
label: "温度范围",
unit: "",
},
];
arr.forEach((e) => {
ruleTypeOptions.value[e.id] = e.label;
});
};
const advanced_label = [
[
......@@ -225,6 +254,7 @@ const getInfoData = () => {
});
};
onBeforeMount(() => {
getRuleTypeOptions();
getInfoData();
});
</script>
......
......@@ -211,8 +211,7 @@ const selectable = (row, index) => {
};
const getTableRows = () => {
let params = { ...state.filter, class_id: node.value.data.class_id };
// axios.get("/v1/api/metric_config/list", { params }).then((res) => {
axios.get("/v1/api/metric_config/list").then((res) => {
axios.get("/v1/api/metric_config/list", { params }).then((res) => {
if (res.data.code == 200) {
state.tableRows =
res.data.data?.list?.map((e) => {
......
......@@ -70,14 +70,40 @@ const props = defineProps({
// 预警规则类型下拉
const ruleTypeOptions = ref({});
const getRuleTypeOptions = () => {
ruleTypeOptions.value = {
empty: "",
1: "百分比范围",
2: "毫秒范围",
3: "秒范围",
4: "个范围",
5: "温度范围",
};
let arr = [
{
id: "empty",
label: "",
},
{
id: "1",
label: "百分比范围",
unit: "%",
},
{
id: "2",
label: "毫秒范围",
unit: "ms",
},
{
id: "3",
label: "秒范围",
unit: "s",
},
{
id: "4",
label: "个范围",
unit: "",
},
{
id: "5",
label: "温度范围",
unit: "",
},
];
arr.forEach((e) => {
ruleTypeOptions.value[e.id] = e.label;
});
};
// 当前是否是编辑
const isEdit = computed(() => !!props.row);
......
......@@ -7,7 +7,7 @@
</div>
<div class="add-btns">
<el-button size="default" @click="Cancle">取消</el-button>
<el-button type="primary" size="default" @click="Save">保存</el-button>
<el-button type="primary" size="default" @click="SaveSubmit">保存</el-button>
</div>
</div>
</div>
......@@ -18,15 +18,20 @@ import { ref } from "vue";
import { useRouter } from "vue-router";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import addForm from "../modules/add-form.vue";
import { ElMessage } from "element-plus";
import { Save } from "../modules/interface.js";
const router = useRouter();
const Cancle = () => {
router.go(-1);
};
const add_form = ref(null);
const Save = async () => {
const SaveSubmit = async () => {
let res = await add_form.value.Submit();
if (!res) return;
console.log("res: ", res);
Save(res, {}, () => {
Cancle();
});
};
</script>
......
......@@ -13,9 +13,17 @@
</template>
</Info>
</div>
<gap-title :hasLine="true" title="预警范围"></gap-title>
<div class="info">
<Info :labelData="warning_scope_label" :valueData="watning_scope_data"> </Info>
<div class="warn-scope" v-if="detection_type == 1">
<gap-title :hasLine="true" title="预警范围"></gap-title>
<div class="info">
<Info :labelData="warning_scope_label" :valueData="watning_scope_data"> </Info>
</div>
</div>
<div class="warn-scope" v-else>
<gap-title :hasLine="true" title="指标表达式"></gap-title>
<div class="indicator-expression">
<bg-code-editor v-model="indicator_expression" :disabled="true"></bg-code-editor>
</div>
</div>
<gap-title :hasLine="true" title="预警规则"></gap-title>
<div class="info">
......@@ -29,7 +37,7 @@
<div class="info">
<Info :labelData="ticket_push_label" :valueData="ticket_push_data">
<template #notification_method="{ valueData }">
<span>{{ valueData.notification_method.map((e) => METHODS[e]).join("") }}</span>
<span>{{ valueData.notification_method?.map((e) => METHODS[e]).join("") || "" }}</span>
</template>
</Info>
<div class="push-lists">
......@@ -42,13 +50,34 @@
</template>
<script setup>
import { ref, onBeforeMount, computed } from "vue";
import { ElMessage } from "element-plus";
import axios from "@/request/http.js";
import { useRoute } from "vue-router";
import gapTitle from "@/components/gap-title.vue";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import Info from "@/components/warn-detail/info.vue";
import { METHODS } from "@/components/manual-distribution/env.js";
const STATUS_OBJ = {
enabled: "启用",
disabled: "禁用",
const route = useRoute();
const { id } = route.query;
const STATUS_OBJ = ["禁用", "启用"];
const ruleTypeOptions = ref({});
const selectRule = {
"=": "等于",
"!=": "不等于",
"=~": "正则匹配",
"!~": "正则不匹配",
};
const riskLevelOptions = {
4: "重大风险",
3: "较大风险",
2: "一般风险",
1: "低风险",
};
const unitOptions = {
s: "",
m: "分钟",
h: "小时",
};
const labelData = [
[
......@@ -92,34 +121,9 @@ const labelData = [
},
],
];
const info = {
warning_rule_name: "服务中断推送规则1",
status: "enabled",
warning_target: "容器云",
warning_type: "容器集群",
warning_index: "CPU使用率",
create_by: "admin",
create_time: "2020-01-01 00:00:00",
update_time: "2020-01-01 00:00:00",
};
const warning_scope_label = [
[
{
prop: "colony",
label: "集群",
},
],
[
{
prop: "core_com",
label: "核心组件",
},
],
];
const watning_scope_data = {
colony: "等于 default",
core_com: "等于 kube-apiserver/kube-apiserver2/kube-apiserver3",
};
const info = ref({});
const warning_scope_label = ref([]);
const watning_scope_data = ref({});
const advanced_label = [
[
{
......@@ -134,10 +138,7 @@ const advanced_label = [
},
],
];
const advanced_data = {
duration: "直接产生预警",
inspection_cycle: "1分钟",
};
const advanced_data = ref({});
const ticket_push_label = [
[
{
......@@ -158,51 +159,135 @@ const ticket_push_label = [
},
],
];
const ticket_push_data = {
notification_method: ["1", "2"],
push_num: "10次",
push_frequency: "60分钟",
};
const ruleHeaders = [
{
prop: "warning_threshold",
label: "预警阈值",
},
const ticket_push_data = ref({});
const ruleHeaders = ref([
{
prop: "risk_level",
label: "风险程度",
},
];
const ruleRows = [
{
warning_threshold: "12% - 50% ",
risk_level: "较大风险",
},
{
warning_threshold: "50% - 100% ",
risk_level: "重大风险",
},
];
]);
const ruleRows = ref([]);
const pushHeaders = [
{
prop: "warning_threshold",
label: "预警阈值",
},
{
prop: "risk_level",
label: "风险程度",
prop: "system_account",
label: "账号",
},
];
const pushRows = [
{
warning_threshold: "12% - 50% ",
risk_level: "较大风险",
prop: "user_name",
label: "姓名",
},
{
warning_threshold: "50% - 100% ",
risk_level: "重大风险",
prop: "phone",
label: "联系方式",
},
];
const pushRows = ref([]);
const detection_type = ref(1);
const indicator_expression = ref("");
const getInfoData = () => {
axios
.get("/v1/api/alert_rules", {
params: {
id: id,
},
})
.then((res) => {
if (res.data.code == 200) {
const { data } = res.data;
detection_type.value = data.detection_type;
indicator_expression.value = data.expr;
info.value = {
warning_rule_name: data.metric_name,
status: data.is_enabled,
warning_target: data.class_parent_name,
warning_type: data.class_name,
warning_index: data.metric_config_name,
create_by: data.created_by,
create_time: data.created_at,
update_time: data.updated_at,
};
data.alert_range.forEach((e) => {
warning_scope_label.value.push([
{
prop: e.name,
label: e.chinese_name || e.name,
},
]);
watning_scope_data.value[e.name] = e.value == ".*" ? "全部" : `${selectRule[e.compare]} ${e.value}`;
});
let isEmpty = !data.alert_rule_type || data.alert_rule_type == "empty";
if (!isEmpty) {
ruleHeaders.value = [
{
prop: "warning_threshold",
label: "预警阈值",
},
...ruleHeaders.value,
];
}
ruleRows.value = data.alert_condition.map((e) => {
return {
warning_threshold: `${e.thresholds_min}${ruleTypeOptions.value[data.alert_rule_type].unit} - ${
e.thresholds_max
}${ruleTypeOptions.value[data.alert_rule_type].unit}`,
risk_level: riskLevelOptions[e.risk_level],
};
});
advanced_data.value = {
duration: data.duration == 0 ? "直接产生预警" : data.duration + unitOptions[data.duration_unit],
inspection_cycle: data.check_period + "分钟",
};
ticket_push_data.value = {
notification_method: data.notify_method,
push_num: data.notify_push_count + "",
push_frequency: data.notify_push_frequency + "分钟",
};
pushRows.value = data.notify_recipients;
} else {
ElMessage.error(res.data.data);
}
});
};
const getRuleTypeOptions = () => {
let arr = [
{
id: "empty",
label: "",
},
{
id: "1",
label: "百分比范围",
unit: "%",
},
{
id: "2",
label: "毫秒范围",
unit: "ms",
},
{
id: "3",
label: "秒范围",
unit: "s",
},
{
id: "4",
label: "个范围",
unit: "",
},
{
id: "5",
label: "温度范围",
unit: "",
},
];
arr.forEach((e) => {
ruleTypeOptions.value[e.id] = e;
});
getInfoData();
};
onBeforeMount(() => {
getRuleTypeOptions();
});
</script>
<style lang="scss" scoped>
......@@ -221,6 +306,16 @@ const pushRows = [
:deep(.gap-title) {
margin-bottom: 16px;
}
.warn-scope {
margin-bottom: 24px;
.indicator-expression {
height: 300px;
width: 100%;
:deep(.vue-ace-editor) {
margin-top: 0;
}
}
}
.info {
max-width: 1072px;
width: 100%;
......@@ -241,8 +336,8 @@ const pushRows = [
border-radius: 50%;
margin-right: 8px;
$statusObj: (
enabled: #48ad97,
disabled: #9e9e9e,
1: #48ad97,
0: #9e9e9e,
);
@each $status, $color in $statusObj {
&-#{$status} {
......
......@@ -7,181 +7,155 @@
</div>
<div class="add-btns">
<el-button size="default" @click="Cancle">取消</el-button>
<el-button type="primary" size="default" @click="Save">保存</el-button>
<el-button type="primary" size="default" @click="SaveSubmit">保存</el-button>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { useRouter } from "vue-router";
import { ref, onBeforeMount } from "vue";
import { useRouter, useRoute } from "vue-router";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import addForm from "../modules/add-form.vue";
// const infoData = ref({
// id: 1,
// name: "11",
// type_key: "static",
// duration: 1,
// time: 10,
// inspection_cycle: 1,
// push_num: 1,
// push_frequency: 60,
// enabled: true,
// type_com_ref: {
// warn_type: "colony",
// warn_indicator: "1",
// warn_target: "1",
// warn_type_com: {
// value1: "22",
// value1_select: "3",
// value2: "33",
// value2_select: "2",
// risk_level: 1,
// },
// },
// manual_distribution_form: {
// method: ["1", "2"],
// lists: [
// {
// user_id: 2,
// user_name: 22,
// phone: 13000000002,
// },
// {
// user_id: 3,
// user_name: 33,
// phone: 13000000003,
// },
// ],
// },
// });
const infoData = ref({
name: "11",
type_key: "static",
duration: 1,
time: 10,
inspection_cycle: 1,
push_num: 1,
push_frequency: 60,
enabled: true,
type_com_ref: {
warn_type: "gateway",
warn_indicator: "1",
warn_target: "1",
warn_type_com: {
ruleRows: [
{
from: "22",
to: "33",
risk_level: 1,
},
{
from: "44",
to: "55",
risk_level: 2,
},
],
},
},
manual_distribution_form: {
method: ["1"],
lists: [
{
user_id: 1,
user_name: 11,
phone: 13000000001,
},
],
},
});
// const infoData = ref({
// id: 1,
// name: "11",
// type_key: "static",
// duration: 1,
// time: 10,
// inspection_cycle: 1,
// push_num: 1,
// push_frequency: 60,
// enabled: true,
// type_com_ref: {
// warn_type: "colony",
// warn_indicator: "1",
// warn_target: "1",
// warn_type_com: {
// value1: "22",
// value1_select: "3",
// value2: "33",
// value2_select: "2",
// risk_level: 1,
// },
// },
// manual_distribution_form: {
// method: ["1", "2"],
// lists: [
// {
// user_id: 2,
// user_name: 22,
// phone: 13000000002,
// },
// {
// user_id: 3,
// user_name: 33,
// phone: 13000000003,
// },
// ],
// },
// });
// const infoData = ref({
// name: "11",
// type_key: "custom",
// duration: 1,
// time: 10,
// inspection_cycle: 1,
// push_num: 1,
// push_frequency: 60,
// enabled: true,
// type_com_ref: {
// warn_target: "22",
// warn_type: "33",
// warn_indicator: "44",
// indicator_expression: "55\n66\n77",
// rule_type: "1",
// ruleRows: [
// {
// from: "88",
// to: "99",
// risk_level: 1,
// },
// ],
// },
// manual_distribution_form: {
// method: ["1", "2"],
// lists: [
// {
// user_id: 1,
// user_name: 11,
// phone: 13000000001,
// },
// {
// user_id: 2,
// user_name: 22,
// phone: 13000000002,
// },
// ],
// },
// });
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
import { Save } from "../modules/interface.js";
const infoData = ref({});
const router = useRouter();
const route = useRoute();
const { id } = route.query;
const Cancle = () => {
router.go(-1);
};
const add_form = ref(null);
const Save = async () => {
const SaveSubmit = async () => {
let res = await add_form.value.Submit();
if (!res) return;
console.log("res: ", res);
Save(res, { id }, () => {
Cancle();
});
};
const staticTypeOptions = ref([]);
const findTypeBySecond = (id) => {
return staticTypeOptions.value.find((e) => {
return e.children.find((e_c) => e_c.class_id == id);
});
};
const getInfoData = () => {
axios
.get("/v1/api/alert_rules", {
params: {
id: id,
},
})
.then((res) => {
if (res.data.code == 200) {
const { data } = res.data;
console.log("data: ", data);
const isEmpty = !data.alert_rule_type || data.alert_rule_type == "empty";
let type_json = {
1: () => {
let obj = {
key: "static",
type_com_ref: {
warn_type: data.class_id,
warn_indicator: data.metric_config_id,
warn_target: findTypeBySecond(data.class_id)?.class_id || "",
rule_type: data.alert_rule_type,
},
};
// if (isEmpty) {
// obj.type_com_ref.risk_level = data.alert_condition[0].risk_level;
// } else {
obj.type_com_ref.warning_scpoe_form =
data.alert_range?.map((e) => {
return {
...e,
select: e.compare,
options: [],
};
}) || [];
obj.type_com_ref.ruleRows =
data.alert_condition?.map((e) => {
return {
from: e.thresholds_min,
to: e.thresholds_max,
risk_level: e.risk_level,
};
}) || [];
// }
return obj;
},
2: () => {
let obj = {
key: "custom",
type_com_ref: {
warn_target: data.class_parent_name,
warn_type: data.class_name,
warn_indicator: data.metric_config_name,
indicator_expression: data.expr,
rule_type: data.alert_rule_type,
},
};
if (isEmpty) {
obj.type_com_ref.risk_level = data.alert_condition[0].risk_level;
} else {
obj.type_com_ref.ruleRows =
data.alert_condition?.map((e) => {
return {
from: e.thresholds_min,
to: e.thresholds_max,
risk_level: e.risk_level,
};
}) || [];
}
return obj;
},
};
let d = type_json[`${data.detection_type}`]();
infoData.value = {
name: data.metric_name,
type_key: d.key,
unit: data.duration_unit,
time: data.duration,
inspection_cycle: data.check_period,
push_num: data.notify_push_count,
push_frequency: data.notify_push_frequency,
enabled: data.is_enabled ? 1 : 2,
type_com_ref: d.type_com_ref,
manual_distribution_form: {
method: data.notify_method,
lists:
data.notify_recipients?.map((e) => {
return {
user_id: e.system_account,
user_name: e.user_name,
phone: e.phone,
};
}) || [],
},
};
} else {
ElMessage.error(res.data.data);
}
});
};
const getStaticTypeOptions = () => {
axios.get("/v1/api/alert_class/tree").then(async (res) => {
if (res.data.code == 200) {
staticTypeOptions.value = res.data.data;
getInfoData();
} else {
ElMessage.error(res.data.msg);
}
});
};
onBeforeMount(() => {
getStaticTypeOptions();
});
</script>
<style lang="scss" scoped>
......
......@@ -4,7 +4,7 @@
<div class="main_container">
<bg-filter-group
@search="changeSearch"
v-model="filter.search"
v-model="filter.keyword"
inputWidth="380px"
placeholder="请输入预警规则名称/预警对象/预警分类/预警指标">
<template v-slot:left_action>
......@@ -24,7 +24,7 @@
<div class="left-filter filter_list">
<div class="filter_item">
<span class="filter_title">通知方式</span>
<el-select v-model="filter.notice_method" placeholder="请选择" style="width: 300px">
<el-select v-model="filter.notify_method" placeholder="请选择" style="width: 300px">
<el-option
v-for="(item, index) in noticeTypes"
:key="'noticeTypes' + index"
......@@ -35,7 +35,7 @@
</div>
<div class="filter_item">
<span class="filter_title">启用状态</span>
<el-select v-model="filter.state" placeholder="请选择" style="width: 300px">
<el-select v-model="filter.is_enabled" placeholder="请选择" style="width: 300px">
<el-option
v-for="(item, index) in stateOptions"
:key="'stateOptions' + index"
......@@ -49,8 +49,8 @@
<el-date-picker
style="width: 300px"
v-model="filter.time"
type="daterange"
value-format="yyyy-MM-DD"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期" />
......@@ -73,28 +73,31 @@
:isIndex="true"
:select="true"
:stripe="true">
<template v-slot:rule_name="{ row }">
<template v-slot:metric_name="{ row }">
<span class="can_click_text" @click="goDetail(row)">
{{ row.rule_name }}
{{ row.metric_name }}
</span>
</template>
<template v-slot:notice_method="{ row }">
{{ ["", "钉钉", "短信"][row.notice_method] }}
<template v-slot:notify_method="{ row }">
{{ row.notify_method.map((e) => METHODS[e]).join("") }}
</template>
<template #state="{ row }">
<template v-slot:notify_recipients="{ row }">
{{ row.notify_recipients.length }}
</template>
<template #is_enabled="{ row }">
<bg-switch
@click="stateChange(row)"
:labels="['否', '是']"
:values="[0, 1]"
v-model="row.state"></bg-switch>
:values="[2, 1]"
v-model="row.is_enabled"></bg-switch>
</template>
<template v-slot:created_time="{ row }">
{{ row.created_time ? row.created_time.split("+")[0].replace("T", " ").replace("Z", " ") : "-" }}
<template v-slot:created_at="{ row }">
{{ row.created_at ? row.created_at.split("+")[0].replace("T", " ").replace("Z", " ") : "-" }}
</template>
<template v-slot:action="{ row }">
<bg-table-btns2 :limit="3" :tableData="tableRows">
<bg-table-btn :disabled="row.state != 0" @click="editRow(row)">编辑</bg-table-btn>
<bg-table-btn :disabled="row.state != 0" @click="deleteRow(row)">删除</bg-table-btn>
<bg-table-btn :disabled="row.is_enabled != 2" @click="editRow(row)">编辑</bg-table-btn>
<bg-table-btn :disabled="row.is_enabled != 2" @click="deleteRow(row)">删除</bg-table-btn>
</bg-table-btns2>
</template>
</bg-table>
......@@ -128,7 +131,10 @@ import axios from "@/request/http.js";
import { Search } from "@element-plus/icons-vue";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import { useRouter } from "vue-router";
const METHODS = {
dingtalk: "钉钉",
sms: "短信",
};
const router = useRouter();
const dataTable = ref(null);
......@@ -141,13 +147,13 @@ const state = reactive({
},
{
name: "钉钉",
value: 1,
value: "dingtalk",
},
{
name: "短信",
value: 2,
value: "sms",
},
], // 风险等级
],
stateOptions: [
{
name: "全部",
......@@ -165,40 +171,40 @@ const state = reactive({
headers: [
{
label: "预警规则名称",
prop: "rule_name",
prop: "metric_name",
width: 200,
},
{
label: "预警分类",
prop: "warning_object",
prop: "class_parent_name",
},
{
label: "预警对象",
prop: "warning_type_name",
prop: "class_name",
},
{
label: "预警指标",
prop: "warning_target_name",
prop: "metric_config_name",
},
{
label: "通知方式",
prop: "notice_method",
prop: "notify_method",
},
{
label: "通知人数",
prop: "notice_people_num",
prop: "notify_recipients",
},
{
label: "是否启用",
prop: "state",
prop: "is_enabled",
},
{
label: "创建人",
prop: "created_user",
prop: "created_by",
},
{
label: "创建时间",
prop: "created_time",
prop: "created_at",
width: 160,
},
{
......@@ -212,10 +218,10 @@ const state = reactive({
selected: [], //选择数据
tableTotal: 0, // 表格数据条数
filter: {
notice_method: "", // 通知方式
state: "", // 状态
notify_method: "", // 通知方式
is_enabled: "", // 状态
time: [],
search: "",
keyword: "",
page: 1,
limit: 10,
}, // 表格筛选项
......@@ -253,7 +259,7 @@ const goDetail = (row) => {
}; // 查看详情
const changeSearch = (val) => {
state.filter.search = val;
state.filter.keyword = val;
changePage(1);
}; // 表格关键字筛选
......@@ -263,10 +269,10 @@ const filterAction = () => {
const filterClear = () => {
state.filter = {
notice_method: "", // 通知方式
state: "", // 状态
notify_method: "", // 通知方式
is_enabled: "", // 状态
time: "",
search: "",
keyword: "",
page: 1,
limit: 10,
};
......@@ -274,61 +280,28 @@ const filterClear = () => {
}; // 重置筛选项
const selectable = (row, index) => {
return row.state === 0;
return row.is_enabled === 2;
};
const getTableRows = () => {
let params = { ...state.filter };
// axios
// .get(`/apaas/system/v5/dictionary/list`, {
// params,
// })
// .then((res) => {
// if (res.data.code == 200) {
// state.tableRows = res.data.data || [];
// state.tableTotal = res.data.total;
// } else {
// ElMessage.error(res.data.data);
// }
// });
state.tableTotal = 23;
state.tableRows = [
{
id: 1,
rule_name: "容器云/tyyh",
warning_object: "容器云",
warning_type_name: "容器组",
warning_target_name: "服务中断",
notice_people_num: 1,
created_user: "王五",
created_time: "2020-01-01 00:00:00",
state: 1,
notice_method: 1,
},
{
id: 2,
rule_name: "容器云/kube-apiserver",
warning_object: "容器云",
warning_type_name: "容器集群",
warning_target_name: "内存使用率",
notice_people_num: 23,
created_user: "李四",
created_time: "2020-01-01 00:00:00",
state: 0,
notice_method: 2,
},
{
id: 3,
rule_name: "容器云/kube-apiserver",
warning_object: "容器云",
warning_type_name: "容器集群",
warning_target_name: "内存使用率",
notice_people_num: 456,
created_user: "张三",
created_time: "2020-01-01 00:00:00",
state: 0,
notice_method: 1,
},
];
let [start_time = "", end_time = ""] = state.filter.time || [];
let params = {
...state.filter,
start_time,
end_time,
};
Reflect.deleteProperty(params, "time");
axios
.get(`/v1/api/alert_rules/list`, {
params,
})
.then((res) => {
if (res.data.code == 200) {
state.tableRows = res.data.data?.list || [];
state.tableTotal = res.data.data.total_count;
} else {
ElMessage.error(res.data.data);
}
});
}; // 获取表格数据
const changePage = (page) => {
......@@ -341,30 +314,29 @@ const changeSize = (size) => {
changePage(1);
}; // 改变每页条数
const stateChange = (row) => {
console.log("更改状态");
// axios
// .put(`/xxx/xxx?id=${row.id}&state=${row.state}`)
// .then((res) => {
// if (res.data.code == 200) {
// ElMessage.success(res.data.msg);
// changePage(1);
// } else {
// ElMessage.error(res.data.data);
// row.state = row.state == 0 ? 1 : 0;
// }
// });
const stateChange = ({ id, is_enabled }) => {
const params = {
id,
is_enabled,
};
axios.put(`/v1/api/alert_rules/is_enabled`, params).then((res) => {
if (res.data.code == 200) {
ElMessage.success("状态修改成功");
changePage(1);
} else {
ElMessage.error(res.data.msg);
row.state = row.state == 1 ? 2 : 1;
}
});
};
const addRule = () => {
console.log("新增");
router.push({
path: `/forewarning/rule-set/add`,
});
}; // 新增规则
const editRow = (row) => {
console.log("编辑");
router.push({
path: `/forewarning/rule-set/edit`,
query: {
......@@ -377,7 +349,6 @@ const deleteRow = (row) => {
state.actionRow = row;
state.delType = 1;
state.delDialog = true;
console.log("删除");
}; // 删除
const delConfirm = () => {
......@@ -389,12 +360,22 @@ const delConfirm = () => {
return e.id;
});
}
console.log(ids);
state.delDialog = false;
setTimeout(() => {
clearSelected();
changePage(1);
}, 200);
axios
.delete("/v1/api/alert_rules", {
data: {
ids,
},
})
.then((res) => {
if (res.data.code == "200") {
ElMessage.success("删除成功");
state.delDialog = false;
clearSelected();
changePage(1);
} else {
ElMessage.error(res.data.msg);
}
});
}; // 确定删除
onBeforeMount(() => {
......
<template>
<div class="add-form">
<el-form :model="state.form" ref="form_ref" :rules="state.rules" label-width="110px">
<el-form :model="state.form" ref="form_ref" :rules="state.rules" label-width="120px">
<div class="add-form-item">
<el-form-item label="预警规则名称" prop="name">
<el-input v-model="state.form.name" :disabled="isEdit" placeholder="请输入预警规则名称"></el-input>
......@@ -70,6 +70,7 @@
<ManualDistributionForm
ref="manual_distribution_form"
class="manual-distribution-form"
labelWidth="120px"
:noElLabel="false"
methodLabel="预警通知方式"
:history="history" />
......
<template>
<div class="container-cluster-form">
<el-form :model="state.form" ref="form_ref" :rules="state.rules" label-width="110px">
<gap-title :hasLine="true" title="预警范围"></gap-title>
<div class="container-cluster-form-item warning-scope-form-item">
<el-form-item :label="typeToTextJson[props.type][0]" prop="value1">
<el-input v-model="state.form.value1" placeholder="请输入">
<template #prepend>
<el-select class="rule-select" v-model="state.form.value1_select" style="width: 114px">
<el-option v-for="(value, key) in selectRule" :key="key" :label="value" :value="key" />
</el-select>
</template>
</el-input>
</el-form-item>
<el-form-item :label="typeToTextJson[props.type][1]" prop="value2">
<el-input v-model="state.form.value2" placeholder="请输入">
<template #prepend>
<el-select class="rule-select" v-model="state.form.value2_select" style="width: 114px">
<el-option v-for="(value, key) in selectRule" :key="key" :label="value" :value="key" />
</el-select>
</template>
</el-input>
</el-form-item>
</div>
<gap-title :hasLine="true" title="预警规则"></gap-title>
<div class="container-cluster-form-item">
<el-form-item label="风险程度" prop="risk_level">
<el-select style="flex: 1" v-model="state.form.risk_level" placeholder="请选择" filterable>
<el-option v-for="item in riskLevelOptions" :key="item.id" :label="item.name" :value="item.id"> </el-option>
</el-select>
</el-form-item>
</div>
</el-form>
</div>
</template>
<script setup>
import { reactive, ref, watch } from "vue";
import gapTitle from "@/components/gap-title.vue";
const props = defineProps({
type: {
type: String,
default: "",
},
form: {
type: Object,
default: null,
},
});
const typeToTextJson = {
colony: ["集群", "核心组件"],
node: ["集群", "节点"],
group: ["命名空间", "容器"],
};
const selectRule = ref({
1: "遍历",
2: "等于",
3: "不等于",
4: "正则匹配",
5: "正则不匹配",
});
const state = reactive({
form: {
value1: "",
value1_select: "1",
value2: "",
value2_select: "1",
risk_level: "",
},
rules: {
value1: [{ required: true, message: "请输入", trigger: "blur" }],
value2: [{ required: true, message: "请输入", trigger: "blur" }],
risk_level: [{ required: true, message: "请选择", trigger: "change" }],
},
});
const form_ref = ref(null);
watch(
() => props.type,
(n) => {
state.form.value1 = "";
state.form.value1_select = "1";
state.form.value2 = "";
state.form.value2_select = "1";
form_ref.value.clearValidate(["value1", "value2", "risk_level"]);
}
);
watch(
() => props.form,
(n) => {
if (!n) return;
state.form.value1 = n.value1;
state.form.value1_select = n.value1_select || "1";
state.form.value2 = n.value2;
state.form.value2_select = n.value2_select || "1";
state.form.risk_level = n.risk_level;
},
{
deep: true,
immediate: true,
}
);
const riskLevelOptions = ref([
{
id: 1,
name: "重大风险",
},
{
id: 2,
name: "较大风险",
},
{
id: 3,
name: "一般风险",
},
{
id: 4,
name: "低风险",
},
]);
const Submit = async () => {
let form_valid = await new Promise((resolve, reject) => {
form_ref.value.validate((res) => resolve(res));
});
return form_valid;
};
defineExpose({
Submit,
form: state.form,
form_ref,
});
</script>
<style lang="scss" scoped>
.container-cluster-form {
:deep(.gap-title) {
margin-bottom: 16px;
}
&-item {
max-width: 1080px;
width: 100%;
padding-left: 8px;
}
.warning-scope-form-item {
display: flex;
align-items: center;
:deep(.el-form-item) {
flex: 1;
.el-input-group__prepend {
border-radius: 4px !important;
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
overflow: hidden;
}
}
:deep(.el-input-group__prepend) {
background-color: #2b4695;
.el-input__wrapper {
box-shadow: 1px 0 0 0 #2b4695 !important;
&,
.el-input__inner {
color: #fff;
}
.el-input__wrapper {
color: #fff;
}
}
}
}
}
</style>
<template>
<div class="custom-form">
<el-form :model="state.form" ref="form_ref" :rules="state.rules" label-width="110px">
<el-form :model="state.form" ref="form_ref" :rules="state.rules" label-width="120px">
<div class="custom-form-item">
<el-form-item label="预警对象" prop="warn_target">
<el-form-item label="预警分类" prop="warn_target">
<el-input
v-model="state.form.warn_target"
placeholder="请输入预警对象"
placeholder="请输入预警分类"
:disabled="isEdit"
:maxlength="20"
show-word-limit
clearable
@change="changeWarnCustomTarget"></el-input>
</el-form-item>
<el-form-item label="预警分类" prop="warn_type">
<el-form-item label="预警对象" prop="warn_type">
<el-input
v-model="state.form.warn_type"
placeholder="请输入预警分类"
placeholder="请输入预警对象"
:disabled="isEdit"
clearable
:maxlength="20"
......@@ -42,7 +42,7 @@
</el-form-item>
</div>
<gap-title :hasLine="true" title="预警规则"></gap-title>
<div class="add-form-item">
<div class="custom-form-item">
<el-form-item label="预警规则类型" prop="rule_type">
<el-select
style="flex: 1"
......@@ -54,7 +54,12 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="" v-if="state.form.ruleRows.length > 0 && state.form.rule_type != 'empty'">
<el-form-item label="风险程度" prop="risk_level" v-if="isEmpty">
<el-select style="flex: 1" v-model="state.form.risk_level" placeholder="请选择风险程度" filterable>
<el-option v-for="item in riskLevels" :key="item.id" :label="item.name" :value="item.id"> </el-option>
</el-select>
</el-form-item>
<el-form-item label="" v-else-if="state.form.ruleRows.length > 0">
<div class="rule-table" style="width: 100%">
<el-form
:model="state.form.ruleRows"
......@@ -76,7 +81,8 @@
style="flex: 1"
v-model="state.form.ruleRows[$index].from"
placeholder="请输入"
@input="changeWarningThresholdFrom($index)">
@input="inputNum($index, 'from')"
@blur="changeWarningThresholdFrom($index)">
<template #append>{{ unitMap }}</template>
</el-input>
</el-form-item>
......@@ -87,7 +93,8 @@
v-model="state.form.ruleRows[$index].to"
placeholder="请输入"
clearable
@input="changeWarningThresholdTo($index)">
@input="inputNum($index, 'to')"
@blur="changeWarningThresholdTo($index)">
<template #append>{{ unitMap }}</template>
</el-input>
</el-form-item>
......@@ -140,7 +147,7 @@
</template>
<script setup>
import { computed, reactive, ref, watch } from "vue";
import { computed, onBeforeMount, reactive, ref, watch } from "vue";
import gapTitle from "@/components/gap-title.vue";
import { ElMessage } from "element-plus";
const props = defineProps({
......@@ -164,7 +171,6 @@ var validateWarnIndex = (rule, value, callback) => {
callback();
}
};
const state = reactive({
form: {
warn_target: "",
......@@ -173,12 +179,14 @@ const state = reactive({
indicator_expression: "",
rule_type: "empty",
ruleRows: [],
risk_level: "",
},
rules: {
warn_target: [{ required: true, message: "请输入预警对象", trigger: "blur" }],
warn_type: [{ required: true, message: "请输入预警分类", trigger: "blur" }],
warn_target: [{ required: true, message: "请输入预警分类", trigger: "blur" }],
warn_type: [{ required: true, message: "请输入预警对象", trigger: "blur" }],
warn_indicator: [{ validator: validateWarnIndex, trigger: "blur" }],
indicator_expression: [{ required: true, message: "请输入预警指标", trigger: "blur" }],
risk_level: [{ required: true, message: "请选择风险程度", trigger: "change" }],
},
tableRules: {
from: [{ required: true, message: "请输入", trigger: "blur" }],
......@@ -186,6 +194,7 @@ const state = reactive({
risk_level: [{ required: true, message: "请选择风险程度", trigger: "change" }],
},
});
const isEmpty = computed(() => state.form.rule_type == "empty");
const changeWarnCustomTarget = () => {};
const changeWarnCustomType = () => {};
const setLimits = (index) => {
......@@ -200,11 +209,21 @@ const setLimits = (index) => {
}) || []
);
};
const inputNum = (index, key) => {
state.form.ruleRows[index][key] = +`${state.form.ruleRows[index][key]}`.replace(/[^\d]/g, "");
};
const changeWarningThresholdFrom = (index) => {
let { down, up } = limit.value;
if (down === "") return;
let { from, to } = state.form.ruleRows[index];
if (+from > +up || (index == 0 && +from < +down) || +from > +to) {
if (+from > +up || (index == 0 && +from < +down) || (to != "" && from > +to)) {
if (+from < +down) {
ElMessage.error(`下限不能小于${down}`);
} else if (+from > +up) {
ElMessage.error(`上限不能超过${up}`);
} else {
ElMessage.error(`下限不能大于上限`);
}
state.form.ruleRows[index].from = "";
return;
}
......@@ -212,11 +231,12 @@ const changeWarningThresholdFrom = (index) => {
if (rows.length == 0) return;
try {
rows.forEach((e) => {
if (+e.up >= +from > +e.down) {
if (e.up !== "" && e.down !== "" && +e.up >= +from > +e.down) {
throw "";
}
});
} catch (e) {
ElMessage.error(`该范围已被设置`);
state.form.ruleRows[index].from = "";
}
};
......@@ -224,7 +244,14 @@ const changeWarningThresholdTo = (index) => {
let { down, up } = limit.value;
if (up === "") return;
let { from, to } = state.form.ruleRows[index];
if ((index == 0 && +to > +up) || +to < +down || +from > +to) {
if ((index == 0 && +to > +up) || +to < +down || (from != "" && +from > +to)) {
if (+to > +up) {
ElMessage.error(`上限不能超过${up}`);
} else if (+to < +down) {
ElMessage.error(`下限不能小于${down}`);
} else {
ElMessage.error(`上限不能小于下限`);
}
state.form.ruleRows[index].to = "";
return;
}
......@@ -232,7 +259,7 @@ const changeWarningThresholdTo = (index) => {
if (rows.length == 0) return;
try {
rows.forEach((e) => {
if (+e.up > +to > +e.down) {
if (e.up !== "" && e.down !== "" && +e.up > +to > +e.down) {
throw "";
}
});
......@@ -247,6 +274,9 @@ const Submit = async () => {
let form_valid = await new Promise((resolve, reject) => {
form_ref.value.validate((res) => resolve(res));
});
if (isEmpty.value) {
return form_valid;
}
let table_form_valid = await new Promise((resolve, reject) => {
table_form.value.validate((res) => resolve(res));
});
......@@ -254,19 +284,19 @@ const Submit = async () => {
};
const riskLevels = ref([
{
id: 1,
id: 4,
name: "重大风险",
},
{
id: 2,
id: 3,
name: "较大风险",
},
{
id: 3,
id: 2,
name: "一般风险",
},
{
id: 4,
id: 1,
name: "低风险",
},
]);
......@@ -281,61 +311,73 @@ const riskLevelOptions = computed(() => {
return riskLevels.value.filter((e) => !rows.includes(e.id));
};
});
const ruleTypeOptions = ref({});
const unitMap = computed(() => {
return ruleTypeOptions[state.form.rule_type].unit || "";
return ruleTypeOptions.value[state.form.rule_type]?.unit || "";
});
const limit = computed(() => {
return (
ruleTypeOptions[state.form.rule_type].limit || {
ruleTypeOptions.value[state.form.rule_type].limit || {
down: "",
up: "",
}
);
});
const ruleTypeOptions = {
empty: {
label: "",
},
1: {
label: "百分比范围",
unit: "%",
limit: {
down: 0,
up: 100,
const getRuleTypeOptions = () => {
let arr = [
{
id: "empty",
label: "",
},
},
2: {
label: "毫秒范围",
unit: "ms",
limit: {
down: 0,
up: "",
{
id: "1",
label: "百分比范围",
unit: "%",
limit: {
down: 0,
up: 100,
},
},
},
3: {
label: "秒范围",
unit: "s",
limit: {
down: 0,
up: "",
{
id: "2",
label: "毫秒范围",
unit: "ms",
limit: {
down: 0,
up: "",
},
},
},
4: {
label: "个范围",
unit: "",
limit: {
down: 0,
up: "",
{
id: "3",
label: "秒范围",
unit: "s",
limit: {
down: 0,
up: "",
},
},
},
5: {
label: "温度范围",
unit: "",
limit: {
down: "",
up: "",
{
id: "4",
label: "个范围",
unit: "",
limit: {
down: 0,
up: "",
},
},
},
{
id: "5",
label: "温度范围",
unit: "",
limit: {
down: "",
up: "",
},
},
];
arr.forEach((e) => {
ruleTypeOptions.value[e.id] = e;
});
};
const ruleHeaders = [
{
......@@ -370,8 +412,9 @@ watch(
state.form.warn_indicator = f.warn_indicator;
state.form.indicator_expression = f.indicator_expression;
state.form.rule_type = f.rule_type;
state.form.risk_level = f.risk_level;
state.form.ruleRows =
f.ruleRows.map((e) => {
f?.ruleRows?.map((e) => {
return {
from: e.from,
to: e.to,
......@@ -387,9 +430,25 @@ watch(
immediate: true,
}
);
// watch(
// () => state.form,
// (n) => {
// console.log(state.form);
// },
// { deep: true }
// );
const formatForm = computed(() => {
return {
...state.form,
isEmpty: isEmpty.value,
};
});
onBeforeMount(() => {
getRuleTypeOptions();
});
defineExpose({
Submit,
form: state.form,
form: formatForm,
form_ref,
});
</script>
......
......@@ -24,7 +24,7 @@
@input="inputNum($index, 'from')"
@blur="changeWarningThresholdFrom($index)">
<template v-if="state.form.rule_type != 'empty'" #append>{{
ruleTypeOptions[props.rule_type].unit
ruleTypeOptions[rule_type]?.unit || ""
}}</template>
</el-input>
</el-form-item>
......@@ -38,7 +38,7 @@
@input="inputNum($index, 'to')"
@blur="changeWarningThresholdTo($index)">
<template v-if="state.form.rule_type != 'empty'" #append>{{
ruleTypeOptions[props.rule_type].unit
ruleTypeOptions[rule_type]?.unit || ""
}}</template>
</el-input>
</el-form-item>
......@@ -81,7 +81,7 @@
</template>
<script setup>
import { computed, reactive, ref, watch } from "vue";
import { computed, nextTick, reactive, ref, watch } from "vue";
import gapTitle from "@/components/gap-title.vue";
import { ElMessage } from "element-plus";
const props = defineProps({
......@@ -94,50 +94,64 @@ const props = defineProps({
default: "1",
},
});
const ruleTypeOptions = {
empty: {
label: "",
},
1: {
label: "百分比范围",
unit: "%",
limit: {
down: 0,
up: 100,
const ruleTypeOptions = ref({});
const getRuleTypeOptions = async (cb) => {
let arr = [
{
id: "empty",
label: "",
},
},
2: {
label: "毫秒范围",
unit: "ms",
limit: {
down: 0,
up: "",
{
id: "1",
label: "百分比范围",
unit: "%",
limit: {
down: 0,
up: 100,
},
},
},
3: {
label: "秒范围",
unit: "s",
limit: {
down: 0,
up: "",
{
id: "2",
label: "毫秒范围",
unit: "ms",
limit: {
down: 0,
up: "",
},
},
},
4: {
label: "个范围",
unit: "",
limit: {
down: 0,
up: "",
{
id: "3",
label: "秒范围",
unit: "s",
limit: {
down: 0,
up: "",
},
},
},
5: {
label: "温度范围",
unit: "",
limit: {
down: "",
up: "",
{
id: "4",
label: "个范围",
unit: "",
limit: {
down: 0,
up: "",
},
},
},
{
id: "5",
label: "温度范围",
unit: "",
limit: {
down: "",
up: "",
},
},
];
arr.forEach((e) => {
ruleTypeOptions.value[e.id] = e;
});
await nextTick();
cb && cb();
};
const state = reactive({
form: {
......@@ -158,19 +172,19 @@ const Submit = async () => {
};
const riskLevels = ref([
{
id: 1,
id: 4,
name: "重大风险",
},
{
id: 2,
id: 3,
name: "较大风险",
},
{
id: 3,
id: 2,
name: "一般风险",
},
{
id: 4,
id: 1,
name: "低风险",
},
]);
......@@ -215,9 +229,13 @@ const setLimits = (index) => {
}) || []
);
};
const rule_type = computed(() => {
// return props.rule_type
return "1";
});
const limit = computed(() => {
return (
ruleTypeOptions[props.rule_type].limit || {
ruleTypeOptions.value[rule_type.value].limit || {
down: "",
up: "",
}
......@@ -227,7 +245,7 @@ const changeWarningThresholdFrom = (index) => {
let { down, up } = limit.value;
if (down === "") return;
let { from, to } = state.form.ruleRows[index];
if (+from > +up || (index == 0 && +from < +down) || (to != "" && from > +to)) {
if ((up != "" && +from > +up) || (index == 0 && +from < +down) || (to != "" && from > +to)) {
if (+from < +down) {
ElMessage.error(`下限不能小于${down}`);
} else if (+from > +up) {
......@@ -242,7 +260,7 @@ const changeWarningThresholdFrom = (index) => {
if (rows.length == 0) return;
try {
rows.forEach((e) => {
if (+e.up >= +from > +e.down) {
if (e.up !== "" && e.down !== "" && +e.up >= +from > +e.down) {
throw "";
}
});
......@@ -252,13 +270,13 @@ const changeWarningThresholdFrom = (index) => {
}
};
const inputNum = (index, key) => {
state.form.ruleRows[index][key] = state.form.ruleRows[index][key].replace(/[^\d]/g, "");
state.form.ruleRows[index][key] = +`${state.form.ruleRows[index][key]}`.replace(/[^\d]/g, "");
};
const changeWarningThresholdTo = (index) => {
let { down, up } = limit.value;
if (up === "") return;
let { from, to } = state.form.ruleRows[index];
if ((index == 0 && +to > +up) || +to < +down || (from != "" && +from > +to)) {
if ((index == 0 && +to > +up) || (down != "" && +to < +down) || (from != "" && +from > +to)) {
if (+to > +up) {
ElMessage.error(`上限不能超过${up}`);
} else if (+to < +down) {
......@@ -273,7 +291,7 @@ const changeWarningThresholdTo = (index) => {
if (rows.length == 0) return;
try {
rows.forEach((e) => {
if (+e.up > +to > +e.down) {
if (e.up !== "" && e.down !== "" && +e.up > +to > +e.down) {
throw "";
}
});
......@@ -287,22 +305,25 @@ const removeRule = (index) => {
};
watch(
() => props.form,
(f) => {
if (!f?.ruleRows) {
(n) => {
if (!n || n?.length == 0) {
getRuleTypeOptions();
createRule();
return;
}
state.form.ruleRows =
f.ruleRows.map((e) => {
return {
from: e.from,
to: e.to,
risk_level: e.risk_level,
};
}) || [];
if (state.form.ruleRows.length == 0) {
createRule();
}
getRuleTypeOptions(() => {
state.form.ruleRows =
n?.map((e) => {
return {
from: e.from,
to: e.to,
risk_level: e.risk_level,
};
}) || [];
if (state.form.ruleRows.length == 0) {
createRule();
}
});
},
{
deep: true,
......@@ -325,7 +346,7 @@ defineExpose({
&-item {
max-width: 1080px;
width: 100%;
padding-left: 110px;
padding-left: 120px;
:deep(.el-table thead th) {
background-color: #f5f6f9;
}
......
import { ElMessage } from "element-plus";
import axios from "@/request/http.js";
// max(container_fs_usage_bytes{pod!=\"\", namespace!=\"arms-prom\",namespace!=\"monitoring\"}) by (pod_name, namespace, device)/max(container_fs_limit_bytes{pod!=\"\"}) by (pod_name,namespace, device) * 100
const setParams = (res, { id }) => {
console.log('res: ', res);
let isEmpty = res.type_com_ref.isEmpty
let params = {
// 预警规则名称
metric_name: res.name,
// 持续时间
duration: +res.time,
// 持续时间单位
duration_unit: res.unit,
// 检查周期
check_period: +res.inspection_cycle,
// 预警通知方式
notify_method: res.manual_distribution_form.method,
// 预警通知人员列表
notify_recipients: res.manual_distribution_form.lists.map(e => {
return {
system_account: `${e.user_id}`,
user_name: `${e.user_name}`,
phone: `${e.phone}`
}
}),
// 消息推送次数
notify_push_count: +res.push_num,
// 消息推送频率
notify_push_frequency: +res.push_frequency,
// 是否立即启用
is_enabled: res.enabled ? 1 : 2
}
let params_push = {
// 自定义传参
custom: () => {
return {
detection_type: 2,
// 预警分类
class_parent_name: res.type_com_ref.warn_target,
// 预警对象
class_name: res.type_com_ref.warn_type,
// 预警指标
metric_config_name: res.type_com_ref.warn_indicator,
// 指标表达式
expr: res.type_com_ref.indicator_expression || "",
// 预警规则(下拉)
alert_rule_type: res.type_com_ref.rule_type,
// 预警规则对象数组
alert_condition,
alert_range: []
}
},
// 静态阈值传参
static: () => {
return {
detection_type: 1,
// 预警对象
class_id: +res.type_com_ref.warn_type,
metric_config_id: res.type_com_ref.warn_indicator,
// 报警范围(指标)
alert_range: res.type_com_ref.warning_scpoe_form.map(e => {
return {
variable_name: e.variable_name,
metric_name: e.metric_name,
chinese_name: e.chinese_name,
metric_label: e.metric_label,
is_required: e.is_required,
is_linked: e.is_linked,
value: e.select == 'all' ? '.*' : e.value,
compare: e.select == 'all' ? '=~' : e.select
}
}),
// // 预警规则(下拉)
// alert_rule_type: res.module_data.alert_rule_type,
}
}
}
let alert_condition = []
// debugger;
if (isEmpty) {
alert_condition = [{
thresholds_max: 0,
thresholds_min: 0,
risk_level: +res.type_com_ref.risk_level
}]
} else {
alert_condition = res.type_com_ref.ruleRows.map(e => {
return {
thresholds_max: +e.to,
thresholds_min: +e.from,
risk_level: +e.risk_level
}
})
}
params = {
...params,
...params_push[res.type_key](),
// 预警规则对象数组
alert_condition,
}
if (id) {
params.id = id
}
return params;
}
export const Save = (res, p, cb) => {
let params = setParams(res, p);
console.log('params: ', params);
return;
axios[p.id ? 'put' : 'post']('/v1/api/alert_rules', params).then(res => {
if (res.data.code == 200) {
ElMessage.success(`${p.id ? '编辑' : '新增'}成功`)
cb && cb()
} else {
ElMessage.error(res.data.data)
}
})
}
\ No newline at end of file
<template>
<div class="static-form">
<el-form :model="state.form" ref="form_ref" :rules="state.rules" label-width="110px">
<el-form :model="state.form" ref="form_ref" :rules="state.rules" label-width="120px">
<div class="static-form-item">
<el-form-item label="预警对象/分类" prop="warn_type">
<el-cascader
......@@ -19,8 +19,10 @@
style="flex: 1"
:disabled="!formFormat.warn_type || props.isEdit"
v-model="state.form.warn_indicator"
placeholder="请选择预警指标">
<el-option v-for="(value, key) in warningIndexOptions" :key="key" :label="value" :value="key"> </el-option>
placeholder="请选择预警指标"
@change="chooseWarnIndicator">
<el-option v-for="item in warningIndexOptions" :key="item.id" :label="item.metric_name" :value="item.id">
</el-option>
</el-select>
</el-form-item>
</div>
......@@ -53,8 +55,8 @@
class="warn-scpoe-input-value"
v-model="state.form.warning_scpoe_form[index].value"
:placeholder="`请输入${item.chinese_name}`"
v-if="!showSelect.includes(item.select)"
:disabled="item.select == 1">
v-if="!item.is_linked"
:disabled="item.select == 'all'">
</el-input>
<el-select
class="warn-scpoe-input-value"
......@@ -64,10 +66,10 @@
filterable
:loading="item.loading"
remote
:remote-method="(query) => remoteMethod(query, index)"
:disabled="item.select == 'all'"
:remote-method="(query) => remoteMethod(query, index, item)"
multiple>
<el-option v-for="item in item.options" :key="item.id" :label="item.name" :value="item.id">
</el-option>
<el-option v-for="item in item.options" :key="item" :label="item" :value="item"> </el-option>
</el-select>
</el-form-item>
</div>
......@@ -83,7 +85,7 @@
</el-option>
</el-select>
</el-form-item>
<Gateway v-else ref="warn_type_com" :rule_type="alert_rule_type" />
<Gateway v-else ref="warn_type_com" :form="rule_rows" :rule_type="alert_rule_type" />
</div>
</div>
</div>
......@@ -98,33 +100,35 @@
</template>
<script setup>
import { computed, nextTick, reactive, ref, shallowReactive, watch } from "vue";
import { computed, nextTick, onMounted, reactive, ref, shallowReactive, watch } from "vue";
import gapTitle from "@/components/gap-title.vue";
// import CommonCom from "./common-com.vue";
import Gateway from "./gateway.vue";
const showSelect = ["4", "5"];
import { ElMessage } from "element-plus";
import axios from "@/request/http.js";
const showSelect = ["=~", "!~"];
const selectRule = ref({
1: "全部",
2: "等于",
3: "不等于",
4: "正则匹配",
5: "正则不匹配",
all: "全部",
"=": "等于",
"!=": "不等于",
"=~": "正则匹配",
"!~": "正则不匹配",
});
const riskLevelOptions = ref([
{
id: 1,
id: 4,
name: "重大风险",
},
{
id: 2,
id: 3,
name: "较大风险",
},
{
id: 3,
id: 2,
name: "一般风险",
},
{
id: 4,
id: 1,
name: "低风险",
},
]);
......@@ -145,15 +149,14 @@ const props = defineProps({
},
});
const changeSelect = (index, item) => {
console.log("item.select: ", item.select);
if (showSelect.includes(item.select)) {
if (showSelect.includes(item.is_linked)) {
state.form.warning_scpoe_form[index].value = [];
} else {
state.form.warning_scpoe_form[index].value = "";
}
};
const validateValue = (rule, value, callback, item, index) => {
if (!item.is_required || item.select == 1) return callback();
if (!item.is_required || item.select == "all") return callback();
if (item.value == "") {
let msg = showSelect.includes(item.select) ? `请选择${item.chinese_name}` : `请输入${item.chinese_name}`;
return callback(new Error(msg));
......@@ -173,140 +176,72 @@ const state = reactive({
risk_level: [{ required: true, message: "请选择风险程度", trigger: "change" }],
},
});
const module_data = {
id: "20da8f87-628a-4f0c-bd9c-0ad176d18d59",
class_id: 101,
metric_name: "xx请求次数告警",
expr: 'shttp_requests_total{method="GET",$pod$}',
duration: 5,
duration_unit: "m",
check_period: 3,
is_enabled: 1,
alert_rule_type: "9f1e6170-65e8-4e14-9c17-6a7b87a900a7",
created_by: "",
created_at: "2023-06-28 17:28:29",
updated_by: "",
updated_at: "2023-06-28 17:54:33",
alert_range: [
{
variable_name: "$pod$",
metric_name: "shttp_requests_total",
metric_label: "pod",
chinese_name: "demoString",
is_required: true,
is_linked: true,
},
{
variable_name: "$image$",
metric_name: "shttp_requests_total",
metric_label: "image",
chinese_name: "镜像",
is_required: true,
is_linked: true,
},
],
};
const module_data = ref({});
const alert_rule_type = ref("");
const isEmpty = computed(() => {
return false;
// return module_data.alert_rule_type == "9f1e6170-65e8-4e14-9c17-6a7b87a900a7";
});
state.form.warning_scpoe_form = module_data.alert_range.map((e) => {
return {
...e,
value: "",
select: "1",
options: [],
loading: false,
};
return !alert_rule_type.value || alert_rule_type.value == "empty";
});
watch(
() => state.form,
(n) => {
console.log(n);
},
{
deep: true,
}
);
const remoteMethod = (query, index) => {
console.log("query, index: ", query, index);
if (!query) {
state.form.warning_scpoe_form[index].options = [];
return;
}
state.form.warning_scpoe_form[index].loading = true;
setTimeout(() => {
state.form.warning_scpoe_form[index].loading = false;
state.form.warning_scpoe_form[index].options = [
{
id: 1,
name: 111,
const chooseWarnIndicator = () => {
axios
.get("/v1/api/metric_config", {
params: {
id: state.form.warn_indicator,
},
{
id: 2,
name: 222,
},
];
}, 1000);
})
.then((res) => {
if (res.data.code == 200) {
module_data.value = res.data.data;
alert_rule_type.value = module_data.value.alert_rule_type;
state.form.warning_scpoe_form = module_data.value.alert_range.map((e) => {
return {
...e,
value: "",
select: "all",
options: [],
loading: false,
};
});
} else {
ElMessage.error(res.data.data);
}
});
};
const remoteMethod = (query, index, item) => {
console.log("query, index, item: ", query, index, item);
// if (!query) {
// state.form.warning_scpoe_form[index].options = [];
// return;
// }
state.form.warning_scpoe_form[index].loading = true;
const params = {
// metric_name: item.metric_name,
metric_name: "grpc_client_handled_total",
// metric_label: item.metric_label,
metric_label: "grpc_method",
value: query,
};
axios.get(`/v1/api/prometheus/value`, { params }).then((res) => {
if (res.data.code == 200) {
state.form.warning_scpoe_form[index].options = res.data.data.list || [];
state.form.warning_scpoe_form[index].loading = false;
}
});
};
const warn_type_com_form = computed(() => props.form?.warn_type_com || null);
watch(
() => props.form,
(n) => {
if (!n) return;
state.form.warn_type = n.warn_target && n.warn_type ? [n.warn_target, n.warn_type] : [];
state.form.warn_indicator = n.warn_indicator;
},
{ deep: true, immediate: true }
);
const cascaderProps = {
value: "id",
label: "label",
value: "class_id",
label: "class_name",
};
const staticTypeOptions = ref([]);
const getStaticTypeOptions = () => {
axios.get("/v1/api/alert_class/tree").then(async (res) => {
if (res.data.code == 200) {
staticTypeOptions.value = res.data.data;
} else {
ElMessage.error(res.data.msg);
}
});
};
const staticTypeOptions = [
{
id: "1",
label: "容器云平台",
children: [
{
id: "colony",
label: "容器集群",
options: {
1: "服务中断",
2: "CPU使用率",
3: "内存使用率",
4: "磁盘使用率",
},
},
{
id: "node",
label: "容器节点",
options: {
1: "服务中断",
2: "CPU使用率",
3: "内存使用率",
4: "磁盘使用率",
},
},
{
id: "group",
label: "容器组",
options: {
1: "服务中断",
2: "CPU使用率",
3: "内存使用率",
},
},
{
id: "gateway",
label: "网关",
options: {
1: "平均响应时长(入口网关)",
},
},
],
},
];
const formFormat = computed(() => {
let [warn_target = "", warn_type = ""] = state.form.warn_type || [];
let obj = {
......@@ -314,21 +249,40 @@ const formFormat = computed(() => {
warn_target,
warn_type,
isEmpty: isEmpty.value,
// module_data: module_data.value
};
if (isEmpty.value) {
obj.warn_type_com = warn_type_com.value?.form || {};
if (!isEmpty.value) {
obj = {
...obj,
...(warn_type_com.value?.form || {}),
};
}
return obj;
});
const warningIndexOptions = ref({});
const cascader_ref = ref(null);
const changeWarnStaticType = async () => {
const { data } = cascader_ref.value.getCheckedNodes()[0];
warningIndexOptions.value = data.options;
let keys = Object.keys(warningIndexOptions.value);
state.form.warn_indicator = keys[0];
setTimeout(() => {
form_ref.value.clearValidate(["warn_indicator"]);
// const { data } = cascader_ref.value.getCheckedNodes()[0];
let class_id = state.form.warn_type[1];
let params = { page: 1, page_size: 10000000000000, class_id };
getWarningIndicator(params);
};
const getWarningIndicator = (params, cb) => {
axios.get("/v1/api/metric_config/list", { params }).then((res) => {
if (res.data.code == 200) {
warningIndexOptions.value = res.data.data?.list || [];
if (!cb) {
state.form.warn_indicator = warningIndexOptions.value[0]?.id || "";
if (state.form.warn_indicator) {
chooseWarnIndicator();
}
} else {
cb();
}
setTimeout(() => {
form_ref.value.clearValidate(["warn_indicator"]);
});
}
});
};
const changeRuleType = () => {};
......@@ -339,7 +293,7 @@ const Submit = async () => {
form_ref.value.validate((res) => resolve(res));
});
if (isEmpty.value) return form_valid;
let warn_type_com_valid = (await warn_type_com.value?.Submit()) || true;
let warn_type_com_valid = await warn_type_com.value?.Submit();
return form_valid && warn_type_com_valid;
};
const ruleHeaders = [
......@@ -353,6 +307,24 @@ const ruleHeaders = [
label: "风险程度",
},
];
const rule_rows = computed(() => props.form?.ruleRows || []);
watch(
() => props.form,
(n) => {
if (!n) return;
state.form.warn_type = n.warn_target && n.warn_type ? [n.warn_target, n.warn_type] : [];
state.form.warn_indicator = n.warn_indicator;
alert_rule_type.value = n.rule_type || "1";
let params = { page: 1, page_size: 10000000000000, class_id: n.warn_type };
getWarningIndicator(params, () => {
state.form.warning_scpoe_form = n?.warning_scpoe_form || [];
});
},
{ deep: true, immediate: true }
);
onMounted(() => {
getStaticTypeOptions();
});
defineExpose({
Submit,
form: formFormat,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment