refactor: 收敛 iot rule/scene API 的重复类型声明(P1)

- antd / ele api/iot/rule/scene,删除外层重复的 4 个 interface
- createSceneRule / updateSceneRule 入参改用 RuleSceneApi.SceneRule
- 业务文件 import 统一改用 RuleSceneApi.SceneRule / Trigger / TriggerCondition / Action
- 清理 2 处 TODO @haohao 残留注释

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pull/345/head
YunaiV 2026-05-21 11:00:30 +08:00
parent df720b2c1a
commit 057ca0bfde
22 changed files with 183 additions and 292 deletions

View File

@ -50,53 +50,6 @@ export namespace RuleSceneApi {
} }
} }
// TODO @haohao貌似下面的和 RuleSceneApi 重复了。
/** IoT 场景联动规则 */
export interface IotSceneRule {
id?: number;
name?: string;
description?: string;
status?: number;
triggers?: Trigger[];
actions?: Action[];
createTime?: Date;
}
/** IoT 场景联动规则触发器 */
export interface Trigger {
type?: number;
productId?: number;
deviceId?: number;
identifier?: string;
operator?: string;
value?: any;
cronExpression?: string;
// 后端结构List<List<TriggerCondition>>;外层「或」、组内「且」
conditionGroups?: TriggerCondition[][];
}
/** IoT 场景联动规则触发条件 */
export interface TriggerCondition {
productId?: number;
deviceId?: number;
identifier?: string;
operator?: string;
value?: any;
type?: number;
param?: string;
}
/** IoT 场景联动规则动作 */
export interface Action {
type?: number;
productId?: number;
deviceId?: number;
identifier?: string;
value?: any;
alertConfigId?: number;
params?: Record<string, any>;
}
/** 查询场景联动规则分页 */ /** 查询场景联动规则分页 */
export function getSceneRulePage(params: PageParam) { export function getSceneRulePage(params: PageParam) {
return requestClient.get<PageResult<RuleSceneApi.SceneRule>>( return requestClient.get<PageResult<RuleSceneApi.SceneRule>>(
@ -113,12 +66,12 @@ export function getSceneRule(id: number) {
} }
/** 新增场景联动规则 */ /** 新增场景联动规则 */
export function createSceneRule(data: IotSceneRule) { export function createSceneRule(data: RuleSceneApi.SceneRule) {
return requestClient.post('/iot/scene-rule/create', data); return requestClient.post('/iot/scene-rule/create', data);
} }
/** 修改场景联动规则 */ /** 修改场景联动规则 */
export function updateSceneRule(data: IotSceneRule) { export function updateSceneRule(data: RuleSceneApi.SceneRule) {
return requestClient.put('/iot/scene-rule/update', data); return requestClient.put('/iot/scene-rule/update', data);
} }
@ -128,7 +81,6 @@ export function deleteSceneRule(id: number) {
} }
/** 批量删除场景联动规则 */ /** 批量删除场景联动规则 */
// TODO @haohao貌似用上。
export function deleteSceneRuleList(ids: number[]) { export function deleteSceneRuleList(ids: number[]) {
return requestClient.delete('/iot/scene-rule/delete-list', { return requestClient.delete('/iot/scene-rule/delete-list', {
params: { ids: ids.join(',') }, params: { ids: ids.join(',') },

View File

@ -1,6 +1,6 @@
<!-- 单个条件配置组件 --> <!-- 单个条件配置组件 -->
<script setup lang="ts"> <script setup lang="ts">
import type { TriggerCondition } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
@ -25,12 +25,12 @@ import CurrentTimeConditionConfig from './current-time-condition-config.vue';
defineOptions({ name: 'ConditionConfig' }); defineOptions({ name: 'ConditionConfig' });
const props = defineProps<{ const props = defineProps<{
modelValue: TriggerCondition; modelValue: RuleSceneApi.TriggerCondition;
triggerType: number; triggerType: number;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: TriggerCondition): void; (e: 'update:modelValue', value: RuleSceneApi.TriggerCondition): void;
}>(); }>();
/** 获取设备状态选项 */ /** 获取设备状态选项 */
@ -84,7 +84,7 @@ function updateConditionField(field: any, value: any) {
* 更新整个条件对象 * 更新整个条件对象
* @param newCondition 新的条件对象 * @param newCondition 新的条件对象
*/ */
function updateCondition(newCondition: TriggerCondition) { function updateCondition(newCondition: RuleSceneApi.TriggerCondition) {
condition.value = newCondition; condition.value = newCondition;
emit('update:modelValue', condition.value); emit('update:modelValue', condition.value);
} }
@ -163,10 +163,12 @@ function handleOperatorChange() {
<Form.Item label="条件类型" required> <Form.Item label="条件类型" required>
<Select <Select
:value="condition.type" :value="condition.type"
@change="(value: any) => { @change="
(value: any) => {
updateConditionField('type', value); updateConditionField('type', value);
handleConditionTypeChange(value); handleConditionTypeChange(value);
}" }
"
placeholder="请选择条件类型" placeholder="请选择条件类型"
class="w-full" class="w-full"
> >
@ -212,8 +214,7 @@ function handleOperatorChange() {
<!-- 设备状态条件配置 --> <!-- 设备状态条件配置 -->
<div <div
v-if=" v-if="
condition.type === condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS
IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS
" "
class="gap-[16px] flex flex-col" class="gap-[16px] flex flex-col"
> >
@ -224,10 +225,7 @@ function handleOperatorChange() {
<Form.Item label="操作符" required> <Form.Item label="操作符" required>
<Select <Select
:value="condition.operator" :value="condition.operator"
@change=" @change="(value: any) => updateConditionField('operator', value)"
(value: any) => updateConditionField('operator', value)
"
placeholder="请选择操作符" placeholder="请选择操作符"
class="w-full" class="w-full"
> >
@ -247,10 +245,7 @@ function handleOperatorChange() {
<Form.Item label="设备状态" required> <Form.Item label="设备状态" required>
<Select <Select
:value="condition.param" :value="condition.param"
@change=" @change="(value: any) => updateConditionField('param', value)"
(value: any) => updateConditionField('param', value)
"
placeholder="请选择设备状态" placeholder="请选择设备状态"
class="w-full" class="w-full"
> >
@ -270,8 +265,7 @@ function handleOperatorChange() {
<!-- 设备属性条件配置 --> <!-- 设备属性条件配置 -->
<div <div
v-else-if=" v-else-if="
condition.type === condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY
IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY
" "
class="space-y-[16px]" class="space-y-[16px]"
> >
@ -327,8 +321,7 @@ function handleOperatorChange() {
<!-- 当前时间条件配置 --> <!-- 当前时间条件配置 -->
<CurrentTimeConditionConfig <CurrentTimeConditionConfig
v-else-if=" v-else-if="
condition.type === condition.type === IotRuleSceneTriggerConditionTypeEnum.CURRENT_TIME
IotRuleSceneTriggerConditionTypeEnum.CURRENT_TIME
" "
:model-value="condition" :model-value="condition"
@update:model-value="updateCondition" @update:model-value="updateCondition"

View File

@ -1,6 +1,6 @@
<!-- 当前时间条件配置组件 --> <!-- 当前时间条件配置组件 -->
<script setup lang="ts"> <script setup lang="ts">
import type { TriggerCondition } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { computed, watch } from 'vue'; import { computed, watch } from 'vue';
@ -22,11 +22,11 @@ import {
defineOptions({ name: 'CurrentTimeConditionConfig' }); defineOptions({ name: 'CurrentTimeConditionConfig' });
const props = defineProps<{ const props = defineProps<{
modelValue: TriggerCondition; modelValue: RuleSceneApi.TriggerCondition;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: TriggerCondition): void; (e: 'update:modelValue', value: RuleSceneApi.TriggerCondition): void;
}>(); }>();
const condition = useVModel(props, 'modelValue', emit); const condition = useVModel(props, 'modelValue', emit);

View File

@ -1,6 +1,6 @@
<!-- 设备控制配置组件 --> <!-- 设备控制配置组件 -->
<script setup lang="ts"> <script setup lang="ts">
import type { Action } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import type { ThingModelApi } from '#/api/iot/thingmodel'; import type { ThingModelApi } from '#/api/iot/thingmodel';
import { computed, onMounted, ref, watch } from 'vue'; import { computed, onMounted, ref, watch } from 'vue';
@ -25,11 +25,11 @@ import ProductSelector from '../selectors/product-selector.vue';
defineOptions({ name: 'DeviceControlConfig' }); defineOptions({ name: 'DeviceControlConfig' });
const props = defineProps<{ const props = defineProps<{
modelValue: Action; modelValue: RuleSceneApi.Action;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: Action): void; (e: 'update:modelValue', value: RuleSceneApi.Action): void;
}>(); }>();
const action = useVModel(props, 'modelValue', emit); const action = useVModel(props, 'modelValue', emit);
@ -58,18 +58,12 @@ const paramsValue = computed({
// //
const isPropertySetAction = computed(() => { const isPropertySetAction = computed(() => {
return ( return action.value.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET;
action.value.type ===
IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET
);
}); });
// //
const isServiceInvokeAction = computed(() => { const isServiceInvokeAction = computed(() => {
return ( return action.value.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE;
action.value.type ===
IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE
);
}); });
/** /**
@ -358,7 +352,10 @@ watch(
</Row> </Row>
<!-- 服务选择 - 服务调用类型时显示 --> <!-- 服务选择 - 服务调用类型时显示 -->
<div v-if="action.productId && isServiceInvokeAction" class="space-y-[16px]"> <div
v-if="action.productId && isServiceInvokeAction"
class="space-y-[16px]"
>
<Form.Item label="服务" required> <Form.Item label="服务" required>
<Select <Select
v-model:value="action.identifier" v-model:value="action.identifier"

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Trigger } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
@ -25,12 +25,12 @@ import PropertySelector from '../selectors/property-selector.vue';
defineOptions({ name: 'MainConditionInnerConfig' }); defineOptions({ name: 'MainConditionInnerConfig' });
const props = defineProps<{ const props = defineProps<{
modelValue: Trigger; modelValue: RuleSceneApi.Trigger;
triggerType: number; triggerType: number;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: Trigger): void; (e: 'update:modelValue', value: RuleSceneApi.Trigger): void;
(e: 'triggerTypeChange', value: number): void; (e: 'triggerTypeChange', value: number): void;
}>(); }>();
@ -325,9 +325,7 @@ function handlePropertyChange(propertyInfo: any) {
<Form.Item label="操作符" required> <Form.Item label="操作符" required>
<Select <Select
:value="condition.operator" :value="condition.operator"
@change=" @change="(value: any) => updateConditionField('operator', value)"
(value: any) => updateConditionField('operator', value)
"
placeholder="请选择操作符" placeholder="请选择操作符"
class="w-full" class="w-full"
> >
@ -347,9 +345,7 @@ function handlePropertyChange(propertyInfo: any) {
<Form.Item label="参数" required> <Form.Item label="参数" required>
<Select <Select
:value="condition.value" :value="condition.value"
@change=" @change="(value: any) => updateConditionField('value', value)"
(value: any) => updateConditionField('value', value)
"
placeholder="请选择操作符" placeholder="请选择操作符"
class="w-full" class="w-full"
> >
@ -371,7 +367,9 @@ function handlePropertyChange(propertyInfo: any) {
<p class="mb-1 text-sm text-muted-foreground"> <p class="mb-1 text-sm text-muted-foreground">
当前触发事件类型{{ getTriggerTypeLabel(triggerType) }} 当前触发事件类型{{ getTriggerTypeLabel(triggerType) }}
</p> </p>
<p class="text-xs text-muted-foreground">此触发类型暂不需要配置额外条件</p> <p class="text-xs text-muted-foreground">
此触发类型暂不需要配置额外条件
</p>
</div> </div>
</div> </div>
</template> </template>

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import type { TriggerCondition } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { computed, nextTick } from 'vue'; import { computed, nextTick } from 'vue';
@ -19,12 +19,12 @@ defineOptions({ name: 'SubConditionGroupConfig' });
const props = defineProps<{ const props = defineProps<{
maxConditions?: number; maxConditions?: number;
modelValue: TriggerCondition[]; modelValue: RuleSceneApi.TriggerCondition[];
triggerType: number; triggerType: number;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: TriggerCondition[]): void; (e: 'update:modelValue', value: RuleSceneApi.TriggerCondition[]): void;
}>(); }>();
const subGroup = useVModel(props, 'modelValue', emit); const subGroup = useVModel(props, 'modelValue', emit);
@ -43,7 +43,7 @@ async function addCondition() {
return; return;
} }
const newCondition: TriggerCondition = { const newCondition: RuleSceneApi.TriggerCondition = {
type: IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY, // type: IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY, //
productId: undefined, productId: undefined,
deviceId: undefined, deviceId: undefined,
@ -74,7 +74,10 @@ function removeCondition(index: number) {
* @param index 条件索引 * @param index 条件索引
* @param condition 条件对象 * @param condition 条件对象
*/ */
function updateCondition(index: number, condition: TriggerCondition) { function updateCondition(
index: number,
condition: RuleSceneApi.TriggerCondition,
) {
if (subGroup.value) { if (subGroup.value) {
subGroup.value[index] = condition; subGroup.value[index] = condition;
} }
@ -138,7 +141,7 @@ function updateCondition(index: number, condition: TriggerCondition) {
<ConditionConfig <ConditionConfig
:model-value="condition" :model-value="condition"
@update:model-value=" @update:model-value="
(value: TriggerCondition) => (value: RuleSceneApi.TriggerCondition) =>
updateCondition(conditionIndex, value) updateCondition(conditionIndex, value)
" "
:trigger-type="triggerType" :trigger-type="triggerType"

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import type { TriggerCondition } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
@ -12,11 +12,11 @@ import { Button, Tag } from 'ant-design-vue';
import SubConditionGroupConfig from './sub-condition-group-config.vue'; import SubConditionGroupConfig from './sub-condition-group-config.vue';
const props = defineProps<{ const props = defineProps<{
modelValue?: TriggerCondition[][]; modelValue?: RuleSceneApi.TriggerCondition[][];
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: TriggerCondition[][]): void; (e: 'update:modelValue', value: RuleSceneApi.TriggerCondition[][]): void;
}>(); }>();
const conditionGroups = useVModel(props, 'modelValue', emit); const conditionGroups = useVModel(props, 'modelValue', emit);
@ -46,7 +46,10 @@ function removeConditionGroup(index: number) {
} }
/** 更新条件组 */ /** 更新条件组 */
function updateConditionGroup(index: number, group: TriggerCondition[]) { function updateConditionGroup(
index: number,
group: RuleSceneApi.TriggerCondition[],
) {
if (conditionGroups.value) { if (conditionGroups.value) {
conditionGroups.value[index] = group; conditionGroups.value[index] = group;
} }
@ -117,9 +120,7 @@ function updateConditionGroup(index: number, group: TriggerCondition[]) {
<Tag color="warning" class="font-medium"> <Tag color="warning" class="font-medium">
组内条件为关系 组内条件为关系
</Tag> </Tag>
<Tag color="default"> <Tag color="default"> {{ group?.length || 0 }} 个条件 </Tag>
{{ group?.length || 0 }} 个条件
</Tag>
</div> </div>
<Button <Button
danger danger
@ -138,7 +139,7 @@ function updateConditionGroup(index: number, group: TriggerCondition[]) {
:trigger-type="IotRuleSceneTriggerTypeEnum.TIMER" :trigger-type="IotRuleSceneTriggerTypeEnum.TIMER"
:max-conditions="maxConditionsPerGroup" :max-conditions="maxConditionsPerGroup"
@update:model-value=" @update:model-value="
(value: TriggerCondition[]) => (value: RuleSceneApi.TriggerCondition[]) =>
updateConditionGroup(groupIndex, value) updateConditionGroup(groupIndex, value)
" "
/> />
@ -150,13 +151,19 @@ function updateConditionGroup(index: number, group: TriggerCondition[]) {
class="py-[12px] flex items-center justify-center" class="py-[12px] flex items-center justify-center"
> >
<div class="gap-[8px] flex items-center"> <div class="gap-[8px] flex items-center">
<div class="w-[32px] h-[1px] bg-orange-300 dark:bg-orange-800"></div> <div
class="w-[32px] h-[1px] bg-orange-300 dark:bg-orange-800"
></div>
<div <div
class="px-[14px] py-[3px] rounded-full border border-orange-300 bg-orange-100 dark:border-orange-800 dark:bg-orange-950/50" class="px-[14px] py-[3px] rounded-full border border-orange-300 bg-orange-100 dark:border-orange-800 dark:bg-orange-950/50"
> >
<span class="text-[13px] font-semibold text-orange-600 dark:text-orange-300"></span> <span
class="text-[13px] font-semibold text-orange-600 dark:text-orange-300"
></span>
</div> </div>
<div class="w-[32px] h-[1px] bg-orange-300 dark:bg-orange-800"></div> <div
class="w-[32px] h-[1px] bg-orange-300 dark:bg-orange-800"
></div>
</div> </div>
</div> </div>
</div> </div>
@ -168,7 +175,10 @@ function updateConditionGroup(index: number, group: TriggerCondition[]) {
class="p-[24px] rounded-[8px] border-2 border-dashed border-blue-200 bg-blue-50/40 text-center dark:border-blue-900/40 dark:bg-blue-950/10" class="p-[24px] rounded-[8px] border-2 border-dashed border-blue-200 bg-blue-50/40 text-center dark:border-blue-900/40 dark:bg-blue-950/10"
> >
<div class="gap-[10px] flex flex-col items-center"> <div class="gap-[10px] flex flex-col items-center">
<IconifyIcon icon="lucide:plus" class="text-[28px] text-blue-400 dark:text-blue-300" /> <IconifyIcon
icon="lucide:plus"
class="text-[28px] text-blue-400 dark:text-blue-300"
/>
<div class="text-blue-600 dark:text-blue-300"> <div class="text-blue-600 dark:text-blue-300">
<p class="text-[13px] font-medium mb-[2px]">暂无附加条件</p> <p class="text-[13px] font-medium mb-[2px]">暂无附加条件</p>
<p class="text-[12px]">定时触发时将直接执行动作</p> <p class="text-[12px]">定时触发时将直接执行动作</p>

View File

@ -1,6 +1,6 @@
<!-- 执行器配置组件 --> <!-- 执行器配置组件 -->
<script setup lang="ts"> <script setup lang="ts">
import type { Action } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { import {
getActionTypeLabel, getActionTypeLabel,
@ -19,11 +19,11 @@ import DeviceControlConfig from '../configs/device-control-config.vue';
defineOptions({ name: 'ActionSection' }); defineOptions({ name: 'ActionSection' });
const props = defineProps<{ const props = defineProps<{
actions: Action[]; actions: RuleSceneApi.Action[];
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:actions', value: Action[]): void; (e: 'update:actions', value: RuleSceneApi.Action[]): void;
}>(); }>();
const actions = useVModel(props, 'actions', emit); const actions = useVModel(props, 'actions', emit);
@ -66,7 +66,7 @@ function isAlertAction(type: number): boolean {
* 创建默认的执行器数据 * 创建默认的执行器数据
* @returns 默认执行器对象 * @returns 默认执行器对象
*/ */
function createDefaultActionData(): Action { function createDefaultActionData(): RuleSceneApi.Action {
return { return {
type: IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET, // type: IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET, //
productId: undefined, productId: undefined,
@ -100,7 +100,7 @@ function removeAction(index: number) {
*/ */
function updateActionType(index: number, type: number) { function updateActionType(index: number, type: number) {
actions.value[index]!.type = type; actions.value[index]!.type = type;
onActionTypeChange(actions.value[index] as Action, type); onActionTypeChange(actions.value[index] as RuleSceneApi.Action, type);
} }
/** /**
@ -108,7 +108,7 @@ function updateActionType(index: number, type: number) {
* @param index 执行器索引 * @param index 执行器索引
* @param action 执行器对象 * @param action 执行器对象
*/ */
function updateAction(index: number, action: Action) { function updateAction(index: number, action: RuleSceneApi.Action) {
actions.value[index] = action; actions.value[index] = action;
} }
@ -126,12 +126,12 @@ function updateActionAlertConfig(index: number, alertConfigId?: number) {
* @param action 执行器对象 * @param action 执行器对象
* @param type 执行器类型 * @param type 执行器类型
*/ */
function onActionTypeChange(action: Action, type: number) { function onActionTypeChange(action: RuleSceneApi.Action, type: number) {
if (isDeviceAction(type)) { if (isDeviceAction(type)) {
// //
action.alertConfigId = undefined; action.alertConfigId = undefined;
if (!action.params) { if (!action.params) {
action.params = {}; action.params = '';
} }
// identifier // identifier
if (action.identifier && type !== action.type) { if (action.identifier && type !== action.type) {
@ -153,7 +153,9 @@ function onActionTypeChange(action: Action, type: number) {
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="gap-[8px] flex items-center"> <div class="gap-[8px] flex items-center">
<IconifyIcon icon="ep:setting" class="text-[18px] text-primary" /> <IconifyIcon icon="ep:setting" class="text-[18px] text-primary" />
<span class="text-[16px] font-semibold text-foreground"> 执行器配置 </span> <span class="text-[16px] font-semibold text-foreground">
执行器配置
</span>
<Tag color="default"> {{ actions.length }} 个执行器 </Tag> <Tag color="default"> {{ actions.length }} 个执行器 </Tag>
</div> </div>
<div class="gap-[8px] flex items-center"> <div class="gap-[8px] flex items-center">
@ -251,10 +253,7 @@ function onActionTypeChange(action: Action, type: number) {
<!-- 告警配置 - 只有恢复告警时才显示 --> <!-- 告警配置 - 只有恢复告警时才显示 -->
<AlertConfig <AlertConfig
v-if=" v-if="action.type === IotRuleSceneActionTypeEnum.ALERT_RECOVER"
action.type ===
IotRuleSceneActionTypeEnum.ALERT_RECOVER
"
:model-value="action.alertConfigId" :model-value="action.alertConfigId"
@update:model-value=" @update:model-value="
(value) => updateActionAlertConfig(index, value) (value) => updateActionAlertConfig(index, value)
@ -263,10 +262,7 @@ function onActionTypeChange(action: Action, type: number) {
<!-- 触发告警提示 - 触发告警时显示 --> <!-- 触发告警提示 - 触发告警时显示 -->
<div <div
v-if=" v-if="action.type === IotRuleSceneActionTypeEnum.ALERT_TRIGGER"
action.type ===
IotRuleSceneActionTypeEnum.ALERT_TRIGGER
"
class="bg-fill-color-blank rounded-lg border border-border p-4" class="bg-fill-color-blank rounded-lg border border-border p-4"
> >
<div class="mb-2 flex items-center gap-2"> <div class="mb-2 flex items-center gap-2">

View File

@ -1,6 +1,6 @@
<!-- 基础信息配置组件 --> <!-- 基础信息配置组件 -->
<script setup lang="ts"> <script setup lang="ts">
import type { IotSceneRule } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { DICT_TYPE } from '@vben/constants'; import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks'; import { getDictOptions } from '@vben/hooks';
@ -15,11 +15,11 @@ import { DictTag } from '#/components/dict-tag';
defineOptions({ name: 'BasicInfoSection' }); defineOptions({ name: 'BasicInfoSection' });
const props = defineProps<{ const props = defineProps<{
modelValue: IotSceneRule; modelValue: RuleSceneApi.SceneRule;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: IotSceneRule): void; (e: 'update:modelValue', value: RuleSceneApi.SceneRule): void;
}>(); }>();
const formData = useVModel(props, 'modelValue', emit); // const formData = useVModel(props, 'modelValue', emit); //

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Trigger, TriggerCondition } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { onMounted } from 'vue'; import { onMounted } from 'vue';
@ -22,11 +22,11 @@ import TimerConditionGroupConfig from '../configs/timer-condition-group-config.v
defineOptions({ name: 'TriggerSection' }); defineOptions({ name: 'TriggerSection' });
const props = defineProps<{ const props = defineProps<{
triggers: Trigger[]; triggers: RuleSceneApi.Trigger[];
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:triggers', value: Trigger[]): void; (e: 'update:triggers', value: RuleSceneApi.Trigger[]): void;
}>(); }>();
const triggers = useVModel(props, 'triggers', emit); const triggers = useVModel(props, 'triggers', emit);
@ -43,7 +43,7 @@ function getTriggerTagColor(
/** 添加触发器 */ /** 添加触发器 */
function addTrigger() { function addTrigger() {
const newTrigger: Trigger = { const newTrigger: RuleSceneApi.Trigger = {
type: IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE, type: IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE,
productId: undefined, productId: undefined,
deviceId: undefined, deviceId: undefined,
@ -70,7 +70,10 @@ function updateTriggerType(index: number, type: number) {
} }
/** 更新触发器设备配置 */ /** 更新触发器设备配置 */
function updateTriggerDeviceConfig(index: number, newTrigger: Trigger) { function updateTriggerDeviceConfig(
index: number,
newTrigger: RuleSceneApi.Trigger,
) {
triggers.value[index] = newTrigger; triggers.value[index] = newTrigger;
} }
@ -82,7 +85,7 @@ function updateTriggerCronConfig(index: number, cronExpression?: string) {
/** 更新触发器条件组配置 */ /** 更新触发器条件组配置 */
function updateTriggerConditionGroups( function updateTriggerConditionGroups(
index: number, index: number,
conditionGroups: TriggerCondition[][], conditionGroups: RuleSceneApi.TriggerCondition[][],
) { ) {
triggers.value[index]!.conditionGroups = conditionGroups; triggers.value[index]!.conditionGroups = conditionGroups;
} }
@ -183,10 +186,7 @@ onMounted(() => {
<!-- 定时触发配置 --> <!-- 定时触发配置 -->
<div <div
v-else-if=" v-else-if="triggerItem.type === IotRuleSceneTriggerTypeEnum.TIMER"
triggerItem.type ===
IotRuleSceneTriggerTypeEnum.TIMER
"
class="gap-[16px] flex flex-col" class="gap-[16px] flex flex-col"
> >
<div <div

View File

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { IotSceneRule, RuleSceneApi } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { computed, nextTick, reactive, ref } from 'vue'; import { computed, nextTick, reactive, ref } from 'vue';
@ -29,7 +29,7 @@ defineOptions({ name: 'IoTRuleSceneForm' });
const emit = defineEmits(['success']); const emit = defineEmits(['success']);
const formRef = ref(); const formRef = ref();
const formData = ref<IotSceneRule>(buildEmptyFormData()); const formData = ref<RuleSceneApi.SceneRule>(buildEmptyFormData());
const getTitle = computed(() => const getTitle = computed(() =>
formData.value.id formData.value.id
@ -46,7 +46,7 @@ const [Drawer, drawerApi] = useVbenDrawer({
} }
drawerApi.lock(); drawerApi.lock();
try { try {
const data = { ...formData.value } as IotSceneRule; const data = { ...formData.value } as RuleSceneApi.SceneRule;
await (data.id ? updateSceneRule(data) : createSceneRule(data)); await (data.id ? updateSceneRule(data) : createSceneRule(data));
await drawerApi.close(); await drawerApi.close();
emit('success'); emit('success');
@ -78,7 +78,7 @@ const [Drawer, drawerApi] = useVbenDrawer({
}); });
/** 构造空白表单数据 */ /** 构造空白表单数据 */
function buildEmptyFormData(): IotSceneRule { function buildEmptyFormData(): RuleSceneApi.SceneRule {
return { return {
name: '', name: '',
description: '', description: '',
@ -100,7 +100,7 @@ function buildEmptyFormData(): IotSceneRule {
} }
/** 回显时兜底,保证触发器/执行器数组不为空 */ /** 回显时兜底,保证触发器/执行器数组不为空 */
function normalizeFormData(result: any): IotSceneRule { function normalizeFormData(result: any): RuleSceneApi.SceneRule {
return { return {
...result, ...result,
triggers: result.triggers?.length triggers: result.triggers?.length

View File

@ -51,53 +51,6 @@ export namespace RuleSceneApi {
} }
} }
// TODO @haohao貌似下面的和 RuleSceneApi 重复了。
/** IoT 场景联动规则 */
export interface IotSceneRule {
id?: number;
name?: string;
description?: string;
status?: number;
triggers?: Trigger[];
actions?: Action[];
createTime?: Date;
}
/** IoT 场景联动规则触发器 */
export interface Trigger {
type?: number;
productId?: number;
deviceId?: number;
identifier?: string;
operator?: string;
value?: any;
cronExpression?: string;
// 后端结构List<List<TriggerCondition>>;外层「或」、组内「且」
conditionGroups?: TriggerCondition[][];
}
/** IoT 场景联动规则触发条件 */
export interface TriggerCondition {
productId?: number;
deviceId?: number;
identifier?: string;
operator?: string;
value?: any;
type?: number;
param?: string;
}
/** IoT 场景联动规则动作 */
export interface Action {
type?: number;
productId?: number;
deviceId?: number;
identifier?: string;
value?: any;
alertConfigId?: number;
params?: Record<string, any>;
}
/** 查询场景联动规则分页 */ /** 查询场景联动规则分页 */
export function getSceneRulePage(params: PageParam) { export function getSceneRulePage(params: PageParam) {
return requestClient.get<PageResult<RuleSceneApi.SceneRule>>( return requestClient.get<PageResult<RuleSceneApi.SceneRule>>(
@ -114,12 +67,12 @@ export function getSceneRule(id: number) {
} }
/** 新增场景联动规则 */ /** 新增场景联动规则 */
export function createSceneRule(data: IotSceneRule) { export function createSceneRule(data: RuleSceneApi.SceneRule) {
return requestClient.post('/iot/scene-rule/create', data); return requestClient.post('/iot/scene-rule/create', data);
} }
/** 修改场景联动规则 */ /** 修改场景联动规则 */
export function updateSceneRule(data: IotSceneRule) { export function updateSceneRule(data: RuleSceneApi.SceneRule) {
return requestClient.put('/iot/scene-rule/update', data); return requestClient.put('/iot/scene-rule/update', data);
} }
@ -129,7 +82,6 @@ export function deleteSceneRule(id: number) {
} }
/** 批量删除场景联动规则 */ /** 批量删除场景联动规则 */
// TODO @haohao貌似用上。
export function deleteSceneRuleList(ids: number[]) { export function deleteSceneRuleList(ids: number[]) {
return requestClient.delete('/iot/scene-rule/delete-list', { return requestClient.delete('/iot/scene-rule/delete-list', {
params: { ids: ids.join(',') }, params: { ids: ids.join(',') },

View File

@ -1,6 +1,6 @@
<!-- 单个条件配置组件 --> <!-- 单个条件配置组件 -->
<script setup lang="ts"> <script setup lang="ts">
import type { TriggerCondition } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
@ -12,13 +12,7 @@ import {
} from '@vben/constants'; } from '@vben/constants';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { import { ElCol, ElFormItem, ElOption, ElRow, ElSelect } from 'element-plus';
ElCol,
ElFormItem,
ElOption,
ElRow,
ElSelect,
} from 'element-plus';
import ValueInput from '../inputs/value-input.vue'; import ValueInput from '../inputs/value-input.vue';
import DeviceSelector from '../selectors/device-selector.vue'; import DeviceSelector from '../selectors/device-selector.vue';
@ -31,12 +25,12 @@ import CurrentTimeConditionConfig from './current-time-condition-config.vue';
defineOptions({ name: 'ConditionConfig' }); defineOptions({ name: 'ConditionConfig' });
const props = defineProps<{ const props = defineProps<{
modelValue: TriggerCondition; modelValue: RuleSceneApi.TriggerCondition;
triggerType: number; triggerType: number;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: TriggerCondition): void; (e: 'update:modelValue', value: RuleSceneApi.TriggerCondition): void;
}>(); }>();
/** 获取设备状态选项 */ /** 获取设备状态选项 */
@ -90,7 +84,7 @@ function updateConditionField(field: any, value: any) {
* 更新整个条件对象 * 更新整个条件对象
* @param newCondition 新的条件对象 * @param newCondition 新的条件对象
*/ */
function updateCondition(newCondition: TriggerCondition) { function updateCondition(newCondition: RuleSceneApi.TriggerCondition) {
condition.value = newCondition; condition.value = newCondition;
emit('update:modelValue', condition.value); emit('update:modelValue', condition.value);
} }
@ -217,8 +211,7 @@ function handleOperatorChange() {
<!-- 设备状态条件配置 --> <!-- 设备状态条件配置 -->
<div <div
v-if=" v-if="
condition.type === condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS
IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS
" "
class="gap-16px flex flex-col" class="gap-16px flex flex-col"
> >
@ -271,8 +264,7 @@ function handleOperatorChange() {
<!-- 设备属性条件配置 --> <!-- 设备属性条件配置 -->
<div <div
v-else-if=" v-else-if="
condition.type === condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY
IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY
" "
class="space-y-16px" class="space-y-16px"
> >
@ -328,8 +320,7 @@ function handleOperatorChange() {
<!-- 当前时间条件配置 --> <!-- 当前时间条件配置 -->
<CurrentTimeConditionConfig <CurrentTimeConditionConfig
v-else-if=" v-else-if="
condition.type === condition.type === IotRuleSceneTriggerConditionTypeEnum.CURRENT_TIME
IotRuleSceneTriggerConditionTypeEnum.CURRENT_TIME
" "
:model-value="condition" :model-value="condition"
@update:model-value="updateCondition" @update:model-value="updateCondition"

View File

@ -1,6 +1,6 @@
<!-- 当前时间条件配置组件 --> <!-- 当前时间条件配置组件 -->
<script setup lang="ts"> <script setup lang="ts">
import type { TriggerCondition } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { computed, watch } from 'vue'; import { computed, watch } from 'vue';
@ -23,11 +23,11 @@ import {
defineOptions({ name: 'CurrentTimeConditionConfig' }); defineOptions({ name: 'CurrentTimeConditionConfig' });
const props = defineProps<{ const props = defineProps<{
modelValue: TriggerCondition; modelValue: RuleSceneApi.TriggerCondition;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: TriggerCondition): void; (e: 'update:modelValue', value: RuleSceneApi.TriggerCondition): void;
}>(); }>();
const condition = useVModel(props, 'modelValue', emit); const condition = useVModel(props, 'modelValue', emit);

View File

@ -1,6 +1,6 @@
<!-- 设备控制配置组件 --> <!-- 设备控制配置组件 -->
<script setup lang="ts"> <script setup lang="ts">
import type { Action } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import type { ThingModelApi } from '#/api/iot/thingmodel'; import type { ThingModelApi } from '#/api/iot/thingmodel';
import { computed, onMounted, ref, watch } from 'vue'; import { computed, onMounted, ref, watch } from 'vue';
@ -32,11 +32,11 @@ import ProductSelector from '../selectors/product-selector.vue';
defineOptions({ name: 'DeviceControlConfig' }); defineOptions({ name: 'DeviceControlConfig' });
const props = defineProps<{ const props = defineProps<{
modelValue: Action; modelValue: RuleSceneApi.Action;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: Action): void; (e: 'update:modelValue', value: RuleSceneApi.Action): void;
}>(); }>();
const action = useVModel(props, 'modelValue', emit); const action = useVModel(props, 'modelValue', emit);

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Trigger } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
@ -12,13 +12,7 @@ import {
} from '@vben/constants'; } from '@vben/constants';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { import { ElCol, ElFormItem, ElOption, ElRow, ElSelect } from 'element-plus';
ElCol,
ElFormItem,
ElOption,
ElRow,
ElSelect,
} from 'element-plus';
import JsonParamsInput from '../inputs/json-params-input.vue'; import JsonParamsInput from '../inputs/json-params-input.vue';
import ValueInput from '../inputs/value-input.vue'; import ValueInput from '../inputs/value-input.vue';
@ -31,12 +25,12 @@ import PropertySelector from '../selectors/property-selector.vue';
defineOptions({ name: 'MainConditionInnerConfig' }); defineOptions({ name: 'MainConditionInnerConfig' });
const props = defineProps<{ const props = defineProps<{
modelValue: Trigger; modelValue: RuleSceneApi.Trigger;
triggerType: number; triggerType: number;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: Trigger): void; (e: 'update:modelValue', value: RuleSceneApi.Trigger): void;
(e: 'triggerTypeChange', value: number): void; (e: 'triggerTypeChange', value: number): void;
}>(); }>();

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import type { TriggerCondition } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { computed, nextTick } from 'vue'; import { computed, nextTick } from 'vue';
@ -19,12 +19,12 @@ defineOptions({ name: 'SubConditionGroupConfig' });
const props = defineProps<{ const props = defineProps<{
maxConditions?: number; maxConditions?: number;
modelValue: TriggerCondition[]; modelValue: RuleSceneApi.TriggerCondition[];
triggerType: number; triggerType: number;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: TriggerCondition[]): void; (e: 'update:modelValue', value: RuleSceneApi.TriggerCondition[]): void;
}>(); }>();
const subGroup = useVModel(props, 'modelValue', emit); const subGroup = useVModel(props, 'modelValue', emit);
@ -43,7 +43,7 @@ async function addCondition() {
return; return;
} }
const newCondition: TriggerCondition = { const newCondition: RuleSceneApi.TriggerCondition = {
type: IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY, // type: IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY, //
productId: undefined, productId: undefined,
deviceId: undefined, deviceId: undefined,
@ -74,7 +74,10 @@ function removeCondition(index: number) {
* @param index 条件索引 * @param index 条件索引
* @param condition 条件对象 * @param condition 条件对象
*/ */
function updateCondition(index: number, condition: TriggerCondition) { function updateCondition(
index: number,
condition: RuleSceneApi.TriggerCondition,
) {
if (subGroup.value) { if (subGroup.value) {
subGroup.value[index] = condition; subGroup.value[index] = condition;
} }
@ -138,7 +141,7 @@ function updateCondition(index: number, condition: TriggerCondition) {
<ConditionConfig <ConditionConfig
:model-value="condition" :model-value="condition"
@update:model-value=" @update:model-value="
(value: TriggerCondition) => (value: RuleSceneApi.TriggerCondition) =>
updateCondition(conditionIndex, value) updateCondition(conditionIndex, value)
" "
:trigger-type="triggerType" :trigger-type="triggerType"

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import type { TriggerCondition } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
@ -12,11 +12,11 @@ import { ElButton, ElTag } from 'element-plus';
import SubConditionGroupConfig from './sub-condition-group-config.vue'; import SubConditionGroupConfig from './sub-condition-group-config.vue';
const props = defineProps<{ const props = defineProps<{
modelValue?: TriggerCondition[][]; modelValue?: RuleSceneApi.TriggerCondition[][];
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: TriggerCondition[][]): void; (e: 'update:modelValue', value: RuleSceneApi.TriggerCondition[][]): void;
}>(); }>();
const conditionGroups = useVModel(props, 'modelValue', emit); const conditionGroups = useVModel(props, 'modelValue', emit);
@ -46,7 +46,10 @@ function removeConditionGroup(index: number) {
} }
/** 更新条件组 */ /** 更新条件组 */
function updateConditionGroup(index: number, group: TriggerCondition[]) { function updateConditionGroup(
index: number,
group: RuleSceneApi.TriggerCondition[],
) {
if (conditionGroups.value) { if (conditionGroups.value) {
conditionGroups.value[index] = group; conditionGroups.value[index] = group;
} }
@ -117,9 +120,7 @@ function updateConditionGroup(index: number, group: TriggerCondition[]) {
<ElTag type="warning" class="font-medium"> <ElTag type="warning" class="font-medium">
组内条件为关系 组内条件为关系
</ElTag> </ElTag>
<ElTag type="info"> <ElTag type="info"> {{ group?.length || 0 }} 个条件 </ElTag>
{{ group?.length || 0 }} 个条件
</ElTag>
</div> </div>
<ElButton <ElButton
link link
@ -138,7 +139,7 @@ function updateConditionGroup(index: number, group: TriggerCondition[]) {
:trigger-type="IotRuleSceneTriggerTypeEnum.TIMER" :trigger-type="IotRuleSceneTriggerTypeEnum.TIMER"
:max-conditions="maxConditionsPerGroup" :max-conditions="maxConditionsPerGroup"
@update:model-value=" @update:model-value="
(value: TriggerCondition[]) => (value: RuleSceneApi.TriggerCondition[]) =>
updateConditionGroup(groupIndex, value) updateConditionGroup(groupIndex, value)
" "
/> />
@ -150,13 +151,19 @@ function updateConditionGroup(index: number, group: TriggerCondition[]) {
class="py-[12px] flex items-center justify-center" class="py-[12px] flex items-center justify-center"
> >
<div class="gap-[8px] flex items-center"> <div class="gap-[8px] flex items-center">
<div class="w-[32px] h-[1px] bg-orange-300 dark:bg-orange-800"></div> <div
class="w-[32px] h-[1px] bg-orange-300 dark:bg-orange-800"
></div>
<div <div
class="px-[14px] py-[3px] rounded-full border border-orange-300 bg-orange-100 dark:border-orange-800 dark:bg-orange-950/50" class="px-[14px] py-[3px] rounded-full border border-orange-300 bg-orange-100 dark:border-orange-800 dark:bg-orange-950/50"
> >
<span class="text-[13px] font-semibold text-orange-600 dark:text-orange-300"></span> <span
class="text-[13px] font-semibold text-orange-600 dark:text-orange-300"
></span>
</div> </div>
<div class="w-[32px] h-[1px] bg-orange-300 dark:bg-orange-800"></div> <div
class="w-[32px] h-[1px] bg-orange-300 dark:bg-orange-800"
></div>
</div> </div>
</div> </div>
</div> </div>
@ -168,7 +175,10 @@ function updateConditionGroup(index: number, group: TriggerCondition[]) {
class="p-[24px] rounded-[8px] border-2 border-dashed border-blue-200 bg-blue-50/40 text-center dark:border-blue-900/40 dark:bg-blue-950/10" class="p-[24px] rounded-[8px] border-2 border-dashed border-blue-200 bg-blue-50/40 text-center dark:border-blue-900/40 dark:bg-blue-950/10"
> >
<div class="gap-[10px] flex flex-col items-center"> <div class="gap-[10px] flex flex-col items-center">
<IconifyIcon icon="lucide:plus" class="text-[28px] text-blue-400 dark:text-blue-300" /> <IconifyIcon
icon="lucide:plus"
class="text-[28px] text-blue-400 dark:text-blue-300"
/>
<div class="text-blue-600 dark:text-blue-300"> <div class="text-blue-600 dark:text-blue-300">
<p class="text-[13px] font-medium mb-[2px]">暂无附加条件</p> <p class="text-[13px] font-medium mb-[2px]">暂无附加条件</p>
<p class="text-[12px]">定时触发时将直接执行动作</p> <p class="text-[12px]">定时触发时将直接执行动作</p>

View File

@ -1,6 +1,6 @@
<!-- 执行器配置组件 --> <!-- 执行器配置组件 -->
<script setup lang="ts"> <script setup lang="ts">
import type { Action } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { import {
getActionTypeLabel, getActionTypeLabel,
@ -27,11 +27,11 @@ import DeviceControlConfig from '../configs/device-control-config.vue';
defineOptions({ name: 'ActionSection' }); defineOptions({ name: 'ActionSection' });
const props = defineProps<{ const props = defineProps<{
actions: Action[]; actions: RuleSceneApi.Action[];
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:actions', value: Action[]): void; (e: 'update:actions', value: RuleSceneApi.Action[]): void;
}>(); }>();
const actions = useVModel(props, 'actions', emit); const actions = useVModel(props, 'actions', emit);
@ -74,7 +74,7 @@ function isAlertAction(type: number): boolean {
* 创建默认的执行器数据 * 创建默认的执行器数据
* @returns 默认执行器对象 * @returns 默认执行器对象
*/ */
function createDefaultActionData(): Action { function createDefaultActionData(): RuleSceneApi.Action {
return { return {
type: IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET, // type: IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET, //
productId: undefined, productId: undefined,
@ -108,7 +108,7 @@ function removeAction(index: number) {
*/ */
function updateActionType(index: number, type: number) { function updateActionType(index: number, type: number) {
actions.value[index]!.type = type; actions.value[index]!.type = type;
onActionTypeChange(actions.value[index] as Action, type); onActionTypeChange(actions.value[index] as RuleSceneApi.Action, type);
} }
/** /**
@ -116,7 +116,7 @@ function updateActionType(index: number, type: number) {
* @param index 执行器索引 * @param index 执行器索引
* @param action 执行器对象 * @param action 执行器对象
*/ */
function updateAction(index: number, action: Action) { function updateAction(index: number, action: RuleSceneApi.Action) {
actions.value[index] = action; actions.value[index] = action;
} }
@ -137,7 +137,7 @@ function updateActionAlertConfig(index: number, alertConfigId?: number) {
* @param action 执行器对象 * @param action 执行器对象
* @param type 执行器类型 * @param type 执行器类型
*/ */
function onActionTypeChange(action: Action, type: any) { function onActionTypeChange(action: RuleSceneApi.Action, type: any) {
// //
if (isDeviceAction(type)) { if (isDeviceAction(type)) {
// //
@ -166,7 +166,9 @@ function onActionTypeChange(action: Action, type: any) {
<div class="gap-8px flex items-center"> <div class="gap-8px flex items-center">
<IconifyIcon icon="ep:setting" class="text-18px text-primary" /> <IconifyIcon icon="ep:setting" class="text-18px text-primary" />
<span class="text-16px font-600 text-primary"> 执行器配置 </span> <span class="text-16px font-600 text-primary"> 执行器配置 </span>
<ElTag size="small" type="info"> {{ actions.length }} 个执行器 </ElTag> <ElTag size="small" type="info">
{{ actions.length }} 个执行器
</ElTag>
</div> </div>
<div class="gap-8px flex items-center"> <div class="gap-8px flex items-center">
<ElButton type="primary" size="small" @click="addAction"> <ElButton type="primary" size="small" @click="addAction">
@ -268,10 +270,7 @@ function onActionTypeChange(action: Action, type: any) {
<!-- 告警配置 - 只有恢复告警时才显示 --> <!-- 告警配置 - 只有恢复告警时才显示 -->
<AlertConfig <AlertConfig
v-if=" v-if="action.type === IotRuleSceneActionTypeEnum.ALERT_RECOVER"
action.type ===
IotRuleSceneActionTypeEnum.ALERT_RECOVER
"
:model-value="action.alertConfigId" :model-value="action.alertConfigId"
@update:model-value=" @update:model-value="
(value) => updateActionAlertConfig(index, value) (value) => updateActionAlertConfig(index, value)
@ -280,10 +279,7 @@ function onActionTypeChange(action: Action, type: any) {
<!-- 触发告警提示 - 触发告警时显示 --> <!-- 触发告警提示 - 触发告警时显示 -->
<div <div
v-if=" v-if="action.type === IotRuleSceneActionTypeEnum.ALERT_TRIGGER"
action.type ===
IotRuleSceneActionTypeEnum.ALERT_TRIGGER
"
class="bg-fill-color-blank rounded-lg border border-border p-4" class="bg-fill-color-blank rounded-lg border border-border p-4"
> >
<div class="mb-2 flex items-center gap-2"> <div class="mb-2 flex items-center gap-2">

View File

@ -1,6 +1,6 @@
<!-- 基础信息配置组件 --> <!-- 基础信息配置组件 -->
<script setup lang="ts"> <script setup lang="ts">
import type { IotSceneRule } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { DICT_TYPE } from '@vben/constants'; import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks'; import { getDictOptions } from '@vben/hooks';
@ -23,12 +23,12 @@ import { DictTag } from '#/components/dict-tag';
defineOptions({ name: 'BasicInfoSection' }); defineOptions({ name: 'BasicInfoSection' });
const props = defineProps<{ const props = defineProps<{
modelValue: IotSceneRule; modelValue: RuleSceneApi.SceneRule;
rules?: any; rules?: any;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: IotSceneRule): void; (e: 'update:modelValue', value: RuleSceneApi.SceneRule): void;
}>(); }>();
const formData = useVModel(props, 'modelValue', emit); // const formData = useVModel(props, 'modelValue', emit); //

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Trigger } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { onMounted } from 'vue'; import { onMounted } from 'vue';
@ -11,13 +11,7 @@ import {
import { IconifyIcon } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { import { ElButton, ElCard, ElEmpty, ElFormItem, ElTag } from 'element-plus';
ElButton,
ElCard,
ElEmpty,
ElFormItem,
ElTag,
} from 'element-plus';
import { CronTab } from '#/components/cron-tab'; import { CronTab } from '#/components/cron-tab';
@ -27,11 +21,11 @@ import DeviceTriggerConfig from '../configs/device-trigger-config.vue';
defineOptions({ name: 'TriggerSection' }); defineOptions({ name: 'TriggerSection' });
const props = defineProps<{ const props = defineProps<{
triggers: Trigger[]; triggers: RuleSceneApi.Trigger[];
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:triggers', value: Trigger[]): void; (e: 'update:triggers', value: RuleSceneApi.Trigger[]): void;
}>(); }>();
const triggers = useVModel(props, 'triggers', emit); const triggers = useVModel(props, 'triggers', emit);
@ -48,7 +42,7 @@ function getTriggerTagType(
/** 添加触发器 */ /** 添加触发器 */
function addTrigger() { function addTrigger() {
const newTrigger: Trigger = { const newTrigger: RuleSceneApi.Trigger = {
type: IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE, type: IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE,
productId: undefined, productId: undefined,
deviceId: undefined, deviceId: undefined,
@ -86,7 +80,10 @@ function updateTriggerType(index: number, type: number) {
* @param index 触发器索引 * @param index 触发器索引
* @param newTrigger 新的触发器对象 * @param newTrigger 新的触发器对象
*/ */
function updateTriggerDeviceConfig(index: number, newTrigger: Trigger) { function updateTriggerDeviceConfig(
index: number,
newTrigger: RuleSceneApi.Trigger,
) {
triggers.value[index] = newTrigger; triggers.value[index] = newTrigger;
} }
@ -130,7 +127,9 @@ onMounted(() => {
<div class="gap-8px flex items-center"> <div class="gap-8px flex items-center">
<IconifyIcon icon="ep:lightning" class="text-18px text-primary" /> <IconifyIcon icon="ep:lightning" class="text-18px text-primary" />
<span class="text-16px font-600 text-primary">触发器配置</span> <span class="text-16px font-600 text-primary">触发器配置</span>
<ElTag size="small" type="info"> {{ triggers.length }} 个触发器 </ElTag> <ElTag size="small" type="info">
{{ triggers.length }} 个触发器
</ElTag>
</div> </div>
<ElButton type="primary" size="small" @click="addTrigger"> <ElButton type="primary" size="small" @click="addTrigger">
<IconifyIcon icon="lucide:plus" /> <IconifyIcon icon="lucide:plus" />
@ -200,10 +199,7 @@ onMounted(() => {
<!-- 定时触发配置 --> <!-- 定时触发配置 -->
<div <div
v-else-if=" v-else-if="triggerItem.type === IotRuleSceneTriggerTypeEnum.TIMER"
triggerItem.type ===
IotRuleSceneTriggerTypeEnum.TIMER
"
class="gap-16px flex flex-col" class="gap-16px flex flex-col"
> >
<div <div

View File

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { IotSceneRule, RuleSceneApi } from '#/api/iot/rule/scene'; import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { computed, nextTick, reactive, ref } from 'vue'; import { computed, nextTick, reactive, ref } from 'vue';
@ -29,7 +29,7 @@ defineOptions({ name: 'IoTRuleSceneForm' });
const emit = defineEmits(['success']); const emit = defineEmits(['success']);
const formRef = ref(); const formRef = ref();
const formData = ref<IotSceneRule>(buildEmptyFormData()); const formData = ref<RuleSceneApi.SceneRule>(buildEmptyFormData());
const getTitle = computed(() => const getTitle = computed(() =>
formData.value.id formData.value.id
@ -46,7 +46,7 @@ const [Drawer, drawerApi] = useVbenDrawer({
} }
drawerApi.lock(); drawerApi.lock();
try { try {
const data = { ...formData.value } as IotSceneRule; const data = { ...formData.value } as RuleSceneApi.SceneRule;
await (data.id ? updateSceneRule(data) : createSceneRule(data)); await (data.id ? updateSceneRule(data) : createSceneRule(data));
await drawerApi.close(); await drawerApi.close();
emit('success'); emit('success');
@ -78,7 +78,7 @@ const [Drawer, drawerApi] = useVbenDrawer({
}); });
/** 构造空白表单数据 */ /** 构造空白表单数据 */
function buildEmptyFormData(): IotSceneRule { function buildEmptyFormData(): RuleSceneApi.SceneRule {
return { return {
name: '', name: '',
description: '', description: '',
@ -100,7 +100,7 @@ function buildEmptyFormData(): IotSceneRule {
} }
/** 回显时兜底,保证触发器/执行器数组不为空 */ /** 回显时兜底,保证触发器/执行器数组不为空 */
function normalizeFormData(result: any): IotSceneRule { function normalizeFormData(result: any): RuleSceneApi.SceneRule {
return { return {
...result, ...result,
triggers: result.triggers?.length triggers: result.triggers?.length