perf: 【IoT 物联网】场景联动类型归纳优化,移除多余文件

pull/808/head
puhui999 2025-08-07 14:29:59 +08:00
parent 9f3eb14a0f
commit a8adda7dc5
20 changed files with 209 additions and 584 deletions

View File

@ -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 = {

View File

@ -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 }

View File

@ -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 = {
// 查询产品物模型分页

View File

@ -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,

View File

@ -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

View File

@ -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()
}

View File

@ -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

View File

@ -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' })

View File

@ -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)

View File

@ -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)
}

View File

@ -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()
}

View File

@ -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

View File

@ -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,

View File

@ -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' })

View File

@ -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
}

View File

@ -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>

View File

@ -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(() => {

View File

@ -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,

View File

@ -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()
/** 添加枚举项 */

View File

@ -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) => {