ValueInput.vue 7.02 KB
<!-- 值输入组件 -->
<template>
  <div class="w-full min-w-0">
    <!-- 布尔值选择 -->
    <el-select
      v-if="propertyType === IoTDataSpecsDataTypeEnum.BOOL"
      v-model="localValue"
      placeholder="请选择布尔值"
      class="w-full!"
    >
      <el-option label="真 (true)" value="true" />
      <el-option label="假 (false)" value="false" />
    </el-select>

    <!-- 枚举值选择 -->
    <el-select
      v-else-if="propertyType === IoTDataSpecsDataTypeEnum.ENUM && enumOptions.length > 0"
      v-model="localValue"
      placeholder="请选择枚举值"
      class="w-full!"
    >
      <el-option
        v-for="option in enumOptions"
        :key="option.value"
        :label="option.label"
        :value="option.value"
      />
    </el-select>

    <!-- 范围输入 (between 操作符) -->
    <div
      v-else-if="operator === IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN.value"
      class="w-full! flex items-center gap-8px"
    >
      <el-input
        v-model="rangeStart"
        :type="getInputType()"
        placeholder="最小值"
        @input="handleRangeChange"
        class="flex-1 min-w-0"
        style="width: auto !important"
      />
      <span class="text-12px text-[var(--el-text-color-secondary)] whitespace-nowrap">至</span>
      <el-input
        v-model="rangeEnd"
        :type="getInputType()"
        placeholder="最大值"
        @input="handleRangeChange"
        class="flex-1 min-w-0"
      />
    </div>

    <!-- 列表输入 (in 操作符) -->
    <div
      v-else-if="operator === IotRuleSceneTriggerConditionParameterOperatorEnum.IN.value"
      class="w-full!"
    >
      <el-input v-model="localValue" placeholder="请输入值列表,用逗号分隔" class="w-full!">
        <template #suffix>
          <el-tooltip content="多个值用逗号分隔,如:1,2,3" placement="top">
            <Icon
              icon="ep:question-filled"
              class="text-[var(--el-text-color-placeholder)] cursor-help"
            />
          </el-tooltip>
        </template>
      </el-input>
      <div v-if="listPreview.length > 0" class="mt-8px flex items-center gap-6px flex-wrap">
        <span class="text-12px text-[var(--el-text-color-secondary)]">解析结果:</span>
        <el-tag v-for="(item, index) in listPreview" :key="index" size="small" class="m-0">
          {{ item }}
        </el-tag>
      </div>
    </div>

    <!-- 日期时间输入 -->
    <el-date-picker
      v-else-if="propertyType === IoTDataSpecsDataTypeEnum.DATE"
      v-model="dateValue"
      type="datetime"
      placeholder="请选择日期时间"
      format="YYYY-MM-DD HH:mm:ss"
      value-format="YYYY-MM-DD HH:mm:ss"
      @change="handleDateChange"
      class="w-full!"
    />

    <!-- 数字输入 -->
    <el-input-number
      v-else-if="isNumericType()"
      v-model="numberValue"
      :precision="getPrecision()"
      :step="getStep()"
      :min="getMin()"
      :max="getMax()"
      placeholder="请输入数值"
      @change="handleNumberChange"
      class="w-full!"
    />

    <!-- 文本输入 -->
    <el-input
      v-else
      v-model="localValue"
      :type="getInputType()"
      :placeholder="getPlaceholder()"
      class="w-full!"
    >
      <template #suffix>
        <el-tooltip
          v-if="propertyConfig?.unit"
          :content="`单位:${propertyConfig.unit}`"
          placement="top"
        >
          <span class="text-12px text-[var(--el-text-color-secondary)] px-4px">
            {{ propertyConfig.unit }}
          </span>
        </el-tooltip>
      </template>
    </el-input>
  </div>
</template>

<script setup lang="ts">
import { useVModel } from '@vueuse/core'
import {
  IoTDataSpecsDataTypeEnum,
  IotRuleSceneTriggerConditionParameterOperatorEnum
} from '@/views/iot/utils/constants'

