【功能完善】IoT: 场景联动执行器配置
parent
eadf26dc3b
commit
c06f7f9ebd
|
|
@ -22,7 +22,7 @@
|
|||
class="flex items-center mr-60px"
|
||||
>
|
||||
<span class="mr-10px">产品</span>
|
||||
<el-button type="primary" @click="productTableSelectRef?.open()" size="small" plain>
|
||||
<el-button type="primary" @click="handleSelectProduct" size="small" plain>
|
||||
{{ product ? product.name : '选择产品' }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
|
@ -31,8 +31,8 @@
|
|||
class="flex items-center mr-60px"
|
||||
>
|
||||
<span class="mr-10px">设备</span>
|
||||
<el-button type="primary" @click="openDeviceSelect" size="small" plain>
|
||||
{{ isEmpty(deviceList) ? '选择设备' : actionConfig.deviceControl?.deviceNames.join(',') }}
|
||||
<el-button type="primary" @click="handleSelectDevice" size="small" plain>
|
||||
{{ isEmpty(deviceList) ? '选择设备' : deviceList.map(d => d.deviceName).join(',') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 删除执行器 -->
|
||||
|
|
@ -47,6 +47,7 @@
|
|||
<DeviceControlAction
|
||||
v-if="actionConfig.type === IotRuleSceneActionTypeEnum.DEVICE_CONTROL"
|
||||
:model-value="actionConfig.deviceControl"
|
||||
:product-id="product?.id"
|
||||
@update:model-value="(val) => (actionConfig.deviceControl = val)"
|
||||
/>
|
||||
|
||||
|
|
@ -86,12 +87,13 @@ import AlertAction from './AlertAction.vue'
|
|||
import DataBridgeAction from './DataBridgeAction.vue'
|
||||
import { ProductVO } from '@/api/iot/product/product'
|
||||
import { DeviceVO } from '@/api/iot/device/device'
|
||||
import { ThingModelApi } from '@/api/iot/thingmodel'
|
||||
import {
|
||||
ActionAlert,
|
||||
ActionConfig,
|
||||
ActionDeviceControl,
|
||||
IotRuleSceneActionTypeEnum
|
||||
IotRuleSceneActionTypeEnum,
|
||||
IotDeviceMessageTypeEnum,
|
||||
IotDeviceMessageIdentifierEnum
|
||||
} from '@/api/iot/rule/scene/scene.types'
|
||||
|
||||
/** 场景联动之执行器组件 */
|
||||
|
|
@ -114,7 +116,13 @@ const initActionConfig = () => {
|
|||
actionConfig.value.type === IotRuleSceneActionTypeEnum.DEVICE_CONTROL &&
|
||||
!actionConfig.value.deviceControl
|
||||
) {
|
||||
actionConfig.value.deviceControl = {} as ActionDeviceControl
|
||||
actionConfig.value.deviceControl = {
|
||||
productKey: '',
|
||||
deviceNames: [],
|
||||
type: IotDeviceMessageTypeEnum.PROPERTY,
|
||||
identifier: IotDeviceMessageIdentifierEnum.PROPERTY_SET,
|
||||
data: {}
|
||||
} as ActionDeviceControl
|
||||
}
|
||||
|
||||
// 告警执行器初始化
|
||||
|
|
@ -146,22 +154,13 @@ const deviceTableSelectRef = ref<InstanceType<typeof DeviceTableSelect>>()
|
|||
const product = ref<ProductVO>()
|
||||
const deviceList = ref<DeviceVO[]>([])
|
||||
|
||||
/** 处理产品选择 */
|
||||
const handleProductSelect = (val: ProductVO) => {
|
||||
product.value = val
|
||||
actionConfig.value.deviceControl!.productKey = val.productKey
|
||||
deviceList.value = []
|
||||
getThingModelTSL()
|
||||
/** 处理选择产品 */
|
||||
const handleSelectProduct = () => {
|
||||
productTableSelectRef.value?.open()
|
||||
}
|
||||
|
||||
/** 处理设备选择 */
|
||||
const handleDeviceSelect = (val: DeviceVO[]) => {
|
||||
deviceList.value = val
|
||||
actionConfig.value.deviceControl!.deviceNames = val.map((item) => item.deviceName)
|
||||
}
|
||||
|
||||
/** 打开设备选择器 */
|
||||
const openDeviceSelect = () => {
|
||||
/** 处理选择设备 */
|
||||
const handleSelectDevice = () => {
|
||||
if (!product.value) {
|
||||
message.warning('请先选择一个产品')
|
||||
return
|
||||
|
|
@ -169,12 +168,24 @@ const openDeviceSelect = () => {
|
|||
deviceTableSelectRef.value?.open()
|
||||
}
|
||||
|
||||
/** 获取产品物模型 */
|
||||
const thingModelTSL = ref<any>()
|
||||
const getThingModelTSL = async () => {
|
||||
if (!product.value) {
|
||||
return
|
||||
/** 处理产品选择成功 */
|
||||
const handleProductSelect = (val: ProductVO) => {
|
||||
product.value = val
|
||||
if (actionConfig.value.deviceControl) {
|
||||
actionConfig.value.deviceControl.productKey = val.productKey
|
||||
}
|
||||
// 重置设备选择
|
||||
deviceList.value = []
|
||||
if (actionConfig.value.deviceControl) {
|
||||
actionConfig.value.deviceControl.deviceNames = []
|
||||
}
|
||||
}
|
||||
|
||||
/** 处理设备选择成功 */
|
||||
const handleDeviceSelect = (val: DeviceVO[]) => {
|
||||
deviceList.value = val
|
||||
if (actionConfig.value.deviceControl) {
|
||||
actionConfig.value.deviceControl.deviceNames = val.map((item) => item.deviceName)
|
||||
}
|
||||
thingModelTSL.value = await ThingModelApi.getThingModelTSLByProductId(product.value.id)
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
<template>
|
||||
<div class="device-action-control">
|
||||
<!-- 属性设置 -->
|
||||
<template v-if="messageType === IotDeviceMessageTypeEnum.PROPERTY">
|
||||
<div class="flex flex-wrap gap-10px">
|
||||
<div v-for="model in thingModels" :key="model.identifier" class="flex items-center mb-10px">
|
||||
<span class="mr-10px inline-block min-w-80px">{{ model.name }}</span>
|
||||
<!-- 根据属性类型渲染不同的输入控件 -->
|
||||
<PropertyValueInput
|
||||
:model-value="propertyData[model.identifier]"
|
||||
:data-specs="model.dataSpecs"
|
||||
@update:model-value="(val) => updatePropertyValue(model.identifier, val)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 服务调用 -->
|
||||
<template v-else-if="messageType === IotDeviceMessageTypeEnum.SERVICE">
|
||||
<div class="flex flex-wrap gap-10px">
|
||||
<template v-if="thingModels && thingModels.inputParams">
|
||||
<div
|
||||
v-for="param in thingModels.inputParams"
|
||||
:key="param.identifier"
|
||||
class="flex items-center mb-10px"
|
||||
>
|
||||
<span class="mr-10px inline-block min-w-80px">{{ param.name }}</span>
|
||||
<!-- 根据服务输入参数类型渲染不同的输入控件 -->
|
||||
<PropertyValueInput
|
||||
:model-value="serviceData[param.identifier]"
|
||||
:data-specs="param.dataSpecs"
|
||||
@update:model-value="(val) => updateServiceValue(param.identifier, val)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="text-gray-500">该服务没有输入参数</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import PropertyValueInput from './PropertyValueInput.vue'
|
||||
import { IotDeviceMessageTypeEnum } from '@/api/iot/rule/scene/scene.types'
|
||||
|
||||
/** 设备控制执行器的参数配置组件 */
|
||||
defineOptions({ name: 'DeviceActionControl' })
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: any
|
||||
thingModels: any
|
||||
messageType: string
|
||||
identifier: string
|
||||
}>()
|
||||
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
const actionData = useVModel(props, 'modelValue', emits)
|
||||
|
||||
// 属性数据
|
||||
const propertyData = computed({
|
||||
get: () => {
|
||||
return actionData.value || {}
|
||||
},
|
||||
set: (value) => {
|
||||
actionData.value = value
|
||||
}
|
||||
})
|
||||
|
||||
// 服务数据
|
||||
const serviceData = computed({
|
||||
get: () => {
|
||||
return actionData.value || {}
|
||||
},
|
||||
set: (value) => {
|
||||
actionData.value = value
|
||||
}
|
||||
})
|
||||
|
||||
// 更新属性值
|
||||
const updatePropertyValue = (identifier: string, value: any) => {
|
||||
const newData = { ...propertyData.value }
|
||||
newData[identifier] = value
|
||||
propertyData.value = newData
|
||||
}
|
||||
|
||||
// 更新服务参数值
|
||||
const updateServiceValue = (identifier: string, value: any) => {
|
||||
const newData = { ...serviceData.value }
|
||||
newData[identifier] = value
|
||||
serviceData.value = newData
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
@ -1,78 +1,61 @@
|
|||
<template>
|
||||
<div class="bg-[#dbe5f6] p-10px">
|
||||
<div class="flex items-center mb-10px">
|
||||
<span class="mr-10px w-60px">产品</span>
|
||||
<el-button type="primary" @click="productTableSelectRef?.open()" size="small" plain>
|
||||
{{ product ? product.name : '选择产品' }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="flex items-center mb-10px">
|
||||
<span class="mr-10px w-60px">设备</span>
|
||||
<el-button type="primary" @click="openDeviceSelect" size="small" plain>
|
||||
{{ isEmpty(deviceList) ? '选择设备' : deviceControlConfig.deviceNames.join(',') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="flex items-center mb-10px">
|
||||
<span class="mr-10px w-60px">消息类型</span>
|
||||
<div class="bg-[#dbe5f6] flex p-10px">
|
||||
<div class="flex flex-col items-center justify-center mr-10px h-a">
|
||||
<el-select
|
||||
v-model="deviceControlConfig.type"
|
||||
@change="deviceControlConfig.data = {}"
|
||||
class="!w-160px"
|
||||
clearable
|
||||
placeholder="选择消息类型"
|
||||
placeholder=""
|
||||
>
|
||||
<el-option label="属性" :value="IotDeviceMessageTypeEnum.PROPERTY" />
|
||||
<el-option label="服务" :value="IotDeviceMessageTypeEnum.SERVICE" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div v-if="deviceControlConfig.type" class="flex items-center mb-10px">
|
||||
<span class="mr-10px w-60px">具体操作</span>
|
||||
<el-select
|
||||
v-model="deviceControlConfig.identifier"
|
||||
class="!w-240px"
|
||||
clearable
|
||||
placeholder="选择操作项"
|
||||
<div class="">
|
||||
<div
|
||||
class="flex items-center justify-around mb-10px last:mb-0"
|
||||
v-for="(parameter, index) in parameters"
|
||||
:key="index"
|
||||
>
|
||||
<template v-if="deviceControlConfig.type === IotDeviceMessageTypeEnum.PROPERTY">
|
||||
<el-option :value="IotDeviceMessageIdentifierEnum.PROPERTY_SET" label="属性设置" />
|
||||
</template>
|
||||
<template v-else-if="deviceControlConfig.type === IotDeviceMessageTypeEnum.SERVICE">
|
||||
<el-select
|
||||
v-model="parameter.identifier"
|
||||
class="!w-240px mr-10px"
|
||||
clearable
|
||||
placeholder="请选择物模型"
|
||||
>
|
||||
<el-option
|
||||
v-for="model in thingModelTSL?.services"
|
||||
:key="model.identifier"
|
||||
:label="model.name"
|
||||
:value="model.identifier"
|
||||
v-for="thingModel in thingModels"
|
||||
:key="thingModel.identifier"
|
||||
:label="thingModel.name"
|
||||
:value="thingModel.identifier"
|
||||
/>
|
||||
</template>
|
||||
</el-select>
|
||||
</el-select>
|
||||
<el-input v-model="parameter.value" class="!w-240px mr-10px" placeholder="请输入值">
|
||||
<template v-if="getUnitName(parameter.identifier)" #append>
|
||||
{{ getUnitName(parameter.identifier) }}
|
||||
</template>
|
||||
</el-input>
|
||||
<el-tooltip content="删除参数" placement="top">
|
||||
<el-button type="danger" circle size="small" @click="removeParameter(index)">
|
||||
<Icon icon="ep:delete" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 添加参数 -->
|
||||
<div class="flex flex-1 flex-col items-center justify-center w-60px h-a">
|
||||
<el-tooltip content="添加参数" placement="top">
|
||||
<el-button type="primary" circle size="small" @click="addParameter">
|
||||
<Icon icon="ep:plus" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<DeviceActionControl
|
||||
v-if="deviceControlConfig.identifier && deviceControlConfig.type"
|
||||
:model-value="deviceControlConfig.data"
|
||||
:thing-models="getThingModels()"
|
||||
:message-type="deviceControlConfig.type"
|
||||
:identifier="deviceControlConfig.identifier"
|
||||
@update:model-value="(val) => (deviceControlConfig.data = val)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 产品、设备的选择 -->
|
||||
<ProductTableSelect ref="productTableSelectRef" @success="handleProductSelect" />
|
||||
<DeviceTableSelect
|
||||
ref="deviceTableSelectRef"
|
||||
multiple
|
||||
:product-id="product?.id"
|
||||
@success="handleDeviceSelect"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
import ProductTableSelect from '@/views/iot/product/product/components/ProductTableSelect.vue'
|
||||
import DeviceTableSelect from '@/views/iot/device/device/components/DeviceTableSelect.vue'
|
||||
import DeviceActionControl from './DeviceActionControl.vue'
|
||||
import { ProductVO } from '@/api/iot/product/product'
|
||||
import { DeviceVO } from '@/api/iot/device/device'
|
||||
import { ThingModelApi } from '@/api/iot/thingmodel'
|
||||
import {
|
||||
ActionDeviceControl,
|
||||
|
|
@ -83,12 +66,27 @@ import {
|
|||
/** 设备控制执行器组件 */
|
||||
defineOptions({ name: 'DeviceControlAction' })
|
||||
|
||||
const props = defineProps<{ modelValue: any }>()
|
||||
const props = defineProps<{
|
||||
modelValue: any
|
||||
productId?: number
|
||||
}>()
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
const deviceControlConfig = useVModel(props, 'modelValue', emits) as Ref<ActionDeviceControl>
|
||||
|
||||
const message = useMessage()
|
||||
|
||||
/** 执行器参数 */
|
||||
const parameters = ref<{ identifier: string; value: any }[]>([])
|
||||
const addParameter = () => {
|
||||
if (parameters.value.length >= thingModels.value.length) {
|
||||
message.warning(`该产品只有${thingModels.value.length}个物模型!!!`)
|
||||
return
|
||||
}
|
||||
parameters.value.push({ identifier: '', value: undefined })
|
||||
}
|
||||
const removeParameter = (index: number) => {
|
||||
parameters.value.splice(index, 1)
|
||||
}
|
||||
// 初始化设备控制执行器结构
|
||||
const initDeviceControlConfig = () => {
|
||||
if (!deviceControlConfig.value) {
|
||||
|
|
@ -100,63 +98,94 @@ const initDeviceControlConfig = () => {
|
|||
data: {}
|
||||
} as ActionDeviceControl
|
||||
}
|
||||
|
||||
// 确保data对象存在
|
||||
if (!deviceControlConfig.value.data) {
|
||||
deviceControlConfig.value.data = {}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
initDeviceControlConfig()
|
||||
if (props.productId) {
|
||||
getThingModelTSL()
|
||||
}
|
||||
})
|
||||
|
||||
// 产品和设备选择
|
||||
const productTableSelectRef = ref<InstanceType<typeof ProductTableSelect>>()
|
||||
const deviceTableSelectRef = ref<InstanceType<typeof DeviceTableSelect>>()
|
||||
const product = ref<ProductVO>()
|
||||
const deviceList = ref<DeviceVO[]>([])
|
||||
|
||||
/** 处理产品选择 */
|
||||
const handleProductSelect = (val: ProductVO) => {
|
||||
product.value = val
|
||||
deviceControlConfig.value.productKey = val.productKey
|
||||
deviceList.value = []
|
||||
getThingModelTSL()
|
||||
}
|
||||
|
||||
/** 处理设备选择 */
|
||||
const handleDeviceSelect = (val: DeviceVO[]) => {
|
||||
deviceList.value = val
|
||||
deviceControlConfig.value.deviceNames = val.map((item) => item.deviceName)
|
||||
}
|
||||
|
||||
/** 打开设备选择器 */
|
||||
const openDeviceSelect = () => {
|
||||
if (!product.value) {
|
||||
message.warning('请先选择一个产品')
|
||||
return
|
||||
// 监听productId变化
|
||||
watch(
|
||||
() => props.productId,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
getThingModelTSL()
|
||||
// 当产品ID变化时,清空原有数据
|
||||
deviceControlConfig.value.data = {}
|
||||
} else {
|
||||
thingModelTSL.value = undefined
|
||||
}
|
||||
}
|
||||
deviceTableSelectRef.value?.open()
|
||||
}
|
||||
)
|
||||
|
||||
// 监听消息类型变化
|
||||
watch(
|
||||
() => deviceControlConfig.value.type,
|
||||
() => {
|
||||
// 切换消息类型时清空参数
|
||||
deviceControlConfig.value.data = {}
|
||||
if (deviceControlConfig.value.type === IotDeviceMessageTypeEnum.PROPERTY) {
|
||||
deviceControlConfig.value.identifier = IotDeviceMessageIdentifierEnum.PROPERTY_SET
|
||||
} else {
|
||||
deviceControlConfig.value.identifier = ''
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// 监听identifier变化
|
||||
watch(
|
||||
() => deviceControlConfig.value.identifier,
|
||||
() => {
|
||||
// 切换identifier时清空参数
|
||||
deviceControlConfig.value.data = {}
|
||||
}
|
||||
)
|
||||
|
||||
/** 获取产品物模型 */
|
||||
const thingModelTSL = ref<any>()
|
||||
const getThingModelTSL = async () => {
|
||||
if (!product.value) {
|
||||
if (!props.productId) {
|
||||
return
|
||||
}
|
||||
thingModelTSL.value = await ThingModelApi.getThingModelTSLByProductId(product.value.id)
|
||||
try {
|
||||
thingModelTSL.value = await ThingModelApi.getThingModelTSLByProductId(props.productId)
|
||||
} catch (error) {
|
||||
console.error('获取物模型失败', error)
|
||||
}
|
||||
}
|
||||
|
||||
/** 根据消息类型和标识符获取对应的物模型 */
|
||||
const getThingModels = () => {
|
||||
if (!deviceControlConfig.value) return []
|
||||
|
||||
const thingModels = computed((): any[] => {
|
||||
switch (deviceControlConfig.value.type) {
|
||||
case IotDeviceMessageTypeEnum.PROPERTY:
|
||||
return thingModelTSL.value?.properties || []
|
||||
return thingModelTSL.value.properties
|
||||
// TODO puhui999: 服务和事件后续考虑
|
||||
case IotDeviceMessageTypeEnum.SERVICE:
|
||||
return thingModelTSL.value?.services.find(
|
||||
(s: any) => s.identifier === deviceControlConfig.value.identifier
|
||||
) || {}
|
||||
return thingModelTSL.value.services
|
||||
case IotDeviceMessageTypeEnum.EVENT:
|
||||
return thingModelTSL.value.events
|
||||
}
|
||||
return []
|
||||
}
|
||||
</script>
|
||||
})
|
||||
/** 获得属性单位 */
|
||||
const getUnitName = computed(() => (identifier: string) => {
|
||||
const model = thingModels.value?.find((item: any) => item.identifier === identifier)
|
||||
// 属性
|
||||
if (model?.dataSpecs) {
|
||||
return model.dataSpecs.unitName
|
||||
}
|
||||
// TODO puhui999: 先不考虑服务和事件的情况
|
||||
// 服务和事件
|
||||
// if (model?.outputParams) {
|
||||
// return model.dataSpecs.unitName
|
||||
// }
|
||||
return ''
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,238 +0,0 @@
|
|||
<template>
|
||||
<div class="property-value-input">
|
||||
<!-- 文本类型 -->
|
||||
<template v-if="dataType === 'text'">
|
||||
<el-input
|
||||
v-model="inputValue"
|
||||
class="!w-240px"
|
||||
:placeholder="`请输入${dataSpecs?.name || '值'}`"
|
||||
>
|
||||
<template v-if="dataSpecs?.unitName" #append>{{ dataSpecs.unitName }}</template>
|
||||
</el-input>
|
||||
</template>
|
||||
|
||||
<!-- 数值类型 -->
|
||||
<template v-else-if="['int', 'float', 'double'].includes(dataType)">
|
||||
<el-input-number
|
||||
v-model="inputValue"
|
||||
class="!w-240px"
|
||||
:min="dataSpecs?.min"
|
||||
:max="dataSpecs?.max"
|
||||
:precision="dataType === 'int' ? 0 : 2"
|
||||
:step="dataType === 'int' ? 1 : 0.1"
|
||||
:placeholder="`请输入${dataSpecs?.name || '值'}`"
|
||||
/>
|
||||
<span v-if="dataSpecs?.unitName" class="ml-5px">{{ dataSpecs.unitName }}</span>
|
||||
</template>
|
||||
|
||||
<!-- 枚举类型 -->
|
||||
<template v-else-if="dataType === 'enum'">
|
||||
<el-select
|
||||
v-model="inputValue"
|
||||
class="!w-240px"
|
||||
clearable
|
||||
:placeholder="`请选择${dataSpecs?.name || '值'}`"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in dataSpecs?.enumList"
|
||||
:key="item.value"
|
||||
:label="item.text"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<!-- 布尔类型 -->
|
||||
<template v-else-if="dataType === 'bool'">
|
||||
<el-radio-group v-model="inputValue">
|
||||
<el-radio
|
||||
v-for="item in [
|
||||
{ value: true, label: dataSpecs?.trueText || '是' },
|
||||
{ value: false, label: dataSpecs?.falseText || '否' }
|
||||
]"
|
||||
:key="String(item.value)"
|
||||
:label="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
|
||||
<!-- 日期类型 -->
|
||||
<template v-else-if="dataType === 'date'">
|
||||
<el-date-picker
|
||||
v-model="inputValue"
|
||||
class="!w-240px"
|
||||
type="datetime"
|
||||
:placeholder="`请选择${dataSpecs?.name || '日期'}`"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 结构体类型 -->
|
||||
<template v-else-if="dataType === 'struct'">
|
||||
<el-collapse class="w-full">
|
||||
<el-collapse-item title="结构体参数">
|
||||
<div
|
||||
v-for="field in dataSpecs?.specs"
|
||||
:key="field.identifier"
|
||||
class="flex items-center mb-10px"
|
||||
>
|
||||
<span class="mr-10px inline-block min-w-80px">{{ field.name }}</span>
|
||||
<PropertyValueInput
|
||||
:model-value="getStructFieldValue(field.identifier)"
|
||||
:data-specs="field"
|
||||
@update:model-value="(val) => updateStructField(field.identifier, val)"
|
||||
/>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</template>
|
||||
|
||||
<!-- 数组类型 -->
|
||||
<template v-else-if="dataType === 'array'">
|
||||
<div class="flex flex-col w-full">
|
||||
<div v-for="(item, index) in arrayValue" :key="index" class="flex items-center mb-10px">
|
||||
<PropertyValueInput
|
||||
:model-value="item"
|
||||
:data-specs="dataSpecs?.arrayInfo"
|
||||
@update:model-value="(val) => updateArrayItem(index, val)"
|
||||
/>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
circle
|
||||
class="ml-10px"
|
||||
@click="removeArrayItem(index)"
|
||||
>
|
||||
<Icon icon="ep:delete" />
|
||||
</el-button>
|
||||
</div>
|
||||
<el-button type="primary" size="small" plain @click="addArrayItem">添加项</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 默认文本输入 -->
|
||||
<template v-else>
|
||||
<el-input
|
||||
v-model="inputValue"
|
||||
class="!w-240px"
|
||||
:placeholder="`请输入${dataSpecs?.name || '值'}`"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core'
|
||||
|
||||
/** 属性值输入组件 */
|
||||
defineOptions({ name: 'PropertyValueInput' })
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: any
|
||||
dataSpecs: any
|
||||
}>()
|
||||
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
|
||||
// 获取数据类型
|
||||
const dataType = computed(() => {
|
||||
return props.dataSpecs?.dataType
|
||||
})
|
||||
|
||||
// 基本类型的输入值
|
||||
const inputValue = computed({
|
||||
get: () => {
|
||||
// 对于日期类型,如果是字符串需要转换为日期对象
|
||||
if (dataType.value === 'date' && typeof props.modelValue === 'string') {
|
||||
return new Date(props.modelValue)
|
||||
}
|
||||
return props.modelValue
|
||||
},
|
||||
set: (value) => {
|
||||
// 对于日期类型,需要转换为ISO字符串
|
||||
if (dataType.value === 'date' && value instanceof Date) {
|
||||
emits('update:modelValue', value.toISOString())
|
||||
return
|
||||
}
|
||||
emits('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
|
||||
// 结构体类型处理
|
||||
const structValue = computed({
|
||||
get: () => {
|
||||
return props.modelValue || {}
|
||||
},
|
||||
set: (value) => {
|
||||
emits('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
|
||||
// 获取结构体字段值
|
||||
const getStructFieldValue = (identifier: string) => {
|
||||
return structValue.value[identifier]
|
||||
}
|
||||
|
||||
// 更新结构体字段
|
||||
const updateStructField = (identifier: string, value: any) => {
|
||||
const newStruct = { ...structValue.value }
|
||||
newStruct[identifier] = value
|
||||
structValue.value = newStruct
|
||||
}
|
||||
|
||||
// 数组类型处理
|
||||
const arrayValue = computed({
|
||||
get: () => {
|
||||
return Array.isArray(props.modelValue) ? props.modelValue : []
|
||||
},
|
||||
set: (value) => {
|
||||
emits('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
|
||||
// 添加数组项
|
||||
const addArrayItem = () => {
|
||||
const newArray = [...arrayValue.value]
|
||||
// 根据数组元素类型创建默认值
|
||||
const arrayItemType = props.dataSpecs?.arrayInfo?.dataType
|
||||
let defaultValue = null
|
||||
switch (arrayItemType) {
|
||||
case 'int':
|
||||
case 'float':
|
||||
case 'double':
|
||||
defaultValue = 0
|
||||
break
|
||||
case 'bool':
|
||||
defaultValue = false
|
||||
break
|
||||
case 'text':
|
||||
defaultValue = ''
|
||||
break
|
||||
case 'struct':
|
||||
defaultValue = {}
|
||||
break
|
||||
case 'array':
|
||||
defaultValue = []
|
||||
break
|
||||
}
|
||||
newArray.push(defaultValue)
|
||||
arrayValue.value = newArray
|
||||
}
|
||||
|
||||
// 更新数组项
|
||||
const updateArrayItem = (index: number, value: any) => {
|
||||
const newArray = [...arrayValue.value]
|
||||
newArray[index] = value
|
||||
arrayValue.value = newArray
|
||||
}
|
||||
|
||||
// 移除数组项
|
||||
const removeArrayItem = (index: number) => {
|
||||
const newArray = [...arrayValue.value]
|
||||
newArray.splice(index, 1)
|
||||
arrayValue.value = newArray
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
Loading…
Reference in New Issue