Files
yudao-ui-admin-vue3/src/views/iot/rule/scene/form/RuleSceneForm.vue

385 lines
11 KiB
Vue
Raw Normal View History

<template>
<!-- 场景联动规则表单抽屉 - 优化高度和布局 -->
<el-drawer
v-model="drawerVisible"
:title="drawerTitle"
size="80%"
direction="rtl"
:close-on-click-modal="false"
:close-on-press-escape="false"
@close="handleClose"
>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="110px">
<!-- 基础信息配置 -->
<BasicInfoSection v-model="formData" :rules="formRules" />
<!-- 触发器配置 -->
<TriggerSection v-model:triggers="formData.triggers" @validate="handleTriggerValidate" />
<!-- 执行器配置 -->
<ActionSection v-model:actions="formData.actions" @validate="handleActionValidate" />
</el-form>
<template #footer>
<div class="drawer-footer">
<el-button :disabled="submitLoading" type="primary" @click="handleSubmit">
<Icon icon="ep:check" />
</el-button>
<el-button @click="handleClose">
<Icon icon="ep:close" />
</el-button>
</div>
</template>
</el-drawer>
</template>
<script setup lang="ts">
import { useVModel } from '@vueuse/core'
import BasicInfoSection from './sections/BasicInfoSection.vue'
import TriggerSection from './sections/TriggerSection.vue'
import ActionSection from './sections/ActionSection.vue'
import {
IotRuleScene,
IotRuleSceneActionTypeEnum,
RuleSceneFormData,
TriggerFormData
} from '@/api/iot/rule/scene/scene.types'
import { IotRuleSceneTriggerTypeEnum } from '@/views/iot/utils/constants'
import { ElMessage } from 'element-plus'
import { generateUUID } from '@/utils'
// 导入全局的 CommonStatusEnum
const CommonStatusEnum = {
ENABLE: 0, // 开启
DISABLE: 1 // 关闭
} as const
/** IoT 场景联动规则表单 - 主表单组件 */
defineOptions({ name: 'RuleSceneForm' })
/** 组件属性定义 */
const props = defineProps<{
/** 抽屉显示状态 */
modelValue: boolean
/** 编辑的规则数据(新增时为空) */
ruleScene?: IotRuleScene
}>()
/** 组件事件定义 */
const emit = defineEmits<{
/** 更新抽屉显示状态 */
'update:modelValue': [value: boolean]
/** 操作成功事件 */
success: []
}>()
const drawerVisible = useVModel(props, 'modelValue', emit) // 是否可见
/** 创建默认的表单数据 */
const createDefaultFormData = (): RuleSceneFormData => {
return {
name: '',
description: '',
status: CommonStatusEnum.ENABLE, // 默认启用状态
triggers: [
{
type: IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST,
productId: undefined,
deviceId: undefined,
identifier: undefined,
operator: undefined,
value: undefined,
cronExpression: undefined,
mainCondition: undefined,
conditionGroup: undefined
}
],
actions: []
}
}
/**
* 将表单数据转换为 API 请求格式
*/
const convertFormToVO = (formData: RuleSceneFormData): IotRuleScene => {
// 构建单个触发器的条件
const buildTriggerConditions = (trigger: TriggerFormData) => {
const conditions: any[] = []
// 处理主条件
if (trigger.mainCondition) {
const mainCondition = trigger.mainCondition
conditions.push({
type: mainCondition.type === 2 ? 'property' : 'event',
identifier: mainCondition.identifier || '',
parameters: [
{
operator: mainCondition.operator,
value: mainCondition.param
}
]
})
}
// 处理条件组
if (trigger.conditionGroup?.subGroups) {
trigger.conditionGroup.subGroups.forEach((subGroup) => {
subGroup.conditions.forEach((condition) => {
conditions.push({
type: condition.type === 2 ? 'property' : 'event',
identifier: condition.identifier || '',
parameters: [
{
operator: condition.operator,
value: condition.param
}
]
})
})
})
}
return conditions
}
return {
id: formData.id,
name: formData.name,
description: formData.description,
status: Number(formData.status),
triggers: formData.triggers.map((trigger) => ({
type: trigger.type,
productKey: trigger.productId ? `product_${trigger.productId}` : undefined,
deviceNames: trigger.deviceId ? [`device_${trigger.deviceId}`] : undefined,
cronExpression: trigger.cronExpression,
conditions: buildTriggerConditions(trigger)
})),
actions:
formData.actions?.map((action) => ({
type: action.type,
alertConfigId: action.alertConfigId,
deviceControl:
action.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET ||
action.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE
? {
productKey: action.productId ? `product_${action.productId}` : '',
deviceNames: action.deviceId ? [`device_${action.deviceId}`] : [],
type:
action.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET
? 'property'
: 'service',
identifier:
action.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET ? 'set' : 'invoke',
params: action.params || {}
}
: undefined
})) || []
} as IotRuleScene
}
/**
* API 响应数据转换为表单格式
*/
const convertVOToForm = (apiData: IotRuleScene): RuleSceneFormData => {
// 解析单个触发器的条件
const parseConditions = (trigger: any) => {
if (!trigger?.conditions?.length) {
return {
mainCondition: undefined,
conditionGroup: undefined
}
}
// 简化处理:将第一个条件作为主条件
const firstCondition = trigger.conditions[0]
const mainCondition = {
type: firstCondition.type === 'property' ? 2 : 3,
productId: undefined, // 需要从 productKey 解析
deviceId: undefined, // 需要从 deviceNames 解析
identifier: firstCondition.identifier,
operator: firstCondition.parameters?.[0]?.operator || '=',
param: firstCondition.parameters?.[0]?.value || ''
}
return {
mainCondition,
conditionGroup: undefined // 暂时简化处理
}
}
// 转换所有触发器
const triggers = apiData.triggers?.length
? apiData.triggers.map((trigger) => {
const conditionData = parseConditions(trigger)
return {
type: Number(trigger.type),
productId: undefined, // 需要从 productKey 解析
deviceId: undefined, // 需要从 deviceNames 解析
identifier: undefined,
operator: undefined,
value: undefined,
cronExpression: trigger.cronExpression,
...conditionData
}
})
: [
{
type: IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST,
productId: undefined,
deviceId: undefined,
identifier: undefined,
operator: undefined,
value: undefined,
cronExpression: undefined,
mainCondition: undefined,
conditionGroup: undefined
}
]
return {
...apiData,
status: Number(apiData.status),
triggers,
actions:
apiData.actions?.map((action) => ({
...action,
type: Number(action.type),
productId: undefined, // 需要从 deviceControl.productKey 解析
deviceId: undefined, // 需要从 deviceControl.deviceNames 解析
params: action.deviceControl?.params || {},
// 为每个执行器添加唯一标识符,解决组件索引重用问题
key: generateUUID()
})) || []
}
}
// 表单数据和状态
const formRef = ref()
const formData = ref<RuleSceneFormData>(createDefaultFormData())
const formRules = reactive({
name: [
{ required: true, message: '场景名称不能为空', trigger: 'blur' },
{ type: 'string', min: 1, max: 50, message: '场景名称长度应在1-50个字符之间', trigger: 'blur' }
],
status: [
{ required: true, message: '场景状态不能为空', trigger: 'change' },
{
type: 'enum',
enum: [CommonStatusEnum.ENABLE, CommonStatusEnum.DISABLE],
message: '状态值必须为启用或禁用',
trigger: 'change'
}
],
description: [
{ type: 'string', max: 200, message: '场景描述不能超过200个字符', trigger: 'blur' }
],
triggers: [
{ required: true, message: '触发器数组不能为空', trigger: 'change' },
{ type: 'array', min: 1, message: '至少需要一个触发器', trigger: 'change' }
],
actions: [
{ required: true, message: '执行器数组不能为空', trigger: 'change' },
{ type: 'array', min: 1, message: '至少需要一个执行器', trigger: 'change' }
]
})
const submitLoading = ref(false)
// 验证状态
const triggerValidation = ref({ valid: true, message: '' })
const actionValidation = ref({ valid: true, message: '' })
// 计算属性
const isEdit = computed(() => !!props.ruleScene?.id)
const drawerTitle = computed(() => (isEdit.value ? '编辑场景联动规则' : '新增场景联动规则'))
// 事件处理
const handleTriggerValidate = (result: { valid: boolean; message: string }) => {
triggerValidation.value = result
}
const handleActionValidate = (result: { valid: boolean; message: string }) => {
actionValidation.value = result
}
const handleSubmit = async () => {
// 校验表单
if (!formRef.value) return
const valid = await formRef.value.validate()
if (!valid) return
// 验证触发器和执行器
if (!triggerValidation.value.valid) {
ElMessage.error(triggerValidation.value.message)
return
}
if (!actionValidation.value.valid) {
ElMessage.error(actionValidation.value.message)
return
}
// 提交请求
submitLoading.value = true
try {
// 转换数据格式
const apiData = convertFormToVO(formData.value)
// 调用API保存数据
if (isEdit.value) {
// 更新场景联动规则
// await RuleSceneApi.updateRuleScene(apiData)
console.log('更新数据:', apiData)
} else {
// 创建场景联动规则
// await RuleSceneApi.createRuleScene(apiData)
console.log('创建数据:', apiData)
}
// 模拟API调用
await new Promise((resolve) => setTimeout(resolve, 1000))
ElMessage.success(isEdit.value ? '更新成功' : '创建成功')
drawerVisible.value = false
emit('success')
} catch (error) {
console.error('保存失败:', error)
ElMessage.error(isEdit.value ? '更新失败' : '创建失败')
} finally {
submitLoading.value = false
}
}
const handleClose = () => {
drawerVisible.value = false
}
/** 初始化表单数据 */
const initFormData = () => {
if (props.ruleScene) {
formData.value = convertVOToForm(props.ruleScene)
} else {
formData.value = createDefaultFormData()
}
}
// 监听抽屉显示
watch(drawerVisible, (visible) => {
if (visible) {
initFormData()
nextTick(() => {
formRef.value?.clearValidate()
})
}
})
// 监听 props 变化
watch(
() => props.ruleScene,
() => {
if (drawerVisible.value) {
initFormData()
}
}
)
</script>