From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目
---
src/views/iot/rule/scene/form/configs/DeviceControlConfig.vue | 376 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 376 insertions(+), 0 deletions(-)
diff --git a/src/views/iot/rule/scene/form/configs/DeviceControlConfig.vue b/src/views/iot/rule/scene/form/configs/DeviceControlConfig.vue
new file mode 100644
index 0000000..2cc89c9
--- /dev/null
+++ b/src/views/iot/rule/scene/form/configs/DeviceControlConfig.vue
@@ -0,0 +1,376 @@
+<!-- 璁惧鎺у埗閰嶇疆缁勪欢 -->
+<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 } as any"
+ placeholder="璇疯緭鍏� JSON 鏍煎紡鐨勬湇鍔″弬鏁�"
+ />
+ </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 鏍煎紡鐨勬帶鍒跺弬鏁�"
+ />
+ </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,
+ IoTDataSpecsDataTypeEnum
+} 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 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)
+ }
+}
+
+/**
+ * 鑾峰彇鐗╂ā鍨婽SL鏁版嵁
+ * @param productId 浜у搧ID
+ * @returns 鐗╂ā鍨婽SL鏁版嵁
+ */
+const getThingModelTSL = async (productId: number) => {
+ if (!productId) return null
+
+ try {
+ return await ThingModelApi.getThingModelTSLByProductId(productId)
+ } catch (error) {
+ console.error('鑾峰彇鐗╂ā鍨婽SL鏁版嵁澶辫触:', 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
+ }
+}
+
+/**
+ * 浠嶵SL鍔犺浇鏈嶅姟淇℃伅锛堢敤浜庣紪杈戞ā寮忓洖鏄撅級
+ * @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 IoTDataSpecsDataTypeEnum.INT:
+ return 0
+ case IoTDataSpecsDataTypeEnum.FLOAT:
+ case IoTDataSpecsDataTypeEnum.DOUBLE:
+ return 0.0
+ case IoTDataSpecsDataTypeEnum.BOOL:
+ return false
+ case IoTDataSpecsDataTypeEnum.TEXT:
+ return ''
+ case IoTDataSpecsDataTypeEnum.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) {
+ // 鍔犺浇鐗╂ā鍨婽SL浠ヨ幏鍙栨湇鍔′俊鎭�
+ 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>
--
Gitblit v1.8.0