feat: 告警配置

This commit is contained in:
leiqiaochu 2023-03-02 20:43:37 +08:00
parent 849bfbdaad
commit 6ba0f0cbb6
8 changed files with 477 additions and 45 deletions

View File

@ -34,4 +34,13 @@ export const save = (data:any) =>server.post('/alarm/config',data);
/**
*
*/
export const detail = (id:string) => server.get(`/alarm/config/${id}`);
export const detail = (id:string) => server.get(`/alarm/config/${id}`);
/**
*
*/
export const unbindScene = (id:string,data:any) => server.post(`/alarm/rule/bind/${id}/_delete`,data);
/**
*
*/
export const bindScene = (data:any) => server.patch("/alarm/rule/bind",data)

View File

@ -1,37 +1,47 @@
import { defineStore } from "pinia";
export const useAlarmStore = defineStore('alarm',()=>{
const data = reactive({
tab: 'all',
current: {},
solveVisible: false,
logVisible: false,
defaultLevel: [],
columns: [
{
dataIndex: 'alarmConfigName',
title: '告警名称',
// hideInSearch: true,
},
{
dataIndex: 'alarmTime',
title: '告警时间',
valueType: 'dateTime',
},
{
dataIndex: 'description',
title: '说明',
// hideInSearch: true,
},
{
dataIndex: 'action',
title: '操作',
hideInSearch: true,
valueType: 'option',
},
],
})
return {
data
export const useAlarmStore = defineStore('alarm', () => {
const data = reactive({
tab: 'all',
current: {},
solveVisible: false,
logVisible: false,
defaultLevel: [],
columns: [
{
dataIndex: 'alarmConfigName',
title: '告警名称',
// hideInSearch: true,
},
{
dataIndex: 'alarmTime',
title: '告警时间',
valueType: 'dateTime',
},
{
dataIndex: 'description',
title: '说明',
// hideInSearch: true,
},
{
dataIndex: 'action',
title: '操作',
hideInSearch: true,
valueType: 'option',
},
],
})
return {
data
}
})
export const useAlarmConfigurationStore = defineStore('alarmConfigration', () => {
const configurationData = reactive({
current:{
}
})
return { configurationData }
})

View File

@ -61,10 +61,13 @@ import { getImage } from '@/utils/comm';
import { message } from 'ant-design-vue';
import { useMenuStore } from '@/store/menu';
import { useRoute } from 'vue-router';
import { Store } from 'jetlinks-store';
import { useAlarmConfigurationStore } from '@/store/alarm';
import { storeToRefs } from 'pinia';
const route = useRoute();
const id = route.query?.id;
let selectDisable = ref(false);
const alarmConfigurationStore = useAlarmConfigurationStore();
let { configurationData } = storeToRefs(alarmConfigurationStore);
const queryData = () => {
if (id) {
detail(id).then((res) => {
@ -73,7 +76,7 @@ const queryData = () => {
form.name = res?.result?.name;
form.targetType = res?.result?.targetType;
form.description = res?.result?.description;
Store.set('configuration-data', res.result);
configurationData.value.current = res.result;
query({
terms: [
{
@ -183,7 +186,7 @@ const handleSave = async () => {
{ id: res.result?.id },
);
if (!id) {
Store.set('configuration-data', res.result);
configurationData.value.current = res.result;
}
}
})

View File

@ -0,0 +1,14 @@
<template>
<div>
<TabComponent type="detail" :id="id"/>
</div>
</template>
<script lang="ts" setup>
import TabComponent from '@/views/rule-engine/Alarm/Log/TabComponent/indev.vue'
import { useRoute } from 'vue-router';
const route = useRoute();
const id = route.query?.id
</script>
<style lang="less" scoped>
</style>

View File

@ -0,0 +1,233 @@
<template>
<a-modal
visible
title="新增"
okText="确定"
cancelText="取消"
:width="1000"
@cancel="closeModal"
@ok="saveCorrelation"
>
<Search :columns="columns" @search="handleSearch"></Search>
<div style="height: 500px; overflow-y: auto">
<JTable
model="CARD"
:request="query"
:rowSelection="{
selectedRowKeys: _selectedRowKeys,
}"
@cancelSelect="cancelSelect"
:gridColumns="[1, 1, 1]"
:defaultParams="{
sorts: [
{
name: 'createTime',
order: 'desc',
},
],
terms,
}"
:params="params"
>
<template #card="slotProps">
<SceneCard
:value="slotProps"
:status="slotProps.state?.value"
:statusText="slotProps.state?.text"
:active="_selectedRowKeys.includes(slotProps.id)"
@click="handleClick"
:statusNames="{
started: 'success',
disable: 'error',
}"
>
<template #type>
<span
><img
:height="16"
:src="
typeMap.get(slotProps.triggerType)?.icon
"
style="margin-right: 5px"
/>{{
typeMap.get(slotProps.triggerType)?.text
}}</span
>
</template>
<template #img>
<img
:src="typeMap.get(slotProps.triggerType)?.img"
/>
</template>
<template #title>
<Ellipsis style="width: calc(100% - 100px)">
<span style="font-size: 16px; font-weight: 600">
{{ slotProps.name }}
</span>
</Ellipsis>
</template>
<template #subTitle>
<Ellipsis :lineClamp="2">
说明{{
slotProps?.description ||
typeMap.get(slotProps.triggerType)?.tip
}}
</Ellipsis>
</template>
</SceneCard>
</template>
</JTable>
</div>
</a-modal>
</template>
<script lang="ts" setup>
import { query } from '@/api/rule-engine/scene';
import { bindScene } from '@/api/rule-engine/configuration';
import SceneCard from '@/views/rule-engine/Scene/SceneCard.vue';
import { getImage } from '@/utils/comm';
import { message } from 'ant-design-vue';
const columns = [
{
title: '名称',
dataIndex: 'name',
key: 'name',
search: {
type: 'string',
},
},
{
title: '触发方式',
dataIndex: 'triggerType',
key: 'triggerType',
search: {
type: 'select',
options: [
{
label: '手动触发',
value: 'manual',
},
{
label: '定时触发',
value: 'timer',
},
{
label: '设备触发',
value: 'device',
},
],
},
},
{
title: '状态',
dataIndex: 'state',
key: 'state',
search: {
type: 'select',
options: [
{
label: '正常',
value: 'started',
},
{
label: '禁用',
value: 'disable',
},
],
},
},
];
const props = defineProps({
id: {
type: String,
},
type: {
type: String,
},
});
const terms = [
{
terms: [
{
column: 'id',
termType: 'alarm-bind-rule$not',
value: props.id,
type: 'and',
},
{
column: 'triggerType',
termType: 'eq',
value: props.type === 'other' ? undefined : 'device',
},
],
type: 'and',
},
];
const params = ref();
const typeMap = new Map();
typeMap.set('manual', {
text: '手动触发',
img: getImage('/scene/scene-hand.png'),
icon: getImage('/scene/trigger-type-icon/manual.png'),
tip: '适用于第三方平台向物联网平台下发指令控制设备',
});
typeMap.set('timer', {
text: '定时触发',
img: getImage('/scene/scene-timer.png'),
icon: getImage('/scene/trigger-type-icon/timing.png'),
tip: '适用于定期执行固定任务',
});
typeMap.set('device', {
text: '设备触发',
img: getImage('/scene/scene-device.png'),
icon: getImage('/scene/trigger-type-icon/device.png'),
tip: '适用于设备数据或行为满足触发条件时,执行指定的动作',
});
const _selectedRowKeys = ref<string[]>([]);
const handleClick = (dt: any) => {
if (_selectedRowKeys.value.includes(dt.id)) {
const _index = _selectedRowKeys.value.findIndex((i) => i === dt.id);
_selectedRowKeys.value.splice(_index, 1);
} else {
_selectedRowKeys.value = [..._selectedRowKeys.value, dt.id];
}
console.log(_selectedRowKeys.value);
};
/**
* 取消选择事件
*/
const cancelSelect = () => {
_selectedRowKeys.value = [];
};
const log = () => {};
log();
const handleSearch = (e: any) => {
params.value = e;
};
const emit = defineEmits(['closeSave','saveScene']);
/**
* 保存选中关联场景
*/
const saveCorrelation = async () => {
if (_selectedRowKeys.value.length > 0) {
const list = _selectedRowKeys.value.map((item: any) => {
return {
alarmId: props.id,
releId: item,
};
});
const res = await bindScene([...list]);
if (res.status === 200) {
message.success('操作成功');
emit('saveScene');
}
} else {
message.error('请选择至少一条数据');
}
};
const closeModal = () => {
emit('closeSave');
};
</script>
<style lang="less" scoped>
</style>

View File

@ -1,9 +1,174 @@
<template>
<div>123</div>
<JTable
model="CARD"
:request="query"
:defaultParams="{
sorts: [{ name: 'createTime', order: 'desc' }],
terms,
}"
:gridColumns="[1, 1, 1]"
ref="actionRef"
>
<template #headerTitle>
<a-space>
<PermissionButton type="primary" @click="showModal">
<template #icon><AIcon type="PlusOutlined" /></template>
新增
</PermissionButton>
</a-space>
</template>
<template #card="slotProps">
<SceneCard
:value="slotProps"
@click="handleClick"
:actions="getActions(slotProps, 'card')"
:status="slotProps.state?.value"
:statusText="slotProps.state?.text"
:statusNames="{
started: 'success',
disable: 'error',
}"
>
<template #type>
<span
><img
:height="16"
:src="typeMap.get(slotProps.triggerType)?.icon"
style="margin-right: 5px"
/>{{ typeMap.get(slotProps.triggerType)?.text }}</span
>
</template>
<template #img>
<img :src="typeMap.get(slotProps.triggerType)?.img" />
</template>
<template #title>
<Ellipsis style="width: calc(100% - 100px)">
<span style="font-size: 16px; font-weight: 600">
{{ slotProps.name }}
</span>
</Ellipsis>
</template>
<template #subTitle>
<Ellipsis :lineClamp="2">
说明{{
slotProps?.description ||
typeMap.get(slotProps.triggerType)?.tip
}}
</Ellipsis>
</template>
<template #actions="item">
<PermissionButton
:disabled="item.disabled"
:popConfirm="item.popConfirm"
:tooltip="{
...item.tooltip,
}"
@click="item.onClick"
>
<AIcon :type="item.icon" />
<span>{{ item?.text }}</span>
</PermissionButton>
</template>
</SceneCard>
</template>
</JTable>
<Save
:id="id"
:type="configurationData.current?.targetType"
@close-save="closeSave"
@save-scene="saveSuccess"
v-if="visible"
></Save>
</template>
<script lang="ts" setup>
import { query } from '@/api/rule-engine/scene';
import { unbindScene } from '@/api/rule-engine/configuration';
import { useRoute } from 'vue-router';
import SceneCard from '@/views/rule-engine/Scene/SceneCard.vue';
import type { ActionsType } from '@/components/Table';
import { getImage } from '@/utils/comm';
import { message } from 'ant-design-vue/es';
import Save from './save/index.vue';
import { useAlarmConfigurationStore } from '@/store/alarm';
import { storeToRefs } from 'pinia';
const route = useRoute();
const id = route.query?.id;
const alarmConfigurationStore = useAlarmConfigurationStore();
const { configurationData } = storeToRefs(alarmConfigurationStore);
const terms = [
{
terms: [
{
column: 'id',
termType: 'alarm-bind-rule',
value: id,
},
],
type: 'and',
},
];
const actionRef = ref();
const typeMap = new Map();
typeMap.set('manual', {
text: '手动触发',
img: getImage('/scene/scene-hand.png'),
icon: getImage('/scene/trigger-type-icon/manual.png'),
tip: '适用于第三方平台向物联网平台下发指令控制设备',
});
typeMap.set('timer', {
text: '定时触发',
img: getImage('/scene/scene-timer.png'),
icon: getImage('/scene/trigger-type-icon/timing.png'),
tip: '适用于定期执行固定任务',
});
typeMap.set('device', {
text: '设备触发',
img: getImage('/scene/scene-device.png'),
icon: getImage('/scene/trigger-type-icon/device.png'),
tip: '适用于设备数据或行为满足触发条件时,执行指定的动作',
});
const getActions = (
data: Partial<Record<string, any>>,
type: 'card' | 'table',
): ActionsType[] => {
if (!data) return [];
const actions: ActionsType[] = [
{
key: 'unbind',
text: '解绑',
icon: 'DisconnectOutlined',
popConfirm: {
title: '确定解绑?',
onConfirm: async () => {
const res = await unbindScene(id, [data.id]);
if (res.status === 200) {
message.success('操作成功');
actionRef.value.reload();
}
},
},
},
];
return actions;
};
const visible = ref(false);
const log = () => {
console.log();
};
log();
const showModal = () => {
visible.value = true;
};
const closeSave = () => {
visible.value = false;
};
const saveSuccess = () =>{
visible.value = false;
actionRef.value.reload();
}
</script>
<style lang="less" scoped>
</style>

View File

@ -8,7 +8,9 @@
<a-tab-pane key="2" tab="关联场景联动">
<Scene></Scene>
</a-tab-pane>
<a-tab-pane key="3" tab="告警记录"></a-tab-pane>
<a-tab-pane key="3" tab="告警记录">
<Log/>
</a-tab-pane>
</a-tabs>
</a-card>
</page-container>
@ -17,7 +19,8 @@
<script lang="ts" setup>
import Base from './Base/index.vue';
import Scene from './Scene/index.vue'
const activeKey = ref('2');
import Log from './Log/indev.vue'
const activeKey = ref('1');
</script>
<style lang="less" scoped>
</style>

View File

@ -280,11 +280,6 @@ let params = ref({
sorts: [{ name: 'alarmTime', order: 'desc' }],
terms: [],
});
let param = reactive({
pageSize: 10,
terms: [],
});
const handleSearch = async (params: any) => {
const resp = await query(params);
if (resp.status === 200) {