feat: 优化 IoT 告警模板选择

- 后端 mail/sms/notify 模板 simple-list 仅返回启用模板精简字段
- 前端补充 mail/sms/notify 模板 simple-list API 封装
- vue3 与 vben antd/ele 在各自 system 模块封装模板选择组件
- IoT 告警配置按接收类型动态选择短信、邮件、站内信模板
- 补充前端 IotAlertReceiveTypeEnum,替换表单内裸常量
pull/880/MERGE
YunaiV 2026-05-30 22:06:01 +08:00
parent 9a4f9b6995
commit aff1439629
5 changed files with 229 additions and 67 deletions

View File

@ -79,61 +79,25 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="formData.receiveTypes?.includes(RECEIVE_TYPE_SMS)" v-if="formData.receiveTypes?.includes(IotAlertReceiveTypeEnum.SMS)"
label="短信模板" label="短信模板"
prop="smsTemplateCode" prop="smsTemplateCode"
> >
<el-select <SmsTemplateSelect v-model="formData.smsTemplateCode" />
v-model="formData.smsTemplateCode"
placeholder="请选择短信模板"
class="w-full"
filterable
>
<el-option
v-for="item in smsTemplateOptions"
:key="item.code"
:label="`${item.name}${item.code}`"
:value="item.code"
/>
</el-select>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="formData.receiveTypes?.includes(RECEIVE_TYPE_MAIL)" v-if="formData.receiveTypes?.includes(IotAlertReceiveTypeEnum.MAIL)"
label="邮件模板" label="邮件模板"
prop="mailTemplateCode" prop="mailTemplateCode"
> >
<el-select <MailTemplateSelect v-model="formData.mailTemplateCode" />
v-model="formData.mailTemplateCode"
placeholder="请选择邮件模板"
class="w-full"
filterable
>
<el-option
v-for="item in mailTemplateOptions"
:key="item.code"
:label="`${item.name}${item.code}`"
:value="item.code"
/>
</el-select>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="formData.receiveTypes?.includes(RECEIVE_TYPE_NOTIFY)" v-if="formData.receiveTypes?.includes(IotAlertReceiveTypeEnum.NOTIFY)"
label="站内信模板" label="站内信模板"
prop="notifyTemplateCode" prop="notifyTemplateCode"
> >
<el-select <NotifyTemplateSelect v-model="formData.notifyTemplateCode" />
v-model="formData.notifyTemplateCode"
placeholder="请选择站内信模板"
class="w-full"
filterable
>
<el-option
v-for="item in notifyTemplateOptions"
:key="item.code"
:label="`${item.name}${item.code}`"
:value="item.code"
/>
</el-select>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
@ -147,15 +111,11 @@ import { AlertConfigApi, AlertConfig } from '@/api/iot/alert/config'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { CommonStatusEnum } from '@/utils/constants' import { CommonStatusEnum } from '@/utils/constants'
import { RuleSceneApi } from '@/api/iot/rule/scene' import { RuleSceneApi } from '@/api/iot/rule/scene'
import { IotAlertReceiveTypeEnum } from '@/views/iot/utils/constants'
import * as UserApi from '@/api/system/user' import * as UserApi from '@/api/system/user'
import * as MailTemplateApi from '@/api/system/mail/template' import MailTemplateSelect from '@/views/system/mail/template/components/MailTemplateSelect.vue'
import * as SmsTemplateApi from '@/api/system/sms/smsTemplate' import NotifyTemplateSelect from '@/views/system/notify/template/components/NotifyTemplateSelect.vue'
import * as NotifyTemplateApi from '@/api/system/notify/template' import SmsTemplateSelect from '@/views/system/sms/template/components/SmsTemplateSelect.vue'
/** 告警接收类型,与 IotAlertReceiveTypeEnum 保持一致 */
const RECEIVE_TYPE_SMS = 1
const RECEIVE_TYPE_MAIL = 2
const RECEIVE_TYPE_NOTIFY = 3
/** IoT 告警配置 表单 */ /** IoT 告警配置 表单 */
defineOptions({ name: 'AlertConfigForm' }) defineOptions({ name: 'AlertConfigForm' })
@ -205,26 +165,23 @@ const formRef = ref() // 表单 Ref
// //
const sceneRuleOptions = ref<any[]>([]) const sceneRuleOptions = ref<any[]>([])
const userOptions = ref<UserApi.UserVO[]>([]) const userOptions = ref<UserApi.UserVO[]>([])
const smsTemplateOptions = ref<SmsTemplateApi.SmsTemplateSimpleVO[]>([])
const mailTemplateOptions = ref<MailTemplateApi.MailTemplateSimpleVO[]>([])
const notifyTemplateOptions = ref<NotifyTemplateApi.NotifyTemplateSimpleVO[]>([])
/** 按接收类型同步模板校验规则 */ /** 按接收类型同步模板校验规则 */
const syncTemplateFormRules = () => { const syncTemplateFormRules = () => {
const types = formData.value.receiveTypes || [] const types = formData.value.receiveTypes || []
if (types.includes(RECEIVE_TYPE_SMS)) { if (types.includes(IotAlertReceiveTypeEnum.SMS)) {
formRules.smsTemplateCode = [{ required: true, message: '短信模板不能为空', trigger: 'change' }] formRules.smsTemplateCode = [{ required: true, message: '短信模板不能为空', trigger: 'change' }]
} else { } else {
delete formRules.smsTemplateCode delete formRules.smsTemplateCode
} }
if (types.includes(RECEIVE_TYPE_MAIL)) { if (types.includes(IotAlertReceiveTypeEnum.MAIL)) {
formRules.mailTemplateCode = [ formRules.mailTemplateCode = [
{ required: true, message: '邮件模板不能为空', trigger: 'change' } { required: true, message: '邮件模板不能为空', trigger: 'change' }
] ]
} else { } else {
delete formRules.mailTemplateCode delete formRules.mailTemplateCode
} }
if (types.includes(RECEIVE_TYPE_NOTIFY)) { if (types.includes(IotAlertReceiveTypeEnum.NOTIFY)) {
formRules.notifyTemplateCode = [ formRules.notifyTemplateCode = [
{ required: true, message: '站内信模板不能为空', trigger: 'change' } { required: true, message: '站内信模板不能为空', trigger: 'change' }
] ]
@ -236,13 +193,13 @@ const syncTemplateFormRules = () => {
watch( watch(
() => formData.value.receiveTypes, () => formData.value.receiveTypes,
(types) => { (types) => {
if (!types?.includes(RECEIVE_TYPE_SMS)) { if (!types?.includes(IotAlertReceiveTypeEnum.SMS)) {
formData.value.smsTemplateCode = undefined formData.value.smsTemplateCode = undefined
} }
if (!types?.includes(RECEIVE_TYPE_MAIL)) { if (!types?.includes(IotAlertReceiveTypeEnum.MAIL)) {
formData.value.mailTemplateCode = undefined formData.value.mailTemplateCode = undefined
} }
if (!types?.includes(RECEIVE_TYPE_NOTIFY)) { if (!types?.includes(IotAlertReceiveTypeEnum.NOTIFY)) {
formData.value.notifyTemplateCode = undefined formData.value.notifyTemplateCode = undefined
} }
syncTemplateFormRules() syncTemplateFormRules()
@ -276,18 +233,12 @@ defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 加载选项数据 */ /** 加载选项数据 */
const loadOptions = async () => { const loadOptions = async () => {
try { try {
const [scenes, users, smsTemplates, mailTemplates, notifyTemplates] = await Promise.all([ const [scenes, users] = await Promise.all([
RuleSceneApi.getSimpleRuleSceneList(), RuleSceneApi.getSimpleRuleSceneList(),
UserApi.getSimpleUserList(), UserApi.getSimpleUserList()
SmsTemplateApi.getSimpleSmsTemplateList(),
MailTemplateApi.getSimpleMailTemplateList(),
NotifyTemplateApi.getSimpleNotifyTemplateList()
]) ])
sceneRuleOptions.value = scenes sceneRuleOptions.value = scenes
userOptions.value = users userOptions.value = users
smsTemplateOptions.value = smsTemplates
mailTemplateOptions.value = mailTemplates
notifyTemplateOptions.value = notifyTemplates
} catch (error) { } catch (error) {
console.error('加载选项数据失败:', error) console.error('加载选项数据失败:', error)
} }

View File

@ -25,6 +25,13 @@ export const IoTThingModelTypeEnum = {
EVENT: 3 // 事件 EVENT: 3 // 事件
} as const } as const
/** IoT 告警接收方式枚举,与后端 IotAlertReceiveTypeEnum 保持一致 */
export const IotAlertReceiveTypeEnum = {
SMS: 1, // 短信
MAIL: 2, // 邮箱
NOTIFY: 3 // 站内信
} as const
/** IoT 设备消息的方法枚举 */ /** IoT 设备消息的方法枚举 */
export const IotDeviceMessageMethodEnum = { export const IotDeviceMessageMethodEnum = {
// ========== 设备状态 ========== // ========== 设备状态 ==========

View File

@ -0,0 +1,68 @@
<template>
<el-select
:model-value="modelValue"
:placeholder="placeholder"
:disabled="disabled"
:loading="loading"
class="w-full"
filterable
clearable
@update:model-value="handleChange"
>
<el-option
v-for="template in templateList"
:key="template.id"
:label="`${template.name}${template.code}`"
:value="template.code"
/>
</el-select>
</template>
<script setup lang="ts">
import {
getSimpleMailTemplateList,
type MailTemplateSimpleVO
} from '@/api/system/mail/template'
/** 邮件模板下拉选择器 */
defineOptions({ name: 'MailTemplateSelect' })
withDefaults(
defineProps<{
disabled?: boolean
modelValue?: string
placeholder?: string
}>(),
{
disabled: false,
modelValue: undefined,
placeholder: '请选择邮件模板'
}
)
const emit = defineEmits<{
(e: 'update:modelValue', value?: string): void
(e: 'change', value?: string): void
}>()
const loading = ref(false)
const templateList = ref<MailTemplateSimpleVO[]>([])
const handleChange = (value?: string) => {
emit('update:modelValue', value)
emit('change', value)
}
const getTemplateList = async () => {
try {
loading.value = true
templateList.value = (await getSimpleMailTemplateList()) || []
} finally {
loading.value = false
}
}
onMounted(() => {
getTemplateList()
})
</script>

View File

@ -0,0 +1,68 @@
<template>
<el-select
:model-value="modelValue"
:placeholder="placeholder"
:disabled="disabled"
:loading="loading"
class="w-full"
filterable
clearable
@update:model-value="handleChange"
>
<el-option
v-for="template in templateList"
:key="template.id"
:label="`${template.name}${template.code}`"
:value="template.code"
/>
</el-select>
</template>
<script setup lang="ts">
import {
getSimpleNotifyTemplateList,
type NotifyTemplateSimpleVO
} from '@/api/system/notify/template'
/** 站内信模板下拉选择器 */
defineOptions({ name: 'NotifyTemplateSelect' })
withDefaults(
defineProps<{
disabled?: boolean
modelValue?: string
placeholder?: string
}>(),
{
disabled: false,
modelValue: undefined,
placeholder: '请选择站内信模板'
}
)
const emit = defineEmits<{
(e: 'update:modelValue', value?: string): void
(e: 'change', value?: string): void
}>()
const loading = ref(false)
const templateList = ref<NotifyTemplateSimpleVO[]>([])
const handleChange = (value?: string) => {
emit('update:modelValue', value)
emit('change', value)
}
const getTemplateList = async () => {
try {
loading.value = true
templateList.value = (await getSimpleNotifyTemplateList()) || []
} finally {
loading.value = false
}
}
onMounted(() => {
getTemplateList()
})
</script>

View File

@ -0,0 +1,68 @@
<template>
<el-select
:model-value="modelValue"
:placeholder="placeholder"
:disabled="disabled"
:loading="loading"
class="w-full"
filterable
clearable
@update:model-value="handleChange"
>
<el-option
v-for="template in templateList"
:key="template.id"
:label="`${template.name}${template.code}`"
:value="template.code"
/>
</el-select>
</template>
<script setup lang="ts">
import {
getSimpleSmsTemplateList,
type SmsTemplateSimpleVO
} from '@/api/system/sms/smsTemplate'
/** 短信模板下拉选择器 */
defineOptions({ name: 'SmsTemplateSelect' })
withDefaults(
defineProps<{
disabled?: boolean
modelValue?: string
placeholder?: string
}>(),
{
disabled: false,
modelValue: undefined,
placeholder: '请选择短信模板'
}
)
const emit = defineEmits<{
(e: 'update:modelValue', value?: string): void
(e: 'change', value?: string): void
}>()
const loading = ref(false)
const templateList = ref<SmsTemplateSimpleVO[]>([])
const handleChange = (value?: string) => {
emit('update:modelValue', value)
emit('change', value)
}
const getTemplateList = async () => {
try {
loading.value = true
templateList.value = (await getSimpleSmsTemplateList()) || []
} finally {
loading.value = false
}
}
onMounted(() => {
getTemplateList()
})
</script>