ValueInput.vue 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. <!-- 值输入组件 -->
  2. <!-- TODO @yunai:这个需要在看看。。。 -->
  3. <template>
  4. <div class="w-full min-w-0">
  5. <!-- 布尔值选择 -->
  6. <el-select
  7. v-if="propertyType === 'bool'"
  8. v-model="localValue"
  9. placeholder="请选择布尔值"
  10. @change="handleChange"
  11. class="w-full!"
  12. style="width: 100% !important"
  13. >
  14. <el-option label="真 (true)" value="true" />
  15. <el-option label="假 (false)" value="false" />
  16. </el-select>
  17. <!-- 枚举值选择 -->
  18. <el-select
  19. v-else-if="propertyType === 'enum' && enumOptions.length > 0"
  20. v-model="localValue"
  21. placeholder="请选择枚举值"
  22. @change="handleChange"
  23. class="w-full!"
  24. style="width: 100% !important"
  25. >
  26. <el-option
  27. v-for="option in enumOptions"
  28. :key="option.value"
  29. :label="option.label"
  30. :value="option.value"
  31. />
  32. </el-select>
  33. <!-- 范围输入 (between 操作符) -->
  34. <div
  35. v-else-if="operator === 'between'"
  36. class="w-full! flex items-center gap-8px"
  37. style="width: 100% !important"
  38. >
  39. <el-input
  40. v-model="rangeStart"
  41. :type="getInputType()"
  42. placeholder="最小值"
  43. @input="handleRangeChange"
  44. class="flex-1 min-w-0"
  45. style="width: auto !important"
  46. />
  47. <span class="text-12px text-[var(--el-text-color-secondary)] whitespace-nowrap">至</span>
  48. <el-input
  49. v-model="rangeEnd"
  50. :type="getInputType()"
  51. placeholder="最大值"
  52. @input="handleRangeChange"
  53. class="flex-1 min-w-0"
  54. style="width: auto !important"
  55. />
  56. </div>
  57. <!-- 列表输入 (in 操作符) -->
  58. <div v-else-if="operator === 'in'" class="w-full!" style="width: 100% !important">
  59. <el-input
  60. v-model="localValue"
  61. placeholder="请输入值列表,用逗号分隔"
  62. @input="handleChange"
  63. class="w-full!"
  64. style="width: 100% !important"
  65. >
  66. <template #suffix>
  67. <el-tooltip content="多个值用逗号分隔,如:1,2,3" placement="top">
  68. <Icon
  69. icon="ep:question-filled"
  70. class="text-[var(--el-text-color-placeholder)] cursor-help"
  71. />
  72. </el-tooltip>
  73. </template>
  74. </el-input>
  75. <div v-if="listPreview.length > 0" class="mt-8px flex items-center gap-6px flex-wrap">
  76. <span class="text-12px text-[var(--el-text-color-secondary)]">解析结果:</span>
  77. <el-tag v-for="(item, index) in listPreview" :key="index" size="small" class="m-0">
  78. {{ item }}
  79. </el-tag>
  80. </div>
  81. </div>
  82. <!-- 日期时间输入 -->
  83. <el-date-picker
  84. v-else-if="propertyType === 'date'"
  85. v-model="dateValue"
  86. type="datetime"
  87. placeholder="请选择日期时间"
  88. format="YYYY-MM-DD HH:mm:ss"
  89. value-format="YYYY-MM-DD HH:mm:ss"
  90. @change="handleDateChange"
  91. class="w-full!"
  92. style="width: 100% !important"
  93. />
  94. <!-- 数字输入 -->
  95. <el-input-number
  96. v-else-if="isNumericType()"
  97. v-model="numberValue"
  98. :precision="getPrecision()"
  99. :step="getStep()"
  100. :min="getMin()"
  101. :max="getMax()"
  102. placeholder="请输入数值"
  103. @change="handleNumberChange"
  104. class="w-full!"
  105. style="width: 100% !important"
  106. />
  107. <!-- 文本输入 -->
  108. <el-input
  109. v-else
  110. v-model="localValue"
  111. :type="getInputType()"
  112. :placeholder="getPlaceholder()"
  113. @input="handleChange"
  114. class="w-full!"
  115. style="width: 100% !important"
  116. >
  117. <template #suffix>
  118. <el-tooltip
  119. v-if="propertyConfig?.unit"
  120. :content="`单位:${propertyConfig.unit}`"
  121. placement="top"
  122. >
  123. <span class="text-12px text-[var(--el-text-color-secondary)] px-4px">{{
  124. propertyConfig.unit
  125. }}</span>
  126. </el-tooltip>
  127. </template>
  128. </el-input>
  129. </div>
  130. </template>
  131. <script setup lang="ts">
  132. import { useVModel } from '@vueuse/core'
  133. import { IoTDataSpecsDataTypeEnum } from '@/views/iot/utils/constants'
  134. /** 值输入组件 */
  135. defineOptions({ name: 'ValueInput' })
  136. interface Props {
  137. modelValue?: string
  138. propertyType?: string
  139. operator?: string
  140. propertyConfig?: any
  141. }
  142. interface Emits {
  143. (e: 'update:modelValue', value: string): void
  144. }
  145. const props = defineProps<Props>()
  146. const emit = defineEmits<Emits>()
  147. const localValue = useVModel(props, 'modelValue', emit, {
  148. defaultValue: ''
  149. })
  150. const rangeStart = ref('') // 范围开始值
  151. const rangeEnd = ref('') // 范围结束值
  152. const dateValue = ref('') // 日期值
  153. const numberValue = ref<number>() // 数字值
  154. // 计算属性:枚举选项
  155. const enumOptions = computed(() => {
  156. if (props.propertyConfig?.enum) {
  157. return props.propertyConfig.enum.map((item: any) => ({
  158. label: item.name || item.label || item.value,
  159. value: item.value
  160. }))
  161. }
  162. return []
  163. })
  164. // 计算属性:列表预览
  165. const listPreview = computed(() => {
  166. if (props.operator === 'in' && localValue.value) {
  167. return localValue.value
  168. .split(',')
  169. .map((item) => item.trim())
  170. .filter((item) => item)
  171. }
  172. return []
  173. })
  174. /**
  175. * 判断是否为数字类型
  176. * @returns 是否为数字类型
  177. */
  178. const isNumericType = () => {
  179. return [
  180. IoTDataSpecsDataTypeEnum.INT,
  181. IoTDataSpecsDataTypeEnum.FLOAT,
  182. IoTDataSpecsDataTypeEnum.DOUBLE
  183. ].includes((props.propertyType || '') as any)
  184. }
  185. /**
  186. * 获取输入框类型
  187. * @returns 输入框类型
  188. */
  189. const getInputType = () => {
  190. switch (props.propertyType) {
  191. case IoTDataSpecsDataTypeEnum.INT:
  192. case IoTDataSpecsDataTypeEnum.FLOAT:
  193. case IoTDataSpecsDataTypeEnum.DOUBLE:
  194. return 'number'
  195. default:
  196. return 'text'
  197. }
  198. }
  199. /**
  200. * 获取占位符文本
  201. * @returns 占位符文本
  202. */
  203. const getPlaceholder = () => {
  204. const typeMap = {
  205. [IoTDataSpecsDataTypeEnum.TEXT]: '请输入字符串',
  206. [IoTDataSpecsDataTypeEnum.INT]: '请输入整数',
  207. [IoTDataSpecsDataTypeEnum.FLOAT]: '请输入浮点数',
  208. [IoTDataSpecsDataTypeEnum.DOUBLE]: '请输入双精度数',
  209. [IoTDataSpecsDataTypeEnum.STRUCT]: '请输入 JSON 格式数据',
  210. [IoTDataSpecsDataTypeEnum.ARRAY]: '请输入数组格式数据'
  211. }
  212. return typeMap[props.propertyType || ''] || '请输入值'
  213. }
  214. /**
  215. * 获取数字精度
  216. * @returns 数字精度
  217. */
  218. const getPrecision = () => {
  219. return props.propertyType === IoTDataSpecsDataTypeEnum.INT ? 0 : 2
  220. }
  221. /**
  222. * 获取数字步长
  223. * @returns 数字步长
  224. */
  225. const getStep = () => {
  226. return props.propertyType === IoTDataSpecsDataTypeEnum.INT ? 1 : 0.1
  227. }
  228. /**
  229. * 获取最小值
  230. * @returns 最小值
  231. */
  232. const getMin = () => {
  233. return props.propertyConfig?.min || undefined
  234. }
  235. /**
  236. * 获取最大值
  237. * @returns 最大值
  238. */
  239. const getMax = () => {
  240. return props.propertyConfig?.max || undefined
  241. }
  242. /**
  243. * 处理值变化事件
  244. */
  245. const handleChange = () => {
  246. // 值变化处理
  247. }
  248. /**
  249. * 处理范围变化事件
  250. */
  251. const handleRangeChange = () => {
  252. if (rangeStart.value && rangeEnd.value) {
  253. localValue.value = `${rangeStart.value},${rangeEnd.value}`
  254. } else {
  255. localValue.value = ''
  256. }
  257. }
  258. /**
  259. * 处理日期变化事件
  260. * @param value 日期值
  261. */
  262. const handleDateChange = (value: string) => {
  263. localValue.value = value || ''
  264. }
  265. /**
  266. * 处理数字变化事件
  267. * @param value 数字值
  268. */
  269. const handleNumberChange = (value: number | undefined) => {
  270. localValue.value = value?.toString() || ''
  271. }
  272. // 监听操作符变化
  273. watch(
  274. () => props.operator,
  275. () => {
  276. localValue.value = ''
  277. rangeStart.value = ''
  278. rangeEnd.value = ''
  279. dateValue.value = ''
  280. numberValue.value = undefined
  281. }
  282. )
  283. </script>