perf:【IoT 物联网】场景联动优化统一类型定义,简化告警配置

This commit is contained in:
puhui999
2025-08-05 11:59:46 +08:00
parent 694de3f0d1
commit 6c954c4ff1
11 changed files with 134 additions and 423 deletions

View File

@ -1,262 +1,81 @@
<!-- 告警配置组件 -->
<template>
<div class="w-full">
<!-- 告警配置选择区域 -->
<div
class="border border-[var(--el-border-color-light)] rounded-6px p-16px bg-[var(--el-fill-color-blank)]"
>
<div class="flex items-center gap-8px mb-12px">
<Icon icon="ep:bell" class="text-[var(--el-color-warning)] text-16px" />
<span class="text-14px font-600 text-[var(--el-text-color-primary)]">告警配置选择</span>
<el-tag size="small" type="warning">必选</el-tag>
</div>
<el-form-item label="告警配置" required>
<el-select
v-model="localValue"
placeholder="请选择告警配置"
filterable
clearable
@change="handleChange"
class="w-full"
:loading="loading"
<el-form-item label="告警配置" required>
<el-select
v-model="localValue"
placeholder="请选择告警配置"
filterable
clearable
@change="handleChange"
class="w-full"
:loading="loading"
>
<el-option
v-for="config in alertConfigs"
:key="config.id"
:label="config.name"
:value="config.id"
>
<template #empty>
<div class="text-center py-20px">
<Icon
icon="ep:warning"
class="text-24px text-[var(--el-text-color-placeholder)] mb-8px"
/>
<p class="text-12px text-[var(--el-text-color-secondary)]">暂无可用的告警配置</p>
</div>
</template>
<el-option
v-for="config in alertConfigs"
:key="config.id"
:label="config.name"
:value="config.id"
:disabled="!config.enabled"
>
<div class="flex items-center justify-between w-full py-6px">
<div class="flex items-center gap-12px flex-1">
<Icon
:icon="config.enabled ? 'ep:circle-check' : 'ep:circle-close'"
:class="
config.enabled
? 'text-[var(--el-color-success)]'
: 'text-[var(--el-color-danger)]'
"
class="text-16px flex-shrink-0"
/>
<div class="flex-1">
<div class="text-14px font-500 text-[var(--el-text-color-primary)] mb-2px">{{
config.name
}}</div>
<div class="text-12px text-[var(--el-text-color-secondary)] line-clamp-1">{{
config.description
}}</div>
</div>
</div>
<div class="flex items-center gap-8px">
<el-tag :type="getNotifyTypeTag(config.notifyType)" size="small">
{{ getNotifyTypeName(config.notifyType) }}
</el-tag>
<el-tag :type="config.enabled ? 'success' : 'danger'" size="small">
{{ config.enabled ? '启用' : '禁用' }}
</el-tag>
</div>
</div>
</el-option>
</el-select>
</el-form-item>
</div>
<!-- 告警配置详情 -->
<div
v-if="selectedConfig"
class="mt-16px border border-[var(--el-border-color-light)] rounded-6px p-16px bg-gradient-to-r from-orange-50 to-yellow-50"
>
<div class="flex items-center gap-8px mb-16px">
<Icon icon="ep:info-filled" class="text-[var(--el-color-warning)] text-18px" />
<span class="text-16px font-600 text-[var(--el-text-color-primary)]">配置详情</span>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-16px">
<!-- 基本信息 -->
<div class="space-y-12px">
<div class="flex items-center gap-8px">
<Icon icon="ep:document" class="text-[var(--el-color-primary)] text-14px" />
<span class="text-14px font-500 text-[var(--el-text-color-primary)]">基本信息</span>
<div class="flex items-center justify-between">
<span>{{ config.name }}</span>
<el-tag :type="config.enabled ? 'success' : 'danger'" size="small">
{{ config.enabled ? '启用' : '禁用' }}
</el-tag>
</div>
<div class="pl-22px space-y-8px">
<div class="flex items-start gap-8px">
<span class="text-12px text-[var(--el-text-color-secondary)] min-w-60px">名称</span>
<span class="text-12px text-[var(--el-text-color-primary)] flex-1 font-500">{{
selectedConfig.name
}}</span>
</div>
<div class="flex items-start gap-8px">
<span class="text-12px text-[var(--el-text-color-secondary)] min-w-60px">描述</span>
<span class="text-12px text-[var(--el-text-color-primary)] flex-1">{{
selectedConfig.description
}}</span>
</div>
<div class="flex items-start gap-8px">
<span class="text-12px text-[var(--el-text-color-secondary)] min-w-60px">状态</span>
<el-tag :type="selectedConfig.enabled ? 'success' : 'danger'" size="small">
{{ selectedConfig.enabled ? '启用' : '禁用' }}
</el-tag>
</div>
</div>
</div>
<!-- 通知配置 -->
<div class="space-y-12px">
<div class="flex items-center gap-8px">
<Icon icon="ep:message" class="text-[var(--el-color-success)] text-14px" />
<span class="text-14px font-500 text-[var(--el-text-color-primary)]">通知配置</span>
</div>
<div class="pl-22px space-y-8px">
<div class="flex items-start gap-8px">
<span class="text-12px text-[var(--el-text-color-secondary)] min-w-60px">方式</span>
<el-tag :type="getNotifyTypeTag(selectedConfig.notifyType)" size="small">
{{ getNotifyTypeName(selectedConfig.notifyType) }}
</el-tag>
</div>
<div
v-if="selectedConfig.receivers && selectedConfig.receivers.length > 0"
class="flex items-start gap-8px"
>
<span class="text-12px text-[var(--el-text-color-secondary)] min-w-60px"
>接收人</span
>
<div class="flex-1">
<div class="flex flex-wrap gap-4px">
<el-tag
v-for="receiver in selectedConfig.receivers.slice(0, 3)"
:key="receiver"
size="small"
type="info"
>
{{ receiver }}
</el-tag>
<el-tag v-if="selectedConfig.receivers.length > 3" size="small" type="info">
+{{ selectedConfig.receivers.length - 3 }}
</el-tag>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</el-option>
</el-select>
</el-form-item>
</div>
</template>
<script setup lang="ts">
import { useVModel } from '@vueuse/core'
import { AlertConfigApi } from '@/api/iot/alert/config'
/** 告警配置组件 */
defineOptions({ name: 'AlertConfig' })
interface Props {
const props = defineProps<{
modelValue?: number
}
}>()
interface Emits {
const emit = defineEmits<{
(e: 'update:modelValue', value?: number): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
}>()
const localValue = useVModel(props, 'modelValue', emit)
// 状态
const loading = ref(false)
const alertConfigs = ref<any[]>([])
const loading = ref(false) // 加载状态
const alertConfigs = ref<any[]>([]) // 告警配置列表
// 计算属性
const selectedConfig = computed(() => {
return alertConfigs.value.find((config) => config.id === localValue.value)
})
// 工具函数
const getNotifyTypeName = (type: number) => {
const typeMap = {
1: '邮件通知',
2: '短信通知',
3: '微信通知',
4: '钉钉通知'
}
return typeMap[type] || '未知'
}
const getNotifyTypeTag = (type: number) => {
const tagMap = {
1: 'primary', // 邮件
2: 'success', // 短信
3: 'warning', // 微信
4: 'info' // 钉钉
}
return tagMap[type] || 'info'
}
// 事件处理
/**
* 处理选择变化事件
* @param value 选中的值
*/
const handleChange = (value?: number) => {
// 可以在这里添加额外的处理逻辑
console.log('告警配置选择变化:', value)
emit('update:modelValue', value)
}
// API 调用
const getAlertConfigs = async () => {
/**
* 加载告警配置列表
*/
const loadAlertConfigs = async () => {
loading.value = true
try {
// 这里应该调用真实的API获取告警配置
// 暂时使用模拟数据
// TODO @puhui999这里是模拟数据
alertConfigs.value = [
{
id: 1,
name: '设备异常告警',
description: '设备状态异常时发送告警',
enabled: true,
notifyType: 1,
receivers: ['admin@example.com', 'operator@example.com']
},
{
id: 2,
name: '温度超限告警',
description: '温度超过阈值时发送告警',
enabled: true,
notifyType: 2,
receivers: ['13800138000', '13900139000']
},
{
id: 3,
name: '系统故障告警',
description: '系统发生故障时发送告警',
enabled: false,
notifyType: 3,
receivers: ['技术支持群']
}
]
} catch (error) {
console.error('获取告警配置失败:', error)
const data = await AlertConfigApi.getAlertConfigPage({
pageNo: 1,
pageSize: 100,
enabled: true // 只加载启用的配置
})
alertConfigs.value = data.list || []
} finally {
loading.value = false
}
}
// 初始化
// 组件挂载时加载数据
onMounted(() => {
getAlertConfigs()
loadAlertConfigs()
})
</script>
<style scoped>
:deep(.el-select-dropdown__item) {
height: auto;
padding: 8px 20px;
}
</style>

View File

@ -123,7 +123,7 @@ import DeviceSelector from '../selectors/DeviceSelector.vue'
import PropertySelector from '../selectors/PropertySelector.vue'
import OperatorSelector from '../selectors/OperatorSelector.vue'
import ValueInput from '../inputs/ValueInput.vue'
import { TriggerConditionFormData } from '@/api/iot/rule/scene/scene.types'
import { TriggerCondition } from '@/api/iot/rule/scene/scene.types'
import {
IotRuleSceneTriggerConditionTypeEnum,
IotRuleSceneTriggerConditionParameterOperatorEnum
@ -133,12 +133,12 @@ import {
defineOptions({ name: 'ConditionConfig' })
const props = defineProps<{
modelValue: TriggerConditionFormData
modelValue: TriggerCondition
triggerType: number
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: TriggerConditionFormData): void
(e: 'update:modelValue', value: TriggerCondition): void
(e: 'validate', result: { valid: boolean; message: string }): void
}>()
@ -155,12 +155,12 @@ const isValid = ref(true)
const valueValidation = ref({ valid: true, message: '' })
// 事件处理
const updateConditionField = (field: keyof TriggerConditionFormData, value: any) => {
const updateConditionField = (field: keyof TriggerCondition, value: any) => {
;(condition.value as any)[field] = value
emit('update:modelValue', condition.value)
}
const updateCondition = (newCondition: TriggerConditionFormData) => {
const updateCondition = (newCondition: TriggerCondition) => {
condition.value = newCondition
emit('update:modelValue', condition.value)
}

View File

@ -320,18 +320,18 @@ import { useVModel } from '@vueuse/core'
import { InfoFilled } from '@element-plus/icons-vue'
import ProductSelector from '../selectors/ProductSelector.vue'
import DeviceSelector from '../selectors/DeviceSelector.vue'
import { ActionFormData, ThingModelService } from '@/api/iot/rule/scene/scene.types'
import { Action, ThingModelService } from '@/api/iot/rule/scene/scene.types'
import { IotRuleSceneActionTypeEnum } from '@/views/iot/utils/constants'
/** 设备控制配置组件 */
defineOptions({ name: 'DeviceControlConfig' })
const props = defineProps<{
modelValue: ActionFormData
modelValue: Action
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: ActionFormData): void
(e: 'update:modelValue', value: Action): void
}>()
const action = useVModel(props, 'modelValue', emit)

View File

@ -84,17 +84,17 @@
import { useVModel } from '@vueuse/core'
import ProductSelector from '../selectors/ProductSelector.vue'
import DeviceSelector from '../selectors/DeviceSelector.vue'
import { TriggerConditionFormData } from '@/api/iot/rule/scene/scene.types'
import { TriggerCondition } from '@/api/iot/rule/scene/scene.types'
/** 设备状态条件配置组件 */
defineOptions({ name: 'DeviceStatusConditionConfig' })
const props = defineProps<{
modelValue: TriggerConditionFormData
modelValue: TriggerCondition
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: TriggerConditionFormData): void
(e: 'update:modelValue', value: TriggerCondition): void
(e: 'validate', result: { valid: boolean; message: string }): void
}>()

View File

@ -83,7 +83,7 @@
import { nextTick } from 'vue'
import { useVModel } from '@vueuse/core'
import ConditionConfig from './ConditionConfig.vue'
import { TriggerConditionFormData } from '@/api/iot/rule/scene/scene.types'
import { TriggerCondition } from '@/api/iot/rule/scene/scene.types'
import {
IotRuleSceneTriggerConditionTypeEnum,
IotRuleSceneTriggerConditionParameterOperatorEnum
@ -93,13 +93,13 @@ import {
defineOptions({ name: 'SubConditionGroupConfig' })
const props = defineProps<{
modelValue: TriggerConditionFormData[]
modelValue: TriggerCondition[]
triggerType: number
maxConditions?: number
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: TriggerConditionFormData[]): void
(e: 'update:modelValue', value: TriggerCondition[]): void
(e: 'validate', result: { valid: boolean; message: string }): void
}>()
@ -123,7 +123,7 @@ const addCondition = () => {
return
}
const newCondition: TriggerConditionFormData = {
const newCondition: TriggerCondition = {
type: IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY, // 默认为设备属性
productId: undefined,
deviceId: undefined,
@ -161,7 +161,7 @@ const removeCondition = (index: number) => {
}
}
const updateCondition = (index: number, condition: TriggerConditionFormData) => {
const updateCondition = (index: number, condition: TriggerCondition) => {
if (subGroup.value) {
subGroup.value[index] = condition
}