admin-vue3/src/views/iot/rule/scene/form/configs/TimerConditionGroupConfig.vue

203 lines
6.4 KiB
Vue

<!-- 定时触发器条件组配置组件 -->
<template>
<div class="space-y-16px">
<!-- 条件组容器头部 -->
<div
class="flex items-center justify-between p-16px bg-gradient-to-r from-blue-50 to-cyan-50 border border-blue-200 rounded-8px"
>
<div class="flex items-center gap-12px">
<div class="flex items-center gap-8px text-16px font-600 text-blue-700">
<div
class="w-24px h-24px bg-blue-500 text-white rounded-full flex items-center justify-center text-12px font-bold"
>
</div>
<span>附加条件组</span>
</div>
<el-tag size="small" type="info">定时触发时需满足以下条件</el-tag>
<el-tag size="small" type="warning"> {{ conditionGroups?.length || 0 }} 个子条件组 </el-tag>
</div>
<el-button
type="primary"
size="small"
@click="addConditionGroup"
:disabled="(conditionGroups?.length || 0) >= maxGroups"
>
<Icon icon="ep:plus" />
添加条件组
</el-button>
</div>
<!-- 条件组列表 -->
<div v-if="conditionGroups && conditionGroups.length > 0" class="space-y-16px">
<div
v-for="(group, groupIndex) in conditionGroups"
:key="`group-${groupIndex}`"
class="relative"
>
<!-- 条件组容器 -->
<div
class="border-2 border-orange-200 rounded-8px bg-orange-50 shadow-sm hover:shadow-md transition-shadow"
>
<div
class="flex items-center justify-between p-16px bg-gradient-to-r from-orange-50 to-yellow-50 border-b border-orange-200 rounded-t-6px"
>
<div class="flex items-center gap-12px">
<div class="flex items-center gap-8px text-16px font-600 text-orange-700">
<div
class="w-24px h-24px bg-orange-500 text-white rounded-full flex items-center justify-center text-12px font-bold"
>
{{ groupIndex + 1 }}
</div>
<span>子条件组 {{ groupIndex + 1 }}</span>
</div>
<el-tag size="small" type="warning" class="font-500">组内条件为"且"关系</el-tag>
<el-tag size="small" type="info"> {{ group?.length || 0 }}个条件 </el-tag>
</div>
<el-button
type="danger"
size="small"
text
@click="removeConditionGroup(groupIndex)"
class="hover:bg-red-50"
>
<Icon icon="ep:delete" />
删除组
</el-button>
</div>
<SubConditionGroupConfig
:ref="(el) => setSubGroupRef(el, groupIndex)"
:model-value="group"
@update:model-value="(value) => updateConditionGroup(groupIndex, value)"
:trigger-type="IotRuleSceneTriggerTypeEnum.TIMER"
:max-conditions="maxConditionsPerGroup"
/>
</div>
<!-- 条件组间的"或"连接符 -->
<div
v-if="groupIndex < conditionGroups.length - 1"
class="flex items-center justify-center py-12px"
>
<div class="flex items-center gap-8px">
<!-- 连接线 -->
<div class="w-32px h-1px bg-orange-300"></div>
<!-- 或标签 -->
<div class="px-16px py-6px bg-orange-100 border-2 border-orange-300 rounded-full">
<span class="text-14px font-600 text-orange-600">或</span>
</div>
<!-- 连接线 -->
<div class="w-32px h-1px bg-orange-300"></div>
</div>
</div>
</div>
</div>
<!-- 空状态 -->
<div
v-else
class="p-24px border-2 border-dashed border-blue-200 rounded-8px text-center bg-blue-50"
>
<div class="flex flex-col items-center gap-12px">
<Icon icon="ep:plus" class="text-32px text-blue-400" />
<div class="text-blue-600">
<p class="text-14px font-500 mb-4px">暂无附加条件</p>
<p class="text-12px">定时触发时将直接执行动作</p>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useVModel } from '@vueuse/core'
import SubConditionGroupConfig from './SubConditionGroupConfig.vue'
import type { TriggerCondition } from '@/api/iot/rule/scene'
import { IotRuleSceneTriggerTypeEnum } from '@/views/iot/utils/constants'
/** */
defineOptions({ name: 'TimerConditionGroupConfig' })
const props = defineProps<{
modelValue?: TriggerCondition[][]
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: TriggerCondition[][]): void
}>()
const conditionGroups = useVModel(props, 'modelValue', emit)
const maxGroups = 3 // 最多 3 个条件组
const maxConditionsPerGroup = 3 // 每组最多 3 个条件
type SubConditionGroupExpose = {
validate: () => Promise<boolean>
clearValidate: () => void
}
const subGroupRefs = ref<Record<number, SubConditionGroupExpose>>({})
const setSubGroupRef = (el: unknown, index: number) => {
if (el) {
subGroupRefs.value[index] = el as SubConditionGroupExpose
} else {
delete subGroupRefs.value[index]
}
}
/** 校验所有附加条件组 */
const validate = async (): Promise<boolean> => {
if (!conditionGroups.value?.length) {
return true
}
for (let i = 0; i < conditionGroups.value.length; i++) {
const subGroupRef = subGroupRefs.value[i]
if (subGroupRef?.validate) {
const valid = await subGroupRef.validate()
if (!valid) {
return false
}
}
}
return true
}
const clearValidate = () => {
Object.values(subGroupRefs.value).forEach((ref) => ref.clearValidate?.())
}
defineExpose({ validate, clearValidate })
/** 添加条件组 */
const addConditionGroup = async () => {
if (!conditionGroups.value) {
conditionGroups.value = []
}
// 检查是否达到最大条件组数量限制
if (conditionGroups.value.length >= maxGroups) {
return
}
// 使用 nextTick 确保响应式更新完成后再添加新的条件组
await nextTick()
if (conditionGroups.value) {
conditionGroups.value.push([])
}
}
/** 移除条件组 */
const removeConditionGroup = (index: number) => {
if (conditionGroups.value) {
conditionGroups.value.splice(index, 1)
}
}
/** 更新条件组 */
const updateConditionGroup = (index: number, group: TriggerCondition[]) => {
if (conditionGroups.value) {
conditionGroups.value[index] = group
}
}
</script>