/** 值输入组件 */
defineOptions({ name: 'ValueInput' })

interface Props {
  modelValue?: string
  propertyType?: string
  operator?: string
  propertyConfig?: any
}

interface Emits {
  (e: 'update:modelValue', value: string): void
}

const props = defineProps<Props>()
const emit = defineEmits<Emits>()

const localValue = useVModel(props, 'modelValue', emit, {
  defaultValue: ''
})

const rangeStart = ref('') // 范围开始值
const rangeEnd = ref('') // 范围结束值
const dateValue = ref('') // 日期值
const numberValue = ref<number>() // 数字值

/** 计算属性:枚举选项 */
const enumOptions = computed(() => {
  if (props.propertyConfig?.enum) {
    return props.propertyConfig.enum.map((item: any) => ({
      label: item.name || item.label || item.value,
      value: item.value
    }))
  }
  return []
})

/** 计算属性:列表预览 */
const listPreview = computed(() => {
  if (
    props.operator === IotRuleSceneTriggerConditionParameterOperatorEnum.IN.value &&
    localValue.value
  ) {
    return localValue.value
      .split(',')
      .map((item) => item.trim())
      .filter((item) => item)
  }
  return []
})

/** 判断是否为数字类型 */
const isNumericType = () => {
  return [
    IoTDataSpecsDataTypeEnum.INT,
    IoTDataSpecsDataTypeEnum.FLOAT,
    IoTDataSpecsDataTypeEnum.DOUBLE
  ].includes((props.propertyType || '') as any)
}

/** 获取输入框类型 */
const getInputType = () => {
  switch (props.propertyType) {
    case IoTDataSpecsDataTypeEnum.INT:
    case IoTDataSpecsDataTypeEnum.FLOAT:
    case IoTDataSpecsDataTypeEnum.DOUBLE:
      return 'number'
    default:
      return 'text'
  }
}

/** 获取占位符文本 */
const getPlaceholder = () => {
  const typeMap = {
    [IoTDataSpecsDataTypeEnum.TEXT]: '请输入字符串',
    [IoTDataSpecsDataTypeEnum.INT]: '请输入整数',
    [IoTDataSpecsDataTypeEnum.FLOAT]: '请输入浮点数',
    [IoTDataSpecsDataTypeEnum.DOUBLE]: '请输入双精度数',
    [IoTDataSpecsDataTypeEnum.STRUCT]: '请输入 JSON 格式数据',
    [IoTDataSpecsDataTypeEnum.ARRAY]: '请输入数组格式数据'
  }
  return typeMap[props.propertyType || ''] || '请输入值'
}

/** 获取数字精度 */
const getPrecision = () => {
  return props.propertyType === IoTDataSpecsDataTypeEnum.INT ? 0 : 2
}

/** 获取数字步长 */
const getStep = () => {
  return props.propertyType === IoTDataSpecsDataTypeEnum.INT ? 1 : 0.1
}

/** 获取最小值 */
const getMin = () => {
  return props.propertyConfig?.min || undefined
}

/** 获取最大值 */
const getMax = () => {
  return props.propertyConfig?.max || undefined
}

/** 处理范围变化事件 */
const handleRangeChange = () => {
  if (rangeStart.value && rangeEnd.value) {
    localValue.value = `${rangeStart.value},${rangeEnd.value}`
  } else {
    localValue.value = ''
  }
}

/** 处理日期变化事件 */
const handleDateChange = (value: string) => {
  localValue.value = value || ''
}

/** 处理数字变化事件 */
const handleNumberChange = (value: number | undefined) => {
  localValue.value = value?.toString() || ''
}

/** 监听操作符变化 */
watch(
  () => props.operator,
  () => {
    localValue.value = ''
    rangeStart.value = ''
    rangeEnd.value = ''
    dateValue.value = ''
    numberValue.value = undefined
  }
)
</script>