Commit 302fb2f2 authored by 张耀's avatar 张耀

feat:

预警管理静态页面开发
parent e849bae2
......@@ -82,20 +82,28 @@ const states = reactive({
const codeChange = () => {
emit("update:modelValue", states.content);
};
onMounted(() => {
const init = (n) => {
let obj = "";
// console.log(typeof JSON.parse(this.datas));
try {
if (typeof JSON.parse(props.modelValue) == "object") {
obj = JSON.stringify(JSON.parse(props.modelValue), null, "\t");
if (typeof JSON.parse(n) == "object") {
obj = JSON.stringify(JSON.parse(n), null, "\t");
} else {
obj = props.modelValue;
obj = n;
}
} catch (e) {
obj = props.modelValue;
obj = n;
}
states.content = obj;
};
watch(
() => props.modelValue,
(n) => {
init(n);
}
);
onMounted(() => {
init(props.modelValue);
});
const { content } = toRefs(states);
......
......@@ -24,7 +24,7 @@
<!-- 序号 -->
</el-table-column>
<el-table-column
v-for="(header) in headers"
v-for="header in headers"
:width="header.width"
:min-width="header.minWidth"
:align="header.align"
......@@ -52,15 +52,8 @@
<script setup>
import { watch, ref, nextTick } from "vue";
import { selectTableMixin } from "./hook/mixin-select-table";
const {
nowSelectData,
allSelectData,
initAllSelectData,
selectData,
initSelectTableData,
runPage,
dealSelectData,
} = selectTableMixin();
const { nowSelectData, allSelectData, initAllSelectData, selectData, initSelectTableData, runPage, dealSelectData } =
selectTableMixin();
const props = defineProps({
height: {
......@@ -112,6 +105,11 @@ const props = defineProps({
type: Array,
default: () => [],
},
// 自定义返回值用来决定这一行的 CheckBox 是否可以勾选
selectable: {
type: Function,
default: null,
},
});
const table = ref(null);
......@@ -198,6 +196,10 @@ const tableRowClassName = ({ row, rowIndex }) => {
}
};
const selectable = (row, index) => {
// 判断是否传入自定义返回值用来决定这一行的 CheckBox 是否可以勾选,否则使用默认勾选逻辑
if (props.selectable) {
return props.selectable(row, index);
}
if (props.canEdit) {
if (row[props.canEditFlag] && row[props.canEditFlag] == 1) {
return false;
......
......@@ -2,4 +2,8 @@ export const TIMEING_RULES = {
1: '手动下发',
2: '按周',
3: '自定义时间'
}
export const MAX_DAY = 7;
export const ONLY_INPUT_NUM = (value) => {
return value.replace(/[^\d]/g, '')
}
\ No newline at end of file
......@@ -268,7 +268,6 @@ defineExpose({
}
}
.user-table {
max-height: 345px;
:deep(.el-table thead th) {
background-color: #f5f6f9;
}
......
......@@ -2,7 +2,7 @@
<div>
<div class="info-list" v-for="(list, index) in labelData" :key="`warn-${index}`">
<div class="info-item" v-for="(item, i) in list" :key="`warn-${index}-${i}`">
<div class="label">
<div class="label" :title="item.label">
{{ item.label }}
</div>
<div class="value">
......@@ -13,7 +13,7 @@
<span class="status" :class="`status-${valueData.status}`"></span>
<span>{{ STATUS_OBJ[valueData[item.prop]] }}</span>
</span>
<span v-else>{{ valueData[item.prop] }}</span>
<span v-else :title="valueData[item.prop]">{{ valueData[item.prop] }}</span>
</span>
</div>
</div>
......@@ -60,7 +60,7 @@ const status_obj = computed(() => {
align-items: center;
flex: 1;
height: 48px;
line-height: 46px;
line-height: 48px;
font-size: 14px;
color: #404a62;
.label {
......
<template>
<div class="detail_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<div class="add-form-main bg-scroll">
<add-form ref="add_form"></add-form>
</div>
<div class="add-btns">
<el-button size="default" @click="Cancle">取消</el-button>
<el-button type="primary" size="default" @click="SaveSubmit">保存</el-button>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import addForm from "../modules/add-form.vue";
import { Save } from "../modules/interface.js";
import { useRouter, useRoute } from "vue-router";
const router = useRouter();
const route = useRoute();
const { id, class_id } = route.query;
const add_form = ref(null);
const params = {};
const SaveSubmit = async () => {
let res = await add_form.value.Submit();
if (!res) return;
Save(res);
};
const Cancle = () => {
router.go(-1);
};
</script>
<style lang="scss" scoped>
.detail_container {
width: 100%;
height: calc(100vh - 56px);
padding: 0 24px 16px;
min-height: 100%;
.main_container {
height: calc(100% - 46px);
padding: 0;
.add-form-main {
padding: 24px 24px 16px;
height: calc(100% - 68px);
}
.add-btns {
height: 68px;
display: flex;
align-items: center;
justify-content: flex-end;
border-top: 1px solid #ddd;
padding: 0 24px;
}
}
}
</style>
<template>
<div>指标配置详情</div>
</template>
<script setup></script>
<style lang="scss" scoped></style>
<template>
<div class="detail_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<div class="add-form-main bg-scroll">
<add-form ref="add_form" :row="infoData"></add-form>
</div>
<div class="add-btns">
<el-button size="default" @click="Cancle">取消</el-button>
<el-button type="primary" size="default" @click="SaveSubmit">保存</el-button>
</div>
</div>
</div>
</template>
<script setup>
import { onBeforeMount, ref } from "vue";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import addForm from "../modules/add-form.vue";
import { Save, URL } from "../modules/interface.js";
import { ElMessage } from "element-plus";
import { useRouter, useRoute } from "vue-router";
const router = useRouter();
const route = useRoute();
const { id, class_id } = route.query;
const infoData = ref({});
const add_form = ref(null);
const SaveSubmit = async () => {
let res = await add_form.value.Submit();
if (!res) return;
Save(res, { id, class_id });
};
const Cancle = () => {
router.go(-1);
};
const getInfoData = () => {
// axios
// .get(URL, {
// params: {
// id: id,
// },
// })
// .then((res) => {
// if (res.data.code == 200) {
// console.log(res.data.data);
// } else {
// ElMessage.error(res.data.data);
// }
// });
let res = {
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,
},
],
};
infoData.value = {
name: res.metric_name,
indicator_expression: res.expr,
rule_type: res.alert_rule_type,
time: res.duration,
unit: res.duration_unit,
inspection_cycle: res.check_period,
warningScopeRows:
res.alert_range.map((e) => {
return {
key: e.variable_name,
input_indicator_tag: e.metric_label,
indicator_scope: e.metric_name,
indicator_tag: e.metric_label,
cname: e.chinese_name,
is_required: e.is_required ? 1 : 0,
is_linkage: e.is_linked ? 1 : 0,
};
}) || [],
state: res.is_enabled,
res: res,
};
};
onBeforeMount(() => {
getInfoData();
});
</script>
<style lang="scss" scoped>
.detail_container {
width: 100%;
height: calc(100vh - 56px);
padding: 0 24px 16px;
min-height: 100%;
.main_container {
height: calc(100% - 46px);
padding: 0;
.add-form-main {
padding: 24px;
height: calc(100% - 68px);
}
.add-btns {
height: 68px;
display: flex;
align-items: center;
justify-content: flex-end;
border-top: 1px solid #ddd;
padding: 0 24px;
}
}
}
</style>
This diff is collapsed.
import { ElMessage } from "element-plus";
const setParams = (res, { id, class_id }) => {
let params = {
class_id,
metric_name: res.name,
expr: res.indicator_expression,
alert_range: res.warningScopeRows.map((e) => {
return {
variable_name: e.key,
metric_name: e.indicator_scope,
metric_label: e.indicator_tag,
chinese_name: e.cname,
is_required: e.is_required == 1,
is_linked: e.is_linkage == 1,
};
}) || [],
duration: res.time,
duration_unit: res.unit,
check_period: res.inspection_cycle,
is_enabled: res.state,
alert_rule_type: res.rule_type,
}
if (id) {
params.id = id
}
return params;
}
export const URL = "https://so.wodcloud.co/v1/api/metric_config"
export const Save = (res, p) => {
let params = setParams(res, p);
console.log("params: ", params);
// axios[id ? 'put' : 'post'](URL, params).then(res => {
// if(res.data.code == 200){
// console.log('success');
// }else{
// ElMessage.error(res.data.data)
// }
// })
}
\ No newline at end of file
This diff is collapsed.
......@@ -87,6 +87,7 @@
:headers="headers"
:rows="tableRows"
@selectAc="selectRows"
:selectable="selectable"
:isIndex="true"
:select="true"
:stripe="true">
......@@ -398,7 +399,9 @@ const filterClear = () => {
};
changePage(1);
}; // 重置筛选项
const selectable = (row, index) => {
return row.state === 0;
};
const getTableRows = () => {
let params = { ...state.filter };
// axios
......
<template>
<div class="detail_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<div class="add-form-main bg-scroll">
<add-form ref="add_form"></add-form>
</div>
<div class="add-btns">
<el-button size="default" @click="Cancle">取消</el-button>
<el-button type="primary" size="default" @click="Save">保存</el-button>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { useRouter } from "vue-router";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import addForm from "../modules/add-form.vue";
const router = useRouter();
const Cancle = () => {
router.go(-1);
};
const add_form = ref(null);
const Save = async () => {
let res = await add_form.value.Submit();
if (!res) return;
console.log("res: ", res);
};
</script>
<style lang="scss" scoped>
.detail_container {
width: 100%;
height: calc(100vh - 56px);
padding: 0 24px 16px;
min-height: 100%;
.main_container {
height: calc(100% - 46px);
padding: 0;
.add-form-main {
padding: 24px;
height: calc(100% - 68px);
}
.add-btns {
height: 68px;
display: flex;
align-items: center;
justify-content: flex-end;
border-top: 1px solid #ddd;
padding: 0 24px;
}
}
}
</style>
<template>
<div>规则详情</div>
<div class="detail_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container bg-scroll">
<gap-title :hasLine="true" title="基本信息"></gap-title>
<div class="info">
<Info :labelData="labelData" :valueData="info">
<template #status="{ item, valueData }">
<span class="status-body">
<span class="status" :class="`status-${valueData.status}`"></span>
<span>{{ STATUS_OBJ[valueData[item.prop]] }}</span>
</span>
</template>
</Info>
</div>
<gap-title :hasLine="true" title="预警范围"></gap-title>
<div class="info">
<Info :labelData="warning_scope_label" :valueData="watning_scope_data"> </Info>
</div>
<gap-title :hasLine="true" title="预警规则"></gap-title>
<div class="info">
<bg-table border ref="ruletable" :headers="ruleHeaders" :rows="ruleRows" height="100%"> </bg-table>
</div>
<gap-title :hasLine="true" title="高级配置"> </gap-title>
<div class="info">
<Info :labelData="advanced_label" :valueData="advanced_data"> </Info>
</div>
<gap-title :hasLine="true" title="预警工单推送"> </gap-title>
<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>
</template>
</Info>
<div class="push-lists">
<bg-table border ref="pushtable" :headers="pushHeaders" :rows="pushRows" height="100%" :isIndex="true">
</bg-table>
</div>
</div>
</div>
</div>
</template>
<script setup></script>
<script setup>
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 labelData = [
[
{
label: "预警规则名称",
prop: "warning_rule_name",
},
{
label: "启用状态",
prop: "status",
},
],
[
{
label: "预警对象",
prop: "warning_target",
},
{
label: "预警分类",
prop: "warning_type",
},
],
[
{
label: "预警指标",
prop: "warning_index",
},
{
label: "创建人",
prop: "create_by",
},
],
[
{
label: "创建时间",
prop: "create_time",
},
{
label: "更新时间",
prop: "update_time",
},
],
];
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 advanced_label = [
[
{
prop: "duration",
label: "持续时间",
},
],
[
{
prop: "inspection_cycle",
label: "检查周期",
},
],
];
const advanced_data = {
duration: "直接产生预警",
inspection_cycle: "1分钟",
};
const ticket_push_label = [
[
{
prop: "notification_method",
label: "预警通知方式",
},
],
[
{
prop: "push_num",
label: "消息推送次数",
},
],
[
{
prop: "push_frequency",
label: "消息推送频率",
},
],
];
const ticket_push_data = {
notification_method: ["1", "2"],
push_num: "10次",
push_frequency: "60分钟",
};
const ruleHeaders = [
{
prop: "warning_threshold",
label: "预警阈值",
},
{
prop: "risk_level",
label: "风险程度",
},
];
const ruleRows = [
{
warning_threshold: "12% - 50% ",
risk_level: "较大风险",
},
{
warning_threshold: "50% - 100% ",
risk_level: "重大风险",
},
];
const pushHeaders = [
{
prop: "warning_threshold",
label: "预警阈值",
},
{
prop: "risk_level",
label: "风险程度",
},
];
const pushRows = [
{
warning_threshold: "12% - 50% ",
risk_level: "较大风险",
},
{
warning_threshold: "50% - 100% ",
risk_level: "重大风险",
},
];
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.detail_container {
width: 100%;
height: calc(100vh - 56px);
padding: 0 24px;
min-height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
.main_container {
height: 100%;
padding: 24px;
:deep(.gap-title) {
margin-bottom: 16px;
}
.info {
max-width: 1072px;
width: 100%;
padding: 0 8px 0;
&:not(:last-child) {
padding-bottom: 24px;
}
.push-lists {
margin-top: 16px;
}
}
}
}
.status {
display: inline-block;
width: 6px;
height: 6px;
border-radius: 50%;
margin-right: 8px;
$statusObj: (
enabled: #48ad97,
disabled: #9e9e9e,
);
@each $status, $color in $statusObj {
&-#{$status} {
background-color: $color;
}
}
}
.status-body {
display: flex;
align-items: center;
}
</style>
<template>
<div class="detail_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<div class="add-form-main bg-scroll">
<add-form ref="add_form" :row="infoData"></add-form>
</div>
<div class="add-btns">
<el-button size="default" @click="Cancle">取消</el-button>
<el-button type="primary" size="default" @click="Save">保存</el-button>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { useRouter } 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,
// },
// ],
// },
// });
const router = useRouter();
const Cancle = () => {
router.go(-1);
};
const add_form = ref(null);
const Save = async () => {
let res = await add_form.value.Submit();
if (!res) return;
console.log("res: ", res);
};
</script>
<style lang="scss" scoped>
.detail_container {
width: 100%;
height: calc(100vh - 56px);
padding: 0 24px 16px;
min-height: 100%;
.main_container {
height: calc(100% - 46px);
padding: 0;
.add-form-main {
padding: 24px;
height: calc(100% - 68px);
}
.add-btns {
height: 68px;
display: flex;
align-items: center;
justify-content: flex-end;
border-top: 1px solid #ddd;
padding: 0 24px;
}
}
}
</style>
......@@ -69,6 +69,7 @@
:headers="headers"
:rows="tableRows"
@selectAc="selectRows"
:selectable="selectable"
:isIndex="true"
:select="true"
:stripe="true">
......@@ -243,7 +244,12 @@ const batchDelete = () => {
const goDetail = (row) => {
console.log("去详情");
router.push(`/forewarning/rule-set/detail?id=${row.id}`);
router.push({
path: "/forewarning/rule-set/detail",
query: {
id: row.id,
},
});
}; // 查看详情
const changeSearch = (val) => {
......@@ -267,6 +273,9 @@ const filterClear = () => {
changePage(1);
}; // 重置筛选项
const selectable = (row, index) => {
return row.state === 0;
};
const getTableRows = () => {
let params = { ...state.filter };
// axios
......@@ -349,12 +358,19 @@ const stateChange = (row) => {
const addRule = () => {
console.log("新增");
// router.push(`/xxx/xxx`);
router.push({
path: `/forewarning/rule-set/add`,
});
}; // 新增规则
const editRow = (row) => {
console.log("编辑");
// router.push(`/xxx/xxx?id=${row.id}`);
router.push({
path: `/forewarning/rule-set/edit`,
query: {
id: row.id,
},
});
}; // 编辑
const deleteRow = (row) => {
......
<template>
<div class="add-form">
<el-form :model="state.form" ref="form_ref" :rules="state.rules" label-width="110px">
<div class="add-form-item">
<el-form-item label="预警规则名称" prop="name">
<el-input v-model="state.form.name" :disabled="isEdit" placeholder="请输入预警规则名称"></el-input>
</el-form-item>
<el-form-item label="检测类型" prop="type">
<el-button-group>
<el-button
v-for="(value, key) in types"
:key="key"
:type="state.form.type_key == key ? 'primary' : ''"
size="small"
@click="changeType(key)"
:disabled="isEdit">
{{ value }}
</el-button>
</el-button-group>
</el-form-item>
</div>
<component
ref="type_com_ref"
:is="typeCom[state.form.type_key]"
:isEdit="isEdit"
:form="typrFormData"></component>
<gap-title :hasLine="true" title="高级配置"></gap-title>
<div class="add-form-item">
<div class="duration">
<el-form-item label="持续时间" prop="time">
<span>当预警持续</span>
<el-input v-model="state.form.time" placeholder="请输入持续时间" @input="inputNum"></el-input>
</el-form-item>
<el-form-item label="" prop="time" class="no-el-label">
<el-select v-model="state.form.unit" placeholder="请选择">
<el-option v-for="(value, key) in unitOptions" :key="key" :label="value.label" :value="key"> </el-option>
</el-select>
<span>时产生报警</span>
</el-form-item>
</div>
<el-form-item label="检查周期" prop="inspection_cycle">
<el-select style="flex: 1" v-model="state.form.inspection_cycle" placeholder="请选择">
<el-option v-for="item in inspectionCycleOptions" :key="item" :label="item" :value="item"> </el-option>
</el-select>
</el-form-item>
</div>
<gap-title :hasLine="true" title="预警工单推送"></gap-title>
<div class="add-form-item">
<el-form-item class="no-el-label" label="" prop="push_method">
<div style="width: 100%">
<ManualDistributionForm
ref="manual_distribution_form"
class="manual-distribution-form"
:noElLabel="false"
methodLabel="预警通知方式"
:history="history" />
</div>
</el-form-item>
<el-form-item label="消息推送次数">
<el-input style="flex: 1" v-model="state.form.push_num" placeholder="请输入消息推送次数" clearable>
<template #append></template>
</el-input>
</el-form-item>
<el-form-item label="消息推送频率">
<el-input style="flex: 1" v-model="state.form.push_frequency" placeholder="请输入消息推送频率" clearable>
<template #append>分钟</template>
</el-input>
</el-form-item>
<el-form-item label="是否立即启用" prop="">
<el-switch
v-model="state.form.enabled"
inline-prompt
active-text="是"
inactive-text="否"
:active-value="true"
:inactive-value="false">
</el-switch>
</el-form-item>
</div>
</el-form>
</div>
</template>
<script setup>
import { reactive, ref, shallowReactive, computed, nextTick, watch } from "vue";
import gapTitle from "@/components/gap-title.vue";
import ManualDistributionForm from "@/components/manual-distribution/form.vue";
import Static from "./static.vue";
import Custom from "./custom.vue";
import { MAX_DAY } from "@/components/env.js";
const props = defineProps({
row: {
type: Object,
default: null,
},
});
const isEdit = computed(() => !!props.row);
const history = computed(() => props.row?.manual_distribution_form || null);
const typrFormData = computed(() => props.row?.type_com_ref || null);
const manual_distribution_form = ref(null);
const typeCom = shallowReactive({
static: Static,
custom: Custom,
});
const unitOptions = {
s: { label: "", max: MAX_DAY * 24 * 3600 },
m: { label: "分钟", max: MAX_DAY * 24 * 60 },
h: { label: "小时", max: MAX_DAY * 24 },
};
const state = reactive({
form: {
name: "",
type_key: "static",
time: 10,
unit: "s",
inspection_cycle: 1,
push_num: 1,
push_frequency: 60,
enabled: true,
},
rules: {
name: [{ required: true, message: "请输入预警规则名称", trigger: "blur" }],
time: [{ required: true, message: "请输入持续时间", trigger: "blur" }],
},
});
const inputNum = () => {
state.form.time = state.form.time.replace(/[^\d]/g, "");
let time = +state.form.time;
let { max } = unitOptions[state.form.unit];
if (time > +max) {
state.form.time = max;
}
};
const types = {
static: "静态阈值",
custom: "自定义",
};
const changeType = async (key) => {
state.form.type_key = key;
form_ref.value.clearValidate();
};
const durationOptions = [
{
id: 1,
name: "直接产生预警",
},
{
id: 2,
name: "当预警持续",
},
];
const timeOptions = [10, 20, 60, 120, 180, 300];
const inspectionCycleOptions = ref([1, 3, 5, 10, 20]);
const form_ref = ref(null);
const type_com_ref = ref(null);
const Submit = async () => {
let form_valid = await new Promise((resolve, reject) => {
form_ref.value.validate((res) => resolve(res));
});
let type_com_ref_valid = await type_com_ref.value.Submit();
let manual_distribution_form_valid = await manual_distribution_form.value.Submit();
if (form_valid && type_com_ref_valid && manual_distribution_form_valid) {
return {
...state.form,
type_com_ref: type_com_ref.value?.form || {},
manual_distribution_form: manual_distribution_form.value?.form || {},
};
}
return;
};
watch(
() => props.row,
(n) => {
if (!n) return;
state.form.name = n.name;
state.form.type_key = n.type_key;
state.form.unit = n.unit;
state.form.time = n.time || 10;
state.form.inspection_cycle = n.inspection_cycle || 1;
state.form.push_num = n.push_num || 1;
state.form.push_frequency = n.push_frequency || 60;
state.form.enabled = n.enabled || false;
},
{ deep: true, immediate: true }
);
defineExpose({
Submit,
});
</script>
<style lang="scss" scoped>
.add-form {
:deep(.gap-title) {
margin-bottom: 16px;
}
&-item {
max-width: 1080px;
width: 100%;
padding-left: 8px;
.indicator-expression {
height: 300px;
width: 100%;
:deep(.vue-ace-editor) {
margin-top: 0;
}
}
.duration {
display: flex;
align-items: center;
gap: 8px;
:deep(.el-form-item__content) {
display: flex;
align-items: center;
gap: 8px;
> .el-input {
flex: 1;
width: 80px;
}
}
.no-el-label {
:deep(.el-form-item__content) {
.el-select {
width: 80px;
}
}
}
}
:deep(.el-switch__inner) {
.is-hide {
display: none;
}
}
:deep(.el-input-group__append, .el-input-group__prepend) {
border-radius: 4px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
}
}
.no-el-label {
:deep(.el-form-item__content) {
margin-left: 0 !important;
}
}
.manual-distribution-form {
:deep(.el-form-item) {
&:not(:last-child) {
margin-bottom: 18px;
}
}
}
</style>
<style lang="scss">
.el-form-item {
min-height: 36px;
label {
height: 36px;
line-height: 36px;
}
.el-input__inner {
min-height: 34px;
}
}
</style>
<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>
This diff is collapsed.
<template>
<div class="gateway-form">
<div class="gateway-form-item">
<el-form
:model="state.form.ruleRows"
ref="form_ref"
:rules="state.tableRules"
label-width="0"
style="width: 100%">
<el-table :data="state.form.ruleRows" stripe border>
<el-table-column
v-for="header in ruleHeaders"
:prop="header.prop"
:key="header.prop"
:label="header.label"
:width="header.width">
<template #default="{ $index }">
<div class="warning-threshold" v-if="header.prop == 'warning_threshold'">
<el-form-item :prop="`[${$index}].from`" :rules="state.tableRules.from" style="flex: 1">
<el-input
style="flex: 1"
v-model.number="state.form.ruleRows[$index].from"
placeholder="请输入"
@input="changeWarningThresholdFrom($index)">
<template v-if="state.form.rule_type != 'empty'" #append>ms</template>
</el-input>
</el-form-item>
<span class="to">-</span>
<el-form-item label="" :prop="`${$index}.to`" :rules="state.tableRules.to" style="flex: 1">
<el-input
style="flex: 1"
v-model.number="state.form.ruleRows[$index].to"
placeholder="请输入"
clearable
@input="changeWarningThresholdTo($index)">
<template v-if="state.form.rule_type != 'empty'" #append>ms</template>
</el-input>
</el-form-item>
</div>
<div v-else-if="header.prop == 'risk_level'">
<el-form-item label="" :prop="`[${$index}].risk_level`" :rules="state.tableRules.risk_level">
<el-select style="flex: 1" v-model="state.form.ruleRows[$index].risk_level" placeholder="请选择">
<el-option
v-for="item in riskLevelOptions($index)"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</div>
</template>
</el-table-column>
<el-table-column prop="" label="操作" width="150px">
<template #default="{ $index }">
<div class="table-operation">
<el-button
link
type="primary"
@click="createRule($index)"
:disabled="state.form.ruleRows.length >= riskLevels.length">
新增
</el-button>
<span class="line"></span>
<el-button link type="primary" @click="removeRule($index)" :disabled="state.form.ruleRows.length == 1">
删除
</el-button>
</div>
</template>
</el-table-column>
</el-table>
</el-form>
</div>
</div>
</template>
<script setup>
import { computed, reactive, ref, watch } from "vue";
import gapTitle from "@/components/gap-title.vue";
const props = defineProps({
form: {
type: Object,
default: null,
},
});
const state = reactive({
form: {
ruleRows: [],
},
tableRules: {
from: [{ required: true, message: "请输入", trigger: "blur" }],
to: [{ required: true, message: "请输入", trigger: "blur" }],
risk_level: [{ required: true, message: "请选择", trigger: "change" }],
},
});
const form_ref = ref(null);
const Submit = async () => {
let form_valid = await new Promise((resolve, reject) => {
form_ref.value.validate((res) => resolve(res));
});
return form_valid;
};
const riskLevels = ref([
{
id: 1,
name: "重大风险",
},
{
id: 2,
name: "较大风险",
},
{
id: 3,
name: "一般风险",
},
{
id: 4,
name: "低风险",
},
]);
const riskLevelOptions = computed(() => {
return (index) => {
let risk_level = state.form.ruleRows[index].risk_level;
let rows = state.form.ruleRows.map((e) => e.risk_level);
if (risk_level) {
let i = rows.findIndex((e) => e == risk_level);
rows.splice(i, 1);
}
return riskLevels.value.filter((e) => !rows.includes(e.id));
};
});
const ruleHeaders = [
{
prop: "warning_threshold",
label: "预警阈值",
width: "500px",
},
{
prop: "risk_level",
label: "风险程度",
},
];
const createRule = (index = -1) => {
state.form.ruleRows.splice(index + 1, 0, {
from: "",
to: "",
risk_level: "",
});
};
const setLimits = (index) => {
let rows = [...state.form.ruleRows];
rows.splice(index, 1);
return (
rows.map((e) => {
return {
down: +e.from,
up: +e.to,
};
}) || []
);
};
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) {
state.form.ruleRows[index].from = "";
return;
}
let rows = setLimits(index);
if (rows.length == 0) return;
try {
rows.forEach((e) => {
if (+e.up >= +from > +e.down) {
throw "";
}
});
} catch (e) {
state.form.ruleRows[index].from = "";
}
};
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) {
state.form.ruleRows[index].to = "";
return;
}
let rows = setLimits(index);
if (rows.length == 0) return;
try {
rows.forEach((e) => {
if (+e.up > +to > +e.down) {
throw "";
}
});
} catch (e) {
state.form.ruleRows[index].to = "";
}
};
const removeRule = (index) => {
state.form.ruleRows.splice(index, 1);
};
watch(
() => props.form,
(f) => {
if (!f?.ruleRows) {
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();
}
},
{
deep: true,
immediate: true,
}
);
defineExpose({
Submit,
form: state.form,
form_ref,
});
</script>
<style lang="scss" scoped>
.gateway-form {
margin-bottom: 16px;
:deep(.gap-title) {
margin-bottom: 16px;
}
&-item {
max-width: 1080px;
width: 100%;
padding-left: 110px;
:deep(.el-table thead th) {
background-color: #f5f6f9;
}
.indicator-expression {
height: 300px;
width: 100%;
:deep(.vue-ace-editor) {
margin-top: 0;
}
}
}
}
:deep(.el-form-item) {
margin-bottom: 0 !important;
}
.warning-threshold {
display: flex;
align-items: center;
:deep(.el-input-group__append, .el-input-group__prepend) {
border-radius: 4px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
}
.to {
margin: 0 16px;
}
.line {
width: 1px;
height: 14px;
background-color: #c1c7d7;
margin: 0 16px;
display: inline-block;
}
.rule-table {
:deep(.el-table thead th) {
background-color: #f5f6f9;
}
}
</style>
This diff is collapsed.
......@@ -63,6 +63,7 @@ const Distribute = async () => {
align-items: center;
justify-content: flex-end;
border-top: 1px solid #ddd;
padding: 0 24px;
}
}
}
......
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