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

388 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- 设备控制配置组件 -->
<template>
<div class="flex flex-col gap-16px">
<!-- 产品和设备选择 - 与触发器保持一致的分离式选择器 -->
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="产品" required>
<ProductSelector v-model="action.productId" @change="handleProductChange" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设备" required>
<DeviceSelector
v-model="action.deviceId"
:product-id="action.productId"
@change="handleDeviceChange"
/>
</el-form-item>
</el-col>
</el-row>
<!-- 服务选择 - 服务调用类型时显示 -->
<div v-if="action.productId && isServiceInvokeAction" class="space-y-16px">
<el-form-item label="服务" required>
<el-select
v-model="action.identifier"
placeholder="请选择服务"
filterable
clearable
class="w-full"
:loading="loadingServices"
@change="handleServiceChange"
>
<el-option
v-for="service in serviceList"
:key="service.identifier"
:label="service.name"
:value="service.identifier"
>
<div class="flex items-center justify-between">
<span>{{ service.name }}</span>
<el-tag :type="service.callType === 'sync' ? 'primary' : 'success'" size="small">
{{ service.callType === 'sync' ? '同步' : '异步' }}
</el-tag>
</div>
</el-option>
</el-select>
</el-form-item>
<!-- 服务参数配置 -->
<div v-if="action.identifier" class="space-y-16px">
<el-form-item label="服务参数" required>
<JsonParamsInput
v-model="paramsValue"
type="service"
:config="{ service: selectedService }"
placeholder="请输入JSON格式的服务参数"
@validate="handleParamsValidate"
/>
</el-form-item>
</div>
</div>
<!-- 控制参数配置 - 属性设置类型时显示 -->
<div v-if="action.productId && isPropertySetAction" class="space-y-16px">
<!-- 参数配置 -->
<el-form-item label="参数" required>
<JsonParamsInput
v-model="paramsValue"
type="property"
:config="{ properties: thingModelProperties }"
placeholder="请输入JSON格式的控制参数"
@validate="handleParamsValidate"
/>
</el-form-item>
</div>
</div>
</template>
<script setup lang="ts">
import { useVModel } from '@vueuse/core'
import ProductSelector from '../selectors/ProductSelector.vue'
import DeviceSelector from '../selectors/DeviceSelector.vue'
import JsonParamsInput from '../inputs/JsonParamsInput.vue'
import type { Action } from '@/api/iot/rule/scene'
import type { ThingModelProperty, ThingModelService } from '@/api/iot/thingmodel'
import {
IotRuleSceneActionTypeEnum,
IoTThingModelAccessModeEnum
} from '@/views/iot/utils/constants'
import { ThingModelApi } from '@/api/iot/thingmodel'
/** 设备控制配置组件 */
defineOptions({ name: 'DeviceControlConfig' })
const props = defineProps<{
modelValue: Action
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: Action): void
}>()
const action = useVModel(props, 'modelValue', emit)
// 简化后的状态变量
const thingModelProperties = ref<ThingModelProperty[]>([]) // 物模型属性列表
const loadingThingModel = ref(false) // 物模型加载状态
const selectedService = ref<ThingModelService | null>(null) // 选中的服务对象
const serviceList = ref<ThingModelService[]>([]) // 服务列表
const loadingServices = ref(false) // 服务加载状态
// 参数值的计算属性,用于双向绑定
const paramsValue = computed({
get: () => {
// 如果 params 是对象,转换为 JSON 字符串(兼容旧数据)
if (action.value.params && typeof action.value.params === 'object') {
return JSON.stringify(action.value.params, null, 2)
}
// 如果 params 已经是字符串,直接返回
return action.value.params || ''
},
set: (value: string) => {
// 直接保存为 JSON 字符串,不进行解析转换
action.value.params = value.trim() || ''
}
})
// 参数验证处理
const handleParamsValidate = (result: { valid: boolean; message: string }) => {
// 可以在这里处理验证结果,比如显示错误信息
console.log('参数验证结果:', result)
}
const isPropertySetAction = computed(() => {
// 是否为属性设置类型
return action.value.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET
})
const isServiceInvokeAction = computed(() => {
// 是否为服务调用类型
return action.value.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE
})
/**
* 处理产品变化事件
* @param productId 产品ID
*/
const handleProductChange = (productId?: number) => {
// 当产品变化时,清空设备选择和参数配置
if (action.value.productId !== productId) {
action.value.deviceId = undefined
action.value.identifier = undefined // 清空服务标识符
action.value.params = '' // 清空参数,保存为空字符串
selectedService.value = null // 清空选中的服务
serviceList.value = [] // 清空服务列表
}
// 加载新产品的物模型属性或服务列表
if (productId) {
if (isPropertySetAction.value) {
loadThingModelProperties(productId)
} else if (isServiceInvokeAction.value) {
loadServiceList(productId)
}
}
}
/**
* 处理设备变化事件
* @param deviceId 设备ID
*/
const handleDeviceChange = (deviceId?: number) => {
// 当设备变化时,清空参数配置
if (action.value.deviceId !== deviceId) {
action.value.params = '' // 清空参数,保存为空字符串
}
}
/**
* 处理服务变化事件
* @param serviceIdentifier 服务标识符
*/
const handleServiceChange = (serviceIdentifier?: string) => {
// 根据服务标识符找到对应的服务对象
const service = serviceList.value.find((s) => s.identifier === serviceIdentifier) || null
selectedService.value = service
// 当服务变化时,清空参数配置
action.value.params = ''
// 如果选择了服务且有输入参数,生成默认参数结构
if (service && service.inputParams && service.inputParams.length > 0) {
const defaultParams = {}
service.inputParams.forEach((param) => {
defaultParams[param.identifier] = getDefaultValueForParam(param)
})
// 将默认参数转换为 JSON 字符串保存
action.value.params = JSON.stringify(defaultParams, null, 2)
}
}
/**
* 获取物模型TSL数据
* @param productId 产品ID
* @returns 物模型TSL数据
*/
const getThingModelTSL = async (productId: number) => {
if (!productId) return null
try {
return await ThingModelApi.getThingModelTSLByProductId(productId)
} catch (error) {
console.error('获取物模型TSL数据失败:', error)
return null
}
}
/**
* 加载物模型属性(可写属性)
* @param productId 产品ID
*/
const loadThingModelProperties = async (productId: number) => {
if (!productId) {
thingModelProperties.value = []
return
}
try {
loadingThingModel.value = true
const tslData = await getThingModelTSL(productId)
if (!tslData?.properties) {
thingModelProperties.value = []
return
}
// 过滤出可写的属性accessMode 包含 'w'
thingModelProperties.value = tslData.properties.filter(
(property: ThingModelProperty) =>
property.accessMode &&
(property.accessMode === IoTThingModelAccessModeEnum.READ_WRITE.value ||
property.accessMode === IoTThingModelAccessModeEnum.WRITE_ONLY.value)
)
} catch (error) {
console.error('加载物模型属性失败:', error)
thingModelProperties.value = []
} finally {
loadingThingModel.value = false
}
}
/**
* 加载服务列表
* @param productId 产品ID
*/
const loadServiceList = async (productId: number) => {
if (!productId) {
serviceList.value = []
return
}
try {
loadingServices.value = true
const tslData = await getThingModelTSL(productId)
if (!tslData?.services) {
serviceList.value = []
return
}
serviceList.value = tslData.services
} catch (error) {
console.error('加载服务列表失败:', error)
serviceList.value = []
} finally {
loadingServices.value = false
}
}
/**
* 从TSL加载服务信息用于编辑模式回显
* @param productId 产品ID
* @param serviceIdentifier 服务标识符
*/
const loadServiceFromTSL = async (productId: number, serviceIdentifier: string) => {
// 先加载服务列表
await loadServiceList(productId)
// 然后设置选中的服务
const service = serviceList.value.find((s: any) => s.identifier === serviceIdentifier)
if (service) {
selectedService.value = service
}
}
/**
* 根据参数类型获取默认值
* @param param 参数对象
* @returns 默认值
*/
const getDefaultValueForParam = (param: any) => {
switch (param.dataType) {
case 'int':
return 0
case 'float':
case 'double':
return 0.0
case 'bool':
return false
case 'text':
return ''
case 'enum':
// 如果有枚举值,使用第一个
if (param.dataSpecs?.dataSpecsList && param.dataSpecs.dataSpecsList.length > 0) {
return param.dataSpecs.dataSpecsList[0].value
}
return ''
default:
return ''
}
}
// 防止重复初始化的标志
const isInitialized = ref(false)
/**
* 初始化组件数据
*/
const initializeComponent = async () => {
if (isInitialized.value) return
const currentAction = action.value
if (!currentAction) return
// 如果已经选择了产品且是属性设置类型,加载物模型
if (currentAction.productId && isPropertySetAction.value) {
await loadThingModelProperties(currentAction.productId)
}
// 如果是服务调用类型且已有标识符,初始化服务选择
if (currentAction.productId && isServiceInvokeAction.value && currentAction.identifier) {
// 加载物模型TSL以获取服务信息
await loadServiceFromTSL(currentAction.productId, currentAction.identifier)
}
isInitialized.value = true
}
/**
* 组件初始化
*/
onMounted(() => {
initializeComponent()
})
// 只监听关键字段的变化,避免深度监听导致的性能问题
watch(
() => [action.value.productId, action.value.type, action.value.identifier],
async ([newProductId, , newIdentifier], [oldProductId, , oldIdentifier]) => {
// 避免初始化时的重复调用
if (!isInitialized.value) return
// 产品变化时重新加载数据
if (newProductId !== oldProductId) {
if (newProductId && isPropertySetAction.value) {
await loadThingModelProperties(newProductId as number)
} else if (newProductId && isServiceInvokeAction.value) {
await loadServiceList(newProductId as number)
}
}
// 服务标识符变化时更新选中的服务
if (
newIdentifier !== oldIdentifier &&
newProductId &&
isServiceInvokeAction.value &&
newIdentifier
) {
const service = serviceList.value.find((s: any) => s.identifier === newIdentifier)
if (service) {
selectedService.value = service
}
}
}
)
</script>