perf: 【IoT 物联网】场景联动类型归纳优化,移除多余文件
parent
9f3eb14a0f
commit
a8adda7dc5
|
|
@ -1,5 +1,46 @@
|
|||
import request from '@/config/axios'
|
||||
import { IotSceneRule } from './scene.types'
|
||||
|
||||
// 场景联动
|
||||
export interface IotSceneRule {
|
||||
id?: number // 场景编号
|
||||
name: string // 场景名称
|
||||
description?: string // 场景描述
|
||||
status: number // 场景状态:0-开启,1-关闭
|
||||
triggers: Trigger[] // 触发器数组
|
||||
actions: Action[] // 执行器数组
|
||||
}
|
||||
|
||||
// 触发器结构
|
||||
export interface Trigger {
|
||||
type: number // 触发类型
|
||||
productId?: number // 产品编号
|
||||
deviceId?: number // 设备编号
|
||||
identifier?: string // 物模型标识符
|
||||
operator?: string // 操作符
|
||||
value?: string // 参数值
|
||||
cronExpression?: string // CRON 表达式
|
||||
conditionGroups?: TriggerCondition[][] // 条件组(二维数组)
|
||||
}
|
||||
|
||||
// 触发条件结构
|
||||
export interface TriggerCondition {
|
||||
type: number // 条件类型:1-设备状态,2-设备属性,3-当前时间
|
||||
productId?: number // 产品编号
|
||||
deviceId?: number // 设备编号
|
||||
identifier?: string // 标识符
|
||||
operator: string // 操作符
|
||||
param: string // 参数
|
||||
}
|
||||
|
||||
// 执行器结构
|
||||
export interface Action {
|
||||
type: number // 执行类型
|
||||
productId?: number // 产品编号
|
||||
deviceId?: number // 设备编号
|
||||
identifier?: string // 物模型标识符(服务调用时使用)
|
||||
params?: string // 请求参数
|
||||
alertConfigId?: number // 告警配置编号
|
||||
}
|
||||
|
||||
// IoT 场景联动 API
|
||||
export const RuleSceneApi = {
|
||||
|
|
|
|||
|
|
@ -1,202 +0,0 @@
|
|||
/**
|
||||
* IoT 场景联动接口定义
|
||||
*/
|
||||
|
||||
// ========== IoT物模型TSL数据类型定义 ==========
|
||||
|
||||
// TODO @puhui999:看看有些是不是在别的模块已经定义了。物模型的
|
||||
|
||||
/** 物模型TSL响应数据结构 */
|
||||
export interface IotThingModelTSLRespVO {
|
||||
productId: number
|
||||
productKey: string
|
||||
properties: ThingModelProperty[]
|
||||
events: ThingModelEvent[]
|
||||
services: ThingModelService[]
|
||||
}
|
||||
|
||||
/** 物模型属性 */
|
||||
export interface ThingModelProperty {
|
||||
identifier: string
|
||||
name: string
|
||||
accessMode: string
|
||||
required?: boolean
|
||||
dataType: string
|
||||
description?: string
|
||||
dataSpecs?: ThingModelDataSpecs
|
||||
dataSpecsList?: ThingModelDataSpecs[]
|
||||
}
|
||||
|
||||
/** 物模型事件 */
|
||||
export interface ThingModelEvent {
|
||||
identifier: string
|
||||
name: string
|
||||
required?: boolean
|
||||
type: string
|
||||
description?: string
|
||||
outputParams?: ThingModelParam[]
|
||||
method?: string
|
||||
}
|
||||
|
||||
/** 物模型服务 */
|
||||
export interface ThingModelService {
|
||||
identifier: string
|
||||
name: string
|
||||
required?: boolean
|
||||
callType: string
|
||||
description?: string
|
||||
inputParams?: ThingModelParam[]
|
||||
outputParams?: ThingModelParam[]
|
||||
method?: string
|
||||
}
|
||||
|
||||
/** 物模型参数 */
|
||||
export interface ThingModelParam {
|
||||
identifier: string
|
||||
name: string
|
||||
direction: string
|
||||
paraOrder?: number
|
||||
dataType: string
|
||||
dataSpecs?: ThingModelDataSpecs
|
||||
dataSpecsList?: ThingModelDataSpecs[]
|
||||
}
|
||||
|
||||
/** 数值型数据规范 */
|
||||
export interface ThingModelNumericDataSpec {
|
||||
dataType: 'int' | 'float' | 'double'
|
||||
max: string
|
||||
min: string
|
||||
step: string
|
||||
precise?: string
|
||||
defaultValue?: string
|
||||
unit?: string
|
||||
unitName?: string
|
||||
}
|
||||
|
||||
/** 布尔/枚举型数据规范 */
|
||||
export interface ThingModelBoolOrEnumDataSpecs {
|
||||
dataType: 'bool' | 'enum'
|
||||
name: string
|
||||
value: number
|
||||
}
|
||||
|
||||
/** 文本/时间型数据规范 */
|
||||
export interface ThingModelDateOrTextDataSpecs {
|
||||
dataType: 'text' | 'date'
|
||||
length?: number
|
||||
defaultValue?: string
|
||||
}
|
||||
|
||||
/** 数组型数据规范 */
|
||||
export interface ThingModelArrayDataSpecs {
|
||||
dataType: 'array'
|
||||
size: number
|
||||
childDataType: string
|
||||
dataSpecsList?: ThingModelDataSpecs[]
|
||||
}
|
||||
|
||||
/** 结构体型数据规范 */
|
||||
export interface ThingModelStructDataSpecs {
|
||||
dataType: 'struct'
|
||||
identifier: string
|
||||
name: string
|
||||
accessMode: string
|
||||
required?: boolean
|
||||
childDataType: string
|
||||
dataSpecs?: ThingModelDataSpecs
|
||||
dataSpecsList?: ThingModelDataSpecs[]
|
||||
}
|
||||
|
||||
/** 数据规范联合类型 */
|
||||
export type ThingModelDataSpecs =
|
||||
| ThingModelNumericDataSpec
|
||||
| ThingModelBoolOrEnumDataSpecs
|
||||
| ThingModelDateOrTextDataSpecs
|
||||
| ThingModelArrayDataSpecs
|
||||
| ThingModelStructDataSpecs
|
||||
|
||||
/** 属性选择器内部使用的统一数据结构 */
|
||||
export interface PropertySelectorItem {
|
||||
identifier: string
|
||||
name: string
|
||||
description?: string
|
||||
dataType: string
|
||||
type: number // IoTThingModelTypeEnum
|
||||
accessMode?: string
|
||||
required?: boolean
|
||||
unit?: string
|
||||
range?: string
|
||||
eventType?: string
|
||||
callType?: string
|
||||
inputParams?: ThingModelParam[]
|
||||
outputParams?: ThingModelParam[]
|
||||
property?: ThingModelProperty
|
||||
event?: ThingModelEvent
|
||||
service?: ThingModelService
|
||||
}
|
||||
|
||||
// ========== 场景联动规则相关接口定义 ==========
|
||||
|
||||
// 后端 DO 接口 - 匹配后端数据结构
|
||||
interface IotSceneRule {
|
||||
id?: number // 场景编号
|
||||
name: string // 场景名称
|
||||
description?: string // 场景描述
|
||||
status: number // 场景状态:0-开启,1-关闭
|
||||
triggers: Trigger[] // 触发器数组
|
||||
actions: Action[] // 执行器数组
|
||||
}
|
||||
|
||||
// 触发器 DO 结构
|
||||
interface Trigger {
|
||||
type: number // 触发类型
|
||||
productId?: number // 产品编号
|
||||
deviceId?: number // 设备编号
|
||||
identifier?: string // 物模型标识符
|
||||
operator?: string // 操作符
|
||||
value?: string // 参数值
|
||||
cronExpression?: string // CRON 表达式
|
||||
conditionGroups?: TriggerCondition[][] // 条件组(二维数组)
|
||||
}
|
||||
|
||||
// 触发条件 DO 结构
|
||||
interface TriggerCondition {
|
||||
type: number // 条件类型:1-设备状态,2-设备属性,3-当前时间
|
||||
productId?: number // 产品编号
|
||||
deviceId?: number // 设备编号
|
||||
identifier?: string // 标识符
|
||||
operator: string // 操作符
|
||||
param: string // 参数
|
||||
}
|
||||
|
||||
// 执行器 DO 结构
|
||||
interface Action {
|
||||
type: number // 执行类型
|
||||
productId?: number // 产品编号
|
||||
deviceId?: number // 设备编号
|
||||
identifier?: string // 物模型标识符(服务调用时使用)
|
||||
params?: string // 请求参数
|
||||
alertConfigId?: number // 告警配置编号
|
||||
}
|
||||
|
||||
// 表单验证规则类型
|
||||
interface ValidationRule {
|
||||
required?: boolean
|
||||
message?: string
|
||||
trigger?: string | string[]
|
||||
type?: string
|
||||
min?: number
|
||||
max?: number
|
||||
enum?: any[]
|
||||
}
|
||||
|
||||
interface FormValidationRules {
|
||||
[key: string]: ValidationRule[]
|
||||
}
|
||||
|
||||
// 表单数据类型别名
|
||||
export type TriggerFormData = Trigger
|
||||
|
||||
// TODO @puhui999:这个文件,目标最终没有哈,和别的模块一致;
|
||||
|
||||
export { IotSceneRule, Trigger, TriggerCondition, Action, ValidationRule, FormValidationRules }
|
||||
|
|
@ -40,7 +40,7 @@ export interface ThingModelService {
|
|||
}
|
||||
|
||||
/** dataSpecs 数值型数据结构 */
|
||||
export interface DataSpecsNumberDataVO {
|
||||
export interface DataSpecsNumberData {
|
||||
dataType: 'int' | 'float' | 'double' // 数据类型,取值为 INT、FLOAT 或 DOUBLE
|
||||
max: string // 最大值,必须与 dataType 设置一致,且为 STRING 类型
|
||||
min: string // 最小值,必须与 dataType 设置一致,且为 STRING 类型
|
||||
|
|
@ -52,13 +52,114 @@ export interface DataSpecsNumberDataVO {
|
|||
}
|
||||
|
||||
/** dataSpecs 枚举型数据结构 */
|
||||
export interface DataSpecsEnumOrBoolDataVO {
|
||||
export interface DataSpecsEnumOrBoolData {
|
||||
dataType: 'enum' | 'bool'
|
||||
defaultValue?: string // 默认值,可选
|
||||
name: string // 枚举项的名称
|
||||
value: number | undefined // 枚举值
|
||||
}
|
||||
|
||||
/** 物模型TSL响应数据结构 */
|
||||
export interface IotThingModelTSLResp {
|
||||
productId: number
|
||||
productKey: string
|
||||
properties: ThingModelProperty[]
|
||||
events: ThingModelEvent[]
|
||||
services: ThingModelService[]
|
||||
}
|
||||
|
||||
/** 物模型属性 */
|
||||
export interface ThingModelProperty {
|
||||
identifier: string
|
||||
name: string
|
||||
accessMode: string
|
||||
required?: boolean
|
||||
dataType: string
|
||||
description?: string
|
||||
dataSpecs?: ThingModelProperty
|
||||
dataSpecsList?: ThingModelProperty[]
|
||||
}
|
||||
|
||||
/** 物模型事件 */
|
||||
export interface ThingModelEvent {
|
||||
identifier: string
|
||||
name: string
|
||||
required?: boolean
|
||||
type: string
|
||||
description?: string
|
||||
outputParams?: ThingModelParam[]
|
||||
method?: string
|
||||
}
|
||||
|
||||
/** 物模型服务 */
|
||||
export interface ThingModelService {
|
||||
identifier: string
|
||||
name: string
|
||||
required?: boolean
|
||||
callType: string
|
||||
description?: string
|
||||
inputParams?: ThingModelParam[]
|
||||
outputParams?: ThingModelParam[]
|
||||
method?: string
|
||||
}
|
||||
|
||||
/** 物模型参数 */
|
||||
export interface ThingModelParam {
|
||||
identifier: string
|
||||
name: string
|
||||
direction: string
|
||||
paraOrder?: number
|
||||
dataType: string
|
||||
dataSpecs?: ThingModelProperty
|
||||
dataSpecsList?: ThingModelProperty[]
|
||||
}
|
||||
|
||||
/** 数值型数据规范 */
|
||||
export interface ThingModelNumericDataSpec {
|
||||
dataType: 'int' | 'float' | 'double'
|
||||
max: string
|
||||
min: string
|
||||
step: string
|
||||
precise?: string
|
||||
defaultValue?: string
|
||||
unit?: string
|
||||
unitName?: string
|
||||
}
|
||||
|
||||
/** 布尔/枚举型数据规范 */
|
||||
export interface ThingModelBoolOrEnumDataSpecs {
|
||||
dataType: 'bool' | 'enum'
|
||||
name: string
|
||||
value: number
|
||||
}
|
||||
|
||||
/** 文本/时间型数据规范 */
|
||||
export interface ThingModelDateOrTextDataSpecs {
|
||||
dataType: 'text' | 'date'
|
||||
length?: number
|
||||
defaultValue?: string
|
||||
}
|
||||
|
||||
/** 数组型数据规范 */
|
||||
export interface ThingModelArrayDataSpecs {
|
||||
dataType: 'array'
|
||||
size: number
|
||||
childDataType: string
|
||||
dataSpecsList?: ThingModelProperty[]
|
||||
}
|
||||
|
||||
/** 结构体型数据规范 */
|
||||
export interface ThingModelStructDataSpecs {
|
||||
dataType: 'struct'
|
||||
identifier: string
|
||||
name: string
|
||||
accessMode: string
|
||||
required?: boolean
|
||||
childDataType: string
|
||||
dataSpecs?: ThingModelProperty
|
||||
dataSpecsList?: ThingModelProperty[]
|
||||
}
|
||||
|
||||
// IoT 产品物模型 API
|
||||
export const ThingModelApi = {
|
||||
// 查询产品物模型分页
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import { useVModel } from '@vueuse/core'
|
|||
import BasicInfoSection from './sections/BasicInfoSection.vue'
|
||||
import TriggerSection from './sections/TriggerSection.vue'
|
||||
import ActionSection from './sections/ActionSection.vue'
|
||||
import { IotSceneRule } from '@/api/iot/rule/scene/scene.types'
|
||||
import { IotSceneRule } from '@/api/iot/rule/scene'
|
||||
import { RuleSceneApi } from '@/api/iot/rule/scene'
|
||||
import {
|
||||
IotRuleSceneTriggerTypeEnum,
|
||||
|
|
|
|||
|
|
@ -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 { TriggerCondition } from '@/api/iot/rule/scene/scene.types'
|
||||
import type { TriggerCondition } from '@/api/iot/rule/scene'
|
||||
import {
|
||||
IotRuleSceneTriggerConditionTypeEnum,
|
||||
IotRuleSceneTriggerConditionParameterOperatorEnum
|
||||
|
|
|
|||
|
|
@ -94,17 +94,18 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import { ConditionFormData, IotRuleSceneTriggerTimeOperatorEnum } from '@/views/iot/utils/constants'
|
||||
import { IotRuleSceneTriggerTimeOperatorEnum } from '@/views/iot/utils/constants'
|
||||
import type { TriggerCondition } from '@/api/iot/rule/scene'
|
||||
|
||||
/** 当前时间条件配置组件 */
|
||||
defineOptions({ name: 'CurrentTimeConditionConfig' })
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: ConditionFormData
|
||||
modelValue: TriggerCondition
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: ConditionFormData): void
|
||||
(e: 'update:modelValue', value: TriggerCondition): void
|
||||
(e: 'validate', result: { valid: boolean; message: string }): void
|
||||
}>()
|
||||
|
||||
|
|
@ -178,7 +179,7 @@ const needsSecondTimeInput = computed(() => {
|
|||
})
|
||||
|
||||
// 事件处理
|
||||
const updateConditionField = (field: keyof ConditionFormData, value: any) => {
|
||||
const updateConditionField = (field: keyof TriggerCondition, value: any) => {
|
||||
condition.value[field] = value
|
||||
updateValidationResult()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,8 @@ import { useVModel } from '@vueuse/core'
|
|||
import ProductSelector from '../selectors/ProductSelector.vue'
|
||||
import DeviceSelector from '../selectors/DeviceSelector.vue'
|
||||
import JsonParamsInput from '../inputs/JsonParamsInput.vue'
|
||||
import { Action, ThingModelProperty, ThingModelService } from '@/api/iot/rule/scene/scene.types'
|
||||
import type { Action } from '@/api/iot/rule/scene'
|
||||
import type { ThingModelProperty, ThingModelService } from '@/api/iot/thingmodel'
|
||||
import {
|
||||
IotRuleSceneActionTypeEnum,
|
||||
IoTThingModelAccessModeEnum
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@
|
|||
import { useVModel } from '@vueuse/core'
|
||||
import ProductSelector from '../selectors/ProductSelector.vue'
|
||||
import DeviceSelector from '../selectors/DeviceSelector.vue'
|
||||
import { TriggerCondition } from '@/api/iot/rule/scene/scene.types'
|
||||
import type { TriggerCondition } from '@/api/iot/rule/scene'
|
||||
|
||||
/** 设备状态条件配置组件 */
|
||||
defineOptions({ name: 'DeviceStatusConditionConfig' })
|
||||
|
|
|
|||
|
|
@ -27,49 +27,24 @@ import { useVModel } from '@vueuse/core'
|
|||
|
||||
import MainConditionConfig from './MainConditionConfig.vue'
|
||||
import ConditionGroupContainerConfig from './ConditionGroupContainerConfig.vue'
|
||||
import { TriggerFormData } from '@/api/iot/rule/scene/scene.types'
|
||||
import { IotRuleSceneTriggerTypeEnum as TriggerTypeEnum } from '@/views/iot/utils/constants'
|
||||
import type { Trigger } from '@/api/iot/rule/scene'
|
||||
|
||||
/** 设备触发配置组件 */
|
||||
defineOptions({ name: 'DeviceTriggerConfig' })
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: TriggerFormData
|
||||
modelValue: Trigger
|
||||
index: number
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: TriggerFormData): void
|
||||
(e: 'update:modelValue', value: Trigger): void
|
||||
(e: 'validate', value: { valid: boolean; message: string }): void
|
||||
(e: 'trigger-type-change', type: number): void
|
||||
}>()
|
||||
|
||||
const trigger = useVModel(props, 'modelValue', emit)
|
||||
|
||||
// 初始化主条件
|
||||
const initMainCondition = () => {
|
||||
// TODO @puhui999: 等到编辑回显时联调
|
||||
// if (!trigger.value.mainCondition) {
|
||||
// trigger.value = {
|
||||
// type: trigger.value.type, // 使用触发事件类型作为条件类型
|
||||
// productId: undefined,
|
||||
// deviceId: undefined,
|
||||
// identifier: '',
|
||||
// operator: '=',
|
||||
// param: ''
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// 监听触发器类型变化,自动初始化主条件
|
||||
watch(
|
||||
() => trigger.value.type,
|
||||
() => {
|
||||
initMainCondition()
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const handleTriggerTypeChange = (type: number) => {
|
||||
trigger.value.type = type
|
||||
emit('trigger-type-change', type)
|
||||
|
|
|
|||
|
|
@ -35,24 +35,24 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import MainConditionInnerConfig from './MainConditionInnerConfig.vue'
|
||||
import { TriggerFormData } from '@/api/iot/rule/scene/scene.types'
|
||||
import { IotRuleSceneTriggerConditionTypeEnum } from '@/views/iot/utils/constants'
|
||||
import { Trigger } from '@/api/iot/rule/scene'
|
||||
|
||||
/** 主条件配置组件 */
|
||||
defineOptions({ name: 'MainConditionConfig' })
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: TriggerFormData
|
||||
defineProps<{
|
||||
modelValue: Trigger
|
||||
triggerType: number
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: TriggerFormData): void
|
||||
(e: 'update:modelValue', value: Trigger): void
|
||||
(e: 'validate', result: { valid: boolean; message: string }): void
|
||||
(e: 'trigger-type-change', type: number): void
|
||||
}>()
|
||||
|
||||
// 事件处理
|
||||
const updateCondition = (condition: TriggerFormData) => {
|
||||
const updateCondition = (condition: Trigger) => {
|
||||
emit('update:modelValue', condition)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
<template>
|
||||
<div class="space-y-16px">
|
||||
<!-- 触发事件类型选择 -->
|
||||
<!-- TODO @puhui999:事件上报时,应该也是 json? -->
|
||||
<el-form-item label="触发事件类型" required>
|
||||
<el-select
|
||||
:model-value="triggerType"
|
||||
|
|
@ -189,7 +188,7 @@ import OperatorSelector from '../selectors/OperatorSelector.vue'
|
|||
import ValueInput from '../inputs/ValueInput.vue'
|
||||
import JsonParamsInput from '../inputs/JsonParamsInput.vue'
|
||||
|
||||
import { TriggerFormData } from '@/api/iot/rule/scene/scene.types'
|
||||
import type { Trigger } from '@/api/iot/rule/scene'
|
||||
import { IotRuleSceneTriggerTypeEnum, getTriggerTypeOptions } from '@/views/iot/utils/constants'
|
||||
import { useVModel } from '@vueuse/core'
|
||||
|
||||
|
|
@ -197,12 +196,12 @@ import { useVModel } from '@vueuse/core'
|
|||
defineOptions({ name: 'MainConditionInnerConfig' })
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: TriggerFormData
|
||||
modelValue: Trigger
|
||||
triggerType: number
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: TriggerFormData): void
|
||||
(e: 'update:modelValue', value: Trigger): void
|
||||
(e: 'validate', result: { valid: boolean; message: string }): void
|
||||
(e: 'trigger-type-change', value: number): void
|
||||
}>()
|
||||
|
|
@ -278,7 +277,7 @@ const getTriggerTypeText = (type: number) => {
|
|||
const triggerTypeOptions = getTriggerTypeOptions()
|
||||
|
||||
// 事件处理
|
||||
const updateConditionField = (field: keyof TriggerFormData, value: any) => {
|
||||
const updateConditionField = (field: keyof Trigger, value: any) => {
|
||||
;(condition.value as any)[field] = value
|
||||
updateValidationResult()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@
|
|||
import { nextTick } from 'vue'
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import ConditionConfig from './ConditionConfig.vue'
|
||||
import { TriggerCondition } from '@/api/iot/rule/scene/scene.types'
|
||||
import type { TriggerCondition } from '@/api/iot/rule/scene'
|
||||
import {
|
||||
IotRuleSceneTriggerConditionTypeEnum,
|
||||
IotRuleSceneTriggerConditionParameterOperatorEnum
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ import { useVModel } from '@vueuse/core'
|
|||
import ActionTypeSelector from '../selectors/ActionTypeSelector.vue'
|
||||
import DeviceControlConfig from '../configs/DeviceControlConfig.vue'
|
||||
import AlertConfig from '../configs/AlertConfig.vue'
|
||||
import { Action } from '@/api/iot/rule/scene/scene.types'
|
||||
import type { Action } from '@/api/iot/rule/scene'
|
||||
import {
|
||||
IotRuleSceneActionTypeEnum as ActionTypeEnum,
|
||||
isDeviceAction,
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@
|
|||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { IotSceneRule } from '@/api/iot/rule/scene/scene.types'
|
||||
import type { IotSceneRule } from '@/api/iot/rule/scene'
|
||||
|
||||
/** 基础信息配置组件 */
|
||||
defineOptions({ name: 'BasicInfoSection' })
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@
|
|||
import { useVModel } from '@vueuse/core'
|
||||
import DeviceTriggerConfig from '../configs/DeviceTriggerConfig.vue'
|
||||
import TimerTriggerConfig from '../configs/TimerTriggerConfig.vue'
|
||||
import { TriggerFormData } from '@/api/iot/rule/scene/scene.types'
|
||||
import type { Trigger } from '@/api/iot/rule/scene'
|
||||
import {
|
||||
getTriggerTypeOptions,
|
||||
IotRuleSceneTriggerTypeEnum as TriggerTypeEnum,
|
||||
|
|
@ -108,11 +108,11 @@ import {
|
|||
defineOptions({ name: 'TriggerSection' })
|
||||
|
||||
const props = defineProps<{
|
||||
triggers: TriggerFormData[]
|
||||
triggers: Trigger[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:triggers', value: TriggerFormData[]): void
|
||||
(e: 'update:triggers', value: Trigger[]): void
|
||||
}>()
|
||||
|
||||
const triggers = useVModel(props, 'triggers', emit)
|
||||
|
|
@ -136,7 +136,7 @@ const getTriggerTagType = (type: number): string => {
|
|||
|
||||
// 事件处理函数
|
||||
const addTrigger = () => {
|
||||
const newTrigger: TriggerFormData = {
|
||||
const newTrigger: Trigger = {
|
||||
type: TriggerTypeEnum.DEVICE_STATE_UPDATE,
|
||||
productId: undefined,
|
||||
deviceId: undefined,
|
||||
|
|
@ -160,7 +160,7 @@ const updateTriggerType = (index: number, type: number) => {
|
|||
onTriggerTypeChange(index, type)
|
||||
}
|
||||
|
||||
const updateTriggerDeviceConfig = (index: number, newTrigger: TriggerFormData) => {
|
||||
const updateTriggerDeviceConfig = (index: number, newTrigger: Trigger) => {
|
||||
triggers.value[index] = newTrigger
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,317 +0,0 @@
|
|||
<!-- 产品设备选择器组件 -->
|
||||
<template>
|
||||
<div class="product-device-selector">
|
||||
<el-row :gutter="16">
|
||||
<!-- 产品选择 -->
|
||||
<el-col :span="12">
|
||||
<el-form-item label="选择产品" required>
|
||||
<el-select
|
||||
v-model="localProductId"
|
||||
placeholder="请选择产品"
|
||||
filterable
|
||||
clearable
|
||||
@change="handleProductChange"
|
||||
class="w-full"
|
||||
:loading="productLoading"
|
||||
>
|
||||
<el-option
|
||||
v-for="product in productList"
|
||||
:key="product.id"
|
||||
:label="product.name"
|
||||
:value="product.id"
|
||||
>
|
||||
<div class="flex items-center justify-between w-full py-4px">
|
||||
<div class="flex-1">
|
||||
<div class="text-14px font-500 text-[var(--el-text-color-primary)] mb-2px">
|
||||
{{ product.name }}
|
||||
</div>
|
||||
<div class="text-12px text-[var(--el-text-color-secondary)]">
|
||||
{{ product.productKey }}
|
||||
</div>
|
||||
</div>
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="product.status" />
|
||||
</div>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<!-- 设备选择模式 -->
|
||||
<el-col :span="12">
|
||||
<el-form-item label="设备选择模式" required>
|
||||
<el-radio-group
|
||||
v-model="deviceSelectionMode"
|
||||
@change="handleDeviceSelectionModeChange"
|
||||
:disabled="!localProductId"
|
||||
>
|
||||
<el-radio value="all">全部设备</el-radio>
|
||||
<el-radio value="specific">选择设备</el-radio>
|
||||
</el-radio-group>
|
||||
<div
|
||||
v-if="!localProductId"
|
||||
class="text-12px text-[var(--el-text-color-placeholder)] mt-4px"
|
||||
>
|
||||
请先选择产品
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 具体设备选择 -->
|
||||
<el-row v-if="deviceSelectionMode === 'specific'" :gutter="16">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="选择设备" required>
|
||||
<el-select
|
||||
v-model="localDeviceId"
|
||||
:placeholder="localProductId ? '请选择设备' : '请先选择产品'"
|
||||
filterable
|
||||
clearable
|
||||
@change="handleDeviceChange"
|
||||
class="w-full"
|
||||
:loading="deviceLoading"
|
||||
:disabled="!localProductId"
|
||||
>
|
||||
<el-option
|
||||
v-for="device in deviceList"
|
||||
:key="device.id"
|
||||
:label="device.deviceName"
|
||||
:value="device.id"
|
||||
>
|
||||
<div class="flex items-center justify-between w-full py-4px">
|
||||
<div class="flex-1">
|
||||
<div class="text-14px font-500 text-[var(--el-text-color-primary)] mb-2px">
|
||||
{{ device.deviceName }}
|
||||
</div>
|
||||
<div class="text-12px text-[var(--el-text-color-secondary)]">
|
||||
{{ device.nickname || '无备注' }}
|
||||
</div>
|
||||
</div>
|
||||
<el-tag size="small" :type="getDeviceStatusTag(device.state)">
|
||||
{{ getDeviceStatusText(device.state) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 选择结果展示 -->
|
||||
<div
|
||||
v-if="localProductId && localDeviceId !== undefined"
|
||||
class="mt-16px p-12px bg-[var(--el-fill-color-light)] rounded-6px border border-[var(--el-border-color-lighter)]"
|
||||
>
|
||||
<div class="flex items-center gap-6px mb-8px">
|
||||
<Icon icon="ep:check" class="text-[var(--el-color-success)] text-16px" />
|
||||
<span class="text-14px font-500 text-[var(--el-text-color-primary)]">已选择设备</span>
|
||||
</div>
|
||||
<div class="flex flex-col gap-6px ml-22px">
|
||||
<div class="flex items-center gap-8px">
|
||||
<span class="text-12px text-[var(--el-text-color-secondary)] min-w-40px">产品:</span>
|
||||
<span class="text-12px text-[var(--el-text-color-primary)] font-500">
|
||||
{{ selectedProduct?.name }}
|
||||
</span>
|
||||
<el-tag size="small" type="primary">{{ selectedProduct?.productKey }}</el-tag>
|
||||
</div>
|
||||
<div class="flex items-center gap-8px">
|
||||
<span class="text-12px text-[var(--el-text-color-secondary)] min-w-40px">设备:</span>
|
||||
<span
|
||||
v-if="deviceSelectionMode === 'all'"
|
||||
class="text-12px text-[var(--el-text-color-primary)] font-500"
|
||||
>全部设备</span
|
||||
>
|
||||
<span v-else class="text-12px text-[var(--el-text-color-primary)] font-500">
|
||||
{{ selectedDevice?.deviceName }}
|
||||
</span>
|
||||
<el-tag v-if="deviceSelectionMode === 'all'" size="small" type="warning"> 全部</el-tag>
|
||||
<el-tag v-else size="small" :type="getDeviceStatusTag(selectedDevice?.state)">
|
||||
{{ getDeviceStatusText(selectedDevice?.state) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import { ProductApi } from '@/api/iot/product/product'
|
||||
import { DeviceApi } from '@/api/iot/device/device'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
|
||||
/** 产品设备选择器组件 */
|
||||
defineOptions({ name: 'ProductDeviceSelector' })
|
||||
|
||||
interface Props {
|
||||
productId?: number
|
||||
deviceId?: number
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:productId', value?: number): void
|
||||
(e: 'update:deviceId', value?: number): void
|
||||
(e: 'change', value: { productId?: number; deviceId?: number }): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const localProductId = useVModel(props, 'productId', emit)
|
||||
const localDeviceId = useVModel(props, 'deviceId', emit)
|
||||
|
||||
// 设备选择模式
|
||||
// 默认选择具体设备,这样用户可以看到设备选择器
|
||||
const deviceSelectionMode = ref<'specific' | 'all'>('specific')
|
||||
|
||||
// 数据状态
|
||||
const productLoading = ref(false)
|
||||
const deviceLoading = ref(false)
|
||||
const productList = ref<any[]>([])
|
||||
const deviceList = ref<any[]>([])
|
||||
|
||||
// 计算属性
|
||||
const selectedProduct = computed(() => {
|
||||
return productList.value.find((p) => p.id === localProductId.value)
|
||||
})
|
||||
|
||||
const selectedDevice = computed(() => {
|
||||
return deviceList.value.find((d) => d.id === localDeviceId.value)
|
||||
})
|
||||
|
||||
// TODO @puhui999:字典下;
|
||||
// 设备状态映射
|
||||
const getDeviceStatusText = (state?: number) => {
|
||||
switch (state) {
|
||||
case 0:
|
||||
return '未激活'
|
||||
case 1:
|
||||
return '在线'
|
||||
case 2:
|
||||
return '离线'
|
||||
default:
|
||||
return '未知'
|
||||
}
|
||||
}
|
||||
|
||||
const getDeviceStatusTag = (state?: number) => {
|
||||
switch (state) {
|
||||
case 0:
|
||||
return 'info'
|
||||
case 1:
|
||||
return 'success'
|
||||
case 2:
|
||||
return 'danger'
|
||||
default:
|
||||
return 'info'
|
||||
}
|
||||
}
|
||||
|
||||
// TODO @puhui999:注释风格哈
|
||||
// 事件处理
|
||||
const handleProductChange = async (productId?: number) => {
|
||||
localProductId.value = productId
|
||||
localDeviceId.value = undefined
|
||||
deviceList.value = []
|
||||
if (productId) {
|
||||
await getDeviceList(productId)
|
||||
}
|
||||
emitChange()
|
||||
}
|
||||
|
||||
const handleDeviceChange = (deviceId?: number) => {
|
||||
localDeviceId.value = deviceId
|
||||
emitChange()
|
||||
}
|
||||
|
||||
const handleDeviceSelectionModeChange = (mode: 'specific' | 'all') => {
|
||||
deviceSelectionMode.value = mode
|
||||
if (mode === 'all') {
|
||||
// 全部设备时,设备 ID 设为 0
|
||||
localDeviceId.value = 0
|
||||
} else {
|
||||
// 选择设备时,清空设备 ID
|
||||
localDeviceId.value = undefined
|
||||
}
|
||||
emitChange()
|
||||
}
|
||||
|
||||
const emitChange = () => {
|
||||
emit('change', {
|
||||
productId: localProductId.value,
|
||||
deviceId: localDeviceId.value
|
||||
})
|
||||
}
|
||||
|
||||
// API 调用
|
||||
const getProductList = async () => {
|
||||
productLoading.value = true
|
||||
try {
|
||||
const data = await ProductApi.getSimpleProductList()
|
||||
productList.value = data || []
|
||||
} catch (error) {
|
||||
console.error('获取产品列表失败:', error)
|
||||
// 模拟数据
|
||||
// TODO @puhui999:移除下,不太合理
|
||||
productList.value = [
|
||||
{ id: 1, name: '智能温度传感器', productKey: 'temp_sensor_001', status: 0 },
|
||||
{ id: 2, name: '智能空调控制器', productKey: 'ac_controller_001', status: 0 },
|
||||
{ id: 3, name: '智能门锁', productKey: 'smart_lock_001', status: 0 }
|
||||
]
|
||||
} finally {
|
||||
productLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const getDeviceList = async (productId: number) => {
|
||||
deviceLoading.value = true
|
||||
try {
|
||||
const data = await DeviceApi.getSimpleDeviceList(undefined, productId)
|
||||
deviceList.value = data || []
|
||||
} catch (error) {
|
||||
console.error('获取设备列表失败:', error)
|
||||
// 模拟数据
|
||||
// TODO @puhui999:移除下,不太合理
|
||||
deviceList.value = [
|
||||
{ id: 1, deviceName: 'sensor_001', nickname: '客厅温度传感器', state: 1, productId },
|
||||
{ id: 2, deviceName: 'sensor_002', nickname: '卧室温度传感器', state: 2, productId },
|
||||
{ id: 3, deviceName: 'sensor_003', nickname: '厨房温度传感器', state: 1, productId }
|
||||
]
|
||||
} finally {
|
||||
deviceLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
await getProductList()
|
||||
|
||||
// 根据初始设备 ID 设置选择模式
|
||||
if (localDeviceId.value === 0) {
|
||||
deviceSelectionMode.value = 'all'
|
||||
} else if (localDeviceId.value) {
|
||||
deviceSelectionMode.value = 'specific'
|
||||
}
|
||||
|
||||
if (localProductId.value) {
|
||||
await getDeviceList(localProductId.value)
|
||||
}
|
||||
})
|
||||
|
||||
// 监听产品变化
|
||||
watch(
|
||||
() => localProductId.value,
|
||||
async (newProductId) => {
|
||||
if (newProductId && deviceList.value.length === 0) {
|
||||
await getDeviceList(newProductId)
|
||||
}
|
||||
}
|
||||
)
|
||||
// TODO @puhui999:是不是 unocss
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.el-select-dropdown__item) {
|
||||
height: auto;
|
||||
padding: 8px 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -160,12 +160,38 @@
|
|||
import { useVModel } from '@vueuse/core'
|
||||
import { InfoFilled } from '@element-plus/icons-vue'
|
||||
import { IotRuleSceneTriggerTypeEnum, IoTThingModelTypeEnum } from '@/views/iot/utils/constants'
|
||||
import type {
|
||||
IotThingModelTSLResp,
|
||||
ThingModelEvent,
|
||||
ThingModelParam,
|
||||
ThingModelProperty,
|
||||
ThingModelService
|
||||
} from '@/api/iot/thingmodel'
|
||||
import { ThingModelApi } from '@/api/iot/thingmodel'
|
||||
import type { IotThingModelTSLRespVO, PropertySelectorItem } from '@/api/iot/rule/scene/scene.types'
|
||||
|
||||
/** 属性选择器组件 */
|
||||
defineOptions({ name: 'PropertySelector' })
|
||||
|
||||
/** 属性选择器内部使用的统一数据结构 */
|
||||
export interface PropertySelectorItem {
|
||||
identifier: string
|
||||
name: string
|
||||
description?: string
|
||||
dataType: string
|
||||
type: number // IoTThingModelTypeEnum
|
||||
accessMode?: string
|
||||
required?: boolean
|
||||
unit?: string
|
||||
range?: string
|
||||
eventType?: string
|
||||
callType?: string
|
||||
inputParams?: ThingModelParam[]
|
||||
outputParams?: ThingModelParam[]
|
||||
property?: ThingModelProperty
|
||||
event?: ThingModelEvent
|
||||
service?: ThingModelService
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue?: string
|
||||
triggerType: number
|
||||
|
|
@ -183,7 +209,7 @@ const localValue = useVModel(props, 'modelValue', emit)
|
|||
// 状态
|
||||
const loading = ref(false)
|
||||
const propertyList = ref<PropertySelectorItem[]>([])
|
||||
const thingModelTSL = ref<IotThingModelTSLRespVO | null>(null)
|
||||
const thingModelTSL = ref<IotThingModelTSLResp | null>(null)
|
||||
|
||||
// 计算属性
|
||||
const propertyGroups = computed(() => {
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@
|
|||
import { DICT_TYPE, getIntDictOptions, getDictLabel } from '@/utils/dict'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import RuleSceneForm from './form/RuleSceneForm.vue'
|
||||
import { IotSceneRule } from '@/api/iot/rule/scene/scene.types'
|
||||
import { IotSceneRule } from '@/api/iot/rule/scene'
|
||||
import { RuleSceneApi } from '@/api/iot/rule/scene'
|
||||
import {
|
||||
IotRuleSceneTriggerTypeEnum,
|
||||
|
|
|
|||
|
|
@ -46,14 +46,14 @@
|
|||
import { useVModel } from '@vueuse/core'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
import { IoTDataSpecsDataTypeEnum } from '@/views/iot/utils/constants'
|
||||
import { DataSpecsEnumOrBoolDataVO } from '@/api/iot/thingmodel'
|
||||
import { DataSpecsEnumOrBoolData } from '@/api/iot/thingmodel'
|
||||
|
||||
/** 枚举型的 dataSpecs 配置组件 */
|
||||
defineOptions({ name: 'ThingModelEnumDataSpecs' })
|
||||
|
||||
const props = defineProps<{ modelValue: any }>()
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
const dataSpecsList = useVModel(props, 'modelValue', emits) as Ref<DataSpecsEnumOrBoolDataVO[]>
|
||||
const dataSpecsList = useVModel(props, 'modelValue', emits) as Ref<DataSpecsEnumOrBoolData[]>
|
||||
const message = useMessage()
|
||||
|
||||
/** 添加枚举项 */
|
||||
|
|
|
|||
|
|
@ -60,14 +60,14 @@
|
|||
<script lang="ts" setup>
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
|
||||
import { DataSpecsNumberDataVO } from '@/api/iot/thingmodel'
|
||||
import { DataSpecsNumberData } from '@/api/iot/thingmodel'
|
||||
|
||||
/** 数值型的 dataSpecs 配置组件 */
|
||||
defineOptions({ name: 'ThingModelNumberDataSpecs' })
|
||||
|
||||
const props = defineProps<{ modelValue: any }>()
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
const dataSpecs = useVModel(props, 'modelValue', emits) as Ref<DataSpecsNumberDataVO>
|
||||
const dataSpecs = useVModel(props, 'modelValue', emits) as Ref<DataSpecsNumberData>
|
||||
|
||||
/** 单位发生变化时触发 */
|
||||
const unitChange = (UnitSpecs: string) => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue