Browse Source

feat:【IoT 物联网】物模型,移除 config.ts

YunaiV 9 months ago
parent
commit
677b0d61ca

+ 121 - 0
src/api/iot/thingmodel/index.ts

@@ -1,4 +1,5 @@
 import request from '@/config/axios'
+import { isEmpty } from '@/utils/is'
 
 /**
  * IoT 产品物模型
@@ -46,6 +47,26 @@ export interface ThingModelService {
   [key: string]: any
 }
 
+/** dataSpecs 数值型数据结构 */
+export interface DataSpecsNumberDataVO {
+  dataType: 'int' | 'float' | 'double' // 数据类型,取值为 INT、FLOAT 或 DOUBLE
+  max: string // 最大值,必须与 dataType 设置一致,且为 STRING 类型
+  min: string // 最小值,必须与 dataType 设置一致,且为 STRING 类型
+  step: string // 步长,必须与 dataType 设置一致,且为 STRING 类型
+  precise?: string // 精度,当 dataType 为 FLOAT 或 DOUBLE 时可选
+  defaultValue?: string // 默认值,可选
+  unit: string // 单位的符号
+  unitName: string // 单位的名称
+}
+
+/** dataSpecs 枚举型数据结构 */
+export interface DataSpecsEnumOrBoolDataVO {
+  dataType: 'enum' | 'bool'
+  defaultValue?: string // 默认值,可选
+  name: string // 枚举项的名称
+  value: number | undefined // 枚举值
+}
+
 // IoT 产品物模型 API
 export const ThingModelApi = {
   // 查询产品物模型分页
@@ -85,3 +106,103 @@ export const ThingModelApi = {
     return await request.delete({ url: `/iot/thing-model/delete?id=` + id })
   }
 }
+
+/** 公共校验规则 */
+export const ThingModelFormRules = {
+  name: [
+    { required: true, message: '功能名称不能为空', trigger: 'blur' },
+    {
+      pattern: /^[\u4e00-\u9fa5a-zA-Z0-9][\u4e00-\u9fa5a-zA-Z0-9\-_/\.]{0,29}$/,
+      message:
+        '支持中文、大小写字母、日文、数字、短划线、下划线、斜杠和小数点,必须以中文、英文或数字开头,不超过 30 个字符',
+      trigger: 'blur'
+    }
+  ],
+  type: [{ required: true, message: '功能类型不能为空', trigger: 'blur' }],
+  identifier: [
+    { required: true, message: '标识符不能为空', trigger: 'blur' },
+    {
+      pattern: /^[a-zA-Z0-9_]{1,50}$/,
+      message: '支持大小写字母、数字和下划线,不超过 50 个字符',
+      trigger: 'blur'
+    },
+    {
+      validator: (_: any, value: string, callback: any) => {
+        const reservedKeywords = ['set', 'get', 'post', 'property', 'event', 'time', 'value']
+        if (reservedKeywords.includes(value)) {
+          callback(
+            new Error(
+              'set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义'
+            )
+          )
+        } else if (/^\d+$/.test(value)) {
+          callback(new Error('标识符不能是纯数字'))
+        } else {
+          callback()
+        }
+      },
+      trigger: 'blur'
+    }
+  ],
+  'property.dataSpecs.childDataType': [{ required: true, message: '元素类型不能为空' }],
+  'property.dataSpecs.size': [
+    { required: true, message: '元素个数不能为空' },
+    {
+      validator: (_: any, value: any, callback: any) => {
+        if (isEmpty(value)) {
+          callback(new Error('元素个数不能为空'))
+          return
+        }
+        if (isNaN(Number(value))) {
+          callback(new Error('元素个数必须是数字'))
+          return
+        }
+        callback()
+      },
+      trigger: 'blur'
+    }
+  ],
+  'property.dataSpecs.length': [
+    { required: true, message: '请输入文本字节长度', trigger: 'blur' },
+    {
+      validator: (_: any, value: any, callback: any) => {
+        if (isEmpty(value)) {
+          callback(new Error('文本长度不能为空'))
+          return
+        }
+        if (isNaN(Number(value))) {
+          callback(new Error('文本长度必须是数字'))
+          return
+        }
+        callback()
+      },
+      trigger: 'blur'
+    }
+  ],
+  'property.accessMode': [{ required: true, message: '请选择读写类型', trigger: 'change' }]
+}
+
+/** 校验布尔值名称 */
+export const validateBoolName = (_: any, value: string, callback: any) => {
+  if (isEmpty(value)) {
+    callback(new Error('布尔值名称不能为空'))
+    return
+  }
+  // 检查开头字符
+  if (!/^[\u4e00-\u9fa5a-zA-Z0-9]/.test(value)) {
+    callback(new Error('布尔值名称必须以中文、英文字母或数字开头'))
+    return
+  }
+  // 检查整体格式
+  if (!/^[\u4e00-\u9fa5a-zA-Z0-9][a-zA-Z0-9\u4e00-\u9fa5_-]*$/.test(value)) {
+    callback(new Error('布尔值名称只能包含中文、英文字母、数字、下划线和短划线'))
+    return
+  }
+  // 检查长度(一个中文算一个字符)
+  if (value.length > 20) {
+    callback(new Error('布尔值名称长度不能超过 20 个字符'))
+    return
+  }
+
+  callback()
+}

+ 2 - 5
src/views/iot/device/device/detail/DeviceDetailsSimulator.vue

@@ -24,7 +24,7 @@
                     <el-table-column align="center" label="数据类型" prop="identifier">
                       <!-- TODO @super:不用翻译,可以减少宽度的占用 -->
                       <template #default="{ row }">
-                        {{ dataTypeOptionsLabel(row.property?.dataType) ?? '-' }}
+                        {{ getDataTypeOptionsLabel(row.property?.dataType) ?? '-' }}
                       </template>
                     </el-table-column>
                     <el-table-column align="left" label="数据定义" prop="identifier">
@@ -145,9 +145,8 @@ import { ProductVO } from '@/api/iot/product/product'
 import { SimulatorData, ThingModelApi } from '@/api/iot/thingmodel'
 import { DeviceApi, DeviceStateEnum, DeviceVO } from '@/api/iot/device/device'
 import DeviceDetailsMessage from './DeviceDetailsMessage.vue'
-import { getDataTypeOptionsLabel } from '@/views/iot/thingmodel/config'
 import { DataDefinition } from '@/views/iot/thingmodel/components'
-import { IotDeviceMessageMethodEnum } from '@/views/iot/utils/constants'
+import { getDataTypeOptionsLabel, IotDeviceMessageMethodEnum } from '@/views/iot/utils/constants'
 
 const props = defineProps<{
   product: ProductVO
@@ -166,8 +165,6 @@ const queryParams = reactive({
   productId: -1
 })
 const list = ref<SimulatorData[]>([]) // 物模型列表的数据 TODO @super:thingModelList
-// TODO @super:dataTypeOptionsLabel 是不是不用定义,直接用 getDataTypeOptionsLabel 在 template 中使用即可?
-const dataTypeOptionsLabel = computed(() => (value: string) => getDataTypeOptionsLabel(value)) // 解析数据类型
 
 /** 查询物模型列表 */
 // TODO @super:getThingModelList 更精准

+ 9 - 4
src/views/iot/device/device/detail/DeviceDetailsThingModelEvent.vue

@@ -95,8 +95,11 @@
 import { DeviceApi } from '@/api/iot/device/device'
 import { ThingModelData } from '@/api/iot/thingmodel'
 import { formatDate, defaultShortcuts } from '@/utils/formatTime'
-import { IotDeviceMessageMethodEnum } from '@/views/iot/utils/constants'
-import { ThingModelType, getEventTypeByValue } from '@/views/iot/thingmodel/config'
+import {
+  getEventTypeLabel,
+  IotDeviceMessageMethodEnum,
+  IoTThingModelTypeEnum
+} from '@/views/iot/utils/constants'
 
 const props = defineProps<{
   deviceId: number
@@ -118,7 +121,9 @@ const queryFormRef = ref() // 搜索的表单
 
 /** 事件类型的物模型数据 */
 const eventThingModels = computed(() => {
-  return props.thingModelList.filter((item: ThingModelData) => item.type === ThingModelType.EVENT)
+  return props.thingModelList.filter(
+    (item: ThingModelData) => item.type === IoTThingModelTypeEnum.EVENT
+  )
 })
 
 /** 查询列表 */
@@ -164,7 +169,7 @@ const getEventType = (identifier: string | undefined) => {
     (item: ThingModelData) => item.identifier === identifier
   )
   if (!event?.event?.type) return '-'
-  return getEventTypeByValue(event.event.type) || '-'
+  return getEventTypeLabel(event.event.type) || '-'
 }
 
 /** 解析参数 */

+ 9 - 4
src/views/iot/device/device/detail/DeviceDetailsThingModelService.vue

@@ -110,8 +110,11 @@
 import { DeviceApi } from '@/api/iot/device/device'
 import { ThingModelData } from '@/api/iot/thingmodel'
 import { formatDate, defaultShortcuts } from '@/utils/formatTime'
-import { IotDeviceMessageMethodEnum } from '@/views/iot/utils/constants'
-import { ThingModelType, getCallTypeByValue } from '@/views/iot/thingmodel/config'
+import {
+  getThingModelServiceCallTypeLabel,
+  IotDeviceMessageMethodEnum,
+  IoTThingModelTypeEnum
+} from '@/views/iot/utils/constants'
 
 const props = defineProps<{
   deviceId: number
@@ -133,7 +136,9 @@ const queryFormRef = ref() // 搜索的表单
 
 /** 服务类型的物模型数据 */
 const serviceThingModels = computed(() => {
-  return props.thingModelList.filter((item: ThingModelData) => item.type === ThingModelType.SERVICE)
+  return props.thingModelList.filter(
+    (item: ThingModelData) => item.type === IoTThingModelTypeEnum.SERVICE
+  )
 })
 
 /** 查询列表 */
@@ -179,7 +184,7 @@ const getCallType = (identifier: string | undefined) => {
     (item: ThingModelData) => item.identifier === identifier
   )
   if (!service?.service?.callType) return '-'
-  return getCallTypeByValue(service.service.callType) || '-'
+  return getThingModelServiceCallTypeLabel(service.service.callType) || '-'
 }
 
 /** 解析参数 */

+ 13 - 9
src/views/iot/rule/scene/components/ThingModelParamInput.vue

@@ -77,8 +77,8 @@
 <script lang="ts" setup>
 import { computed } from 'vue'
 import { useVModel } from '@vueuse/core'
-import { DataSpecsDataType } from '@/views/iot/thingmodel/config'
 import ThingModelDualView from './ThingModelDualView.vue'
+import { IoTDataSpecsDataTypeEnum } from '@/views/iot/utils/constants'
 
 /** 物模型属性参数输入组件 */
 defineOptions({ name: 'ThingModelParamInput' })
@@ -98,14 +98,16 @@ const openJsonEditor = () => {
 
 /** 计算属性:判断数据类型 */
 const isNumeric = computed(() =>
-  [DataSpecsDataType.INT, DataSpecsDataType.FLOAT, DataSpecsDataType.DOUBLE].includes(
-    props.thingModel?.dataType as any
-  )
+  [
+    IoTDataSpecsDataTypeEnum.INT,
+    IoTDataSpecsDataTypeEnum.FLOAT,
+    IoTDataSpecsDataTypeEnum.DOUBLE
+  ].includes(props.thingModel?.dataType as any)
 )
-const isBool = computed(() => props.thingModel?.dataType === DataSpecsDataType.BOOL)
-const isEnum = computed(() => props.thingModel?.dataType === DataSpecsDataType.ENUM)
-const isDate = computed(() => props.thingModel?.dataType === DataSpecsDataType.DATE)
-const isText = computed(() => props.thingModel?.dataType === DataSpecsDataType.TEXT)
+const isBool = computed(() => props.thingModel?.dataType === IoTDataSpecsDataTypeEnum.BOOL)
+const isEnum = computed(() => props.thingModel?.dataType === IoTDataSpecsDataTypeEnum.ENUM)
+const isDate = computed(() => props.thingModel?.dataType === IoTDataSpecsDataTypeEnum.DATE)
+const isText = computed(() => props.thingModel?.dataType === IoTDataSpecsDataTypeEnum.TEXT)
 /** 获取数据规格 */
 const dataSpecs = computed(() => {
   if (isNumeric.value || isDate.value || isText.value) {
@@ -117,7 +119,9 @@ const dataSpecsList = computed(() => {
   if (
     isBool.value ||
     isEnum.value ||
-    [DataSpecsDataType.ARRAY, DataSpecsDataType.STRUCT].includes(props.thingModel?.dataType)
+    [IoTDataSpecsDataTypeEnum.ARRAY, IoTDataSpecsDataTypeEnum.STRUCT].includes(
+      props.thingModel?.dataType
+    )
   ) {
     return props.thingModel?.dataSpecsList || []
   }

+ 14 - 9
src/views/iot/thingmodel/ThingModelEvent.vue

@@ -6,21 +6,22 @@
     prop="event.type"
   >
     <el-radio-group v-model="thingModelEvent.type">
-      <el-radio :value="ThingModelEventType.INFO.value">
-        {{ ThingModelEventType.INFO.label }}
+      <!-- TODO @AI:使用枚举 -->
+      <el-radio :value="IoTThingModelEventTypeEnum.INFO.value">
+        {{ IoTThingModelEventTypeEnum.INFO.label }}
       </el-radio>
-      <el-radio :value="ThingModelEventType.ALERT.value">
-        {{ ThingModelEventType.ALERT.label }}
+      <el-radio :value="IoTThingModelEventTypeEnum.ALERT.value">
+        {{ IoTThingModelEventTypeEnum.ALERT.label }}
       </el-radio>
-      <el-radio :value="ThingModelEventType.ERROR.value">
-        {{ ThingModelEventType.ERROR.label }}
+      <el-radio :value="IoTThingModelEventTypeEnum.ERROR.value">
+        {{ IoTThingModelEventTypeEnum.ERROR.label }}
       </el-radio>
     </el-radio-group>
   </el-form-item>
   <el-form-item label="输出参数">
     <ThingModelInputOutputParam
       v-model="thingModelEvent.outputParams"
-      :direction="ThingModelParamDirection.OUTPUT"
+      :direction="IoTThingModelParamDirectionEnum.OUTPUT"
     />
   </el-form-item>
 </template>
@@ -29,8 +30,11 @@
 import ThingModelInputOutputParam from './ThingModelInputOutputParam.vue'
 import { useVModel } from '@vueuse/core'
 import { ThingModelEvent } from '@/api/iot/thingmodel'
-import { ThingModelEventType, ThingModelParamDirection } from './config'
 import { isEmpty } from '@/utils/is'
+import {
+  IoTThingModelEventTypeEnum,
+  IoTThingModelParamDirectionEnum
+} from '@/views/iot/utils/constants'
 
 /** IoT 物模型事件 */
 defineOptions({ name: 'ThingModelEvent' })
@@ -42,7 +46,8 @@ const thingModelEvent = useVModel(props, 'modelValue', emits) as Ref<ThingModelE
 // 默认选中,INFO 信息
 watch(
   () => thingModelEvent.value.type,
-  (val: string) => isEmpty(val) && (thingModelEvent.value.type = ThingModelEventType.INFO.value),
+  (val: string) =>
+    isEmpty(val) && (thingModelEvent.value.type = IoTThingModelEventTypeEnum.INFO.value),
   { immediate: true }
 )
 </script>

+ 26 - 20
src/views/iot/thingmodel/ThingModelForm.vue

@@ -27,16 +27,19 @@
       </el-form-item>
       <!-- 属性配置 -->
       <ThingModelProperty
-        v-if="formData.type === ThingModelType.PROPERTY"
+        v-if="formData.type === IoTThingModelTypeEnum.PROPERTY"
         v-model="formData.property"
       />
       <!-- 服务配置 -->
       <ThingModelService
-        v-if="formData.type === ThingModelType.SERVICE"
+        v-if="formData.type === IoTThingModelTypeEnum.SERVICE"
         v-model="formData.service"
       />
       <!-- 事件配置 -->
-      <ThingModelEvent v-if="formData.type === ThingModelType.EVENT" v-model="formData.event" />
+      <ThingModelEvent
+        v-if="formData.type === IoTThingModelTypeEnum.EVENT"
+        v-model="formData.event"
+      />
       <el-form-item label="描述" prop="description">
         <el-input
           v-model="formData.description"
@@ -60,9 +63,12 @@ import { ProductVO } from '@/api/iot/product/product'
 import ThingModelProperty from './ThingModelProperty.vue'
 import ThingModelService from './ThingModelService.vue'
 import ThingModelEvent from './ThingModelEvent.vue'
-import { ThingModelApi, ThingModelData } from '@/api/iot/thingmodel'
-import { IOT_PROVIDE_KEY } from '@/views/iot/utils/constants'
-import { DataSpecsDataType, ThingModelFormRules, ThingModelType } from './config'
+import { ThingModelApi, ThingModelData, ThingModelFormRules } from '@/api/iot/thingmodel'
+import {
+  IOT_PROVIDE_KEY,
+  IoTDataSpecsDataTypeEnum,
+  IoTThingModelTypeEnum
+} from '@/views/iot/utils/constants'
 import { cloneDeep } from 'lodash-es'
 import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
 import { isEmpty } from '@/utils/is'
@@ -80,12 +86,12 @@ const dialogTitle = ref('') // 弹窗的标题
 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
 const formType = ref('') // 表单的类型:create - 新增;update - 修改
 const formData = ref<ThingModelData>({
-  type: ThingModelType.PROPERTY,
-  dataType: DataSpecsDataType.INT,
+  type: IoTThingModelTypeEnum.PROPERTY,
+  dataType: IoTDataSpecsDataTypeEnum.INT,
   property: {
-    dataType: DataSpecsDataType.INT,
+    dataType: IoTDataSpecsDataTypeEnum.INT,
     dataSpecs: {
-      dataType: DataSpecsDataType.INT
+      dataType: IoTDataSpecsDataTypeEnum.INT
     }
   },
   service: {},
@@ -106,11 +112,11 @@ const open = async (type: string, id?: number) => {
       formData.value = await ThingModelApi.getThingModel(id)
       // 情况一:属性初始化
       if (isEmpty(formData.value.property)) {
-        formData.value.dataType = DataSpecsDataType.INT
+        formData.value.dataType = IoTDataSpecsDataTypeEnum.INT
         formData.value.property = {
-          dataType: DataSpecsDataType.INT,
+          dataType: IoTDataSpecsDataTypeEnum.INT,
           dataSpecs: {
-            dataType: DataSpecsDataType.INT
+            dataType: IoTDataSpecsDataTypeEnum.INT
           }
         }
       }
@@ -158,7 +164,7 @@ const submitForm = async () => {
 const fillExtraAttributes = (data: any) => {
   // 处理不同类型的情况
   // 属性
-  if (data.type === ThingModelType.PROPERTY) {
+  if (data.type === IoTThingModelTypeEnum.PROPERTY) {
     removeDataSpecs(data.property)
     data.dataType = data.property.dataType
     data.property.identifier = data.identifier
@@ -167,7 +173,7 @@ const fillExtraAttributes = (data: any) => {
     delete data.event
   }
   // 服务
-  if (data.type === ThingModelType.SERVICE) {
+  if (data.type === IoTThingModelTypeEnum.SERVICE) {
     removeDataSpecs(data.service)
     data.dataType = data.service.dataType
     data.service.identifier = data.identifier
@@ -176,7 +182,7 @@ const fillExtraAttributes = (data: any) => {
     delete data.event
   }
   // 事件
-  if (data.type === ThingModelType.EVENT) {
+  if (data.type === IoTThingModelTypeEnum.EVENT) {
     removeDataSpecs(data.event)
     data.dataType = data.event.dataType
     data.event.identifier = data.identifier
@@ -198,12 +204,12 @@ const removeDataSpecs = (val: any) => {
 /** 重置表单 */
 const resetForm = () => {
   formData.value = {
-    type: ThingModelType.PROPERTY,
-    dataType: DataSpecsDataType.INT,
+    type: IoTThingModelTypeEnum.PROPERTY,
+    dataType: IoTDataSpecsDataTypeEnum.INT,
     property: {
-      dataType: DataSpecsDataType.INT,
+      dataType: IoTDataSpecsDataTypeEnum.INT,
       dataSpecs: {
-        dataType: DataSpecsDataType.INT
+        dataType: IoTDataSpecsDataTypeEnum.INT
       }
     },
     service: {},

+ 8 - 7
src/views/iot/thingmodel/ThingModelInputOutputParam.vue

@@ -43,8 +43,9 @@
 <script lang="ts" setup>
 import { useVModel } from '@vueuse/core'
 import ThingModelProperty from './ThingModelProperty.vue'
-import { DataSpecsDataType, ThingModelFormRules } from './config'
 import { isEmpty } from '@/utils/is'
+import { IoTDataSpecsDataTypeEnum } from '@/views/iot/utils/constants'
+import { ThingModelFormRules } from '@/api/iot/thingmodel'
 
 /** 输入输出参数配置组件 */
 defineOptions({ name: 'ThingModelInputOutputParam' })
@@ -57,11 +58,11 @@ const dialogTitle = ref('新增参数') // 弹窗的标题
 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
 const paramFormRef = ref() // 表单 ref
 const formData = ref<any>({
-  dataType: DataSpecsDataType.INT,
+  dataType: IoTDataSpecsDataTypeEnum.INT,
   property: {
-    dataType: DataSpecsDataType.INT,
+    dataType: IoTDataSpecsDataTypeEnum.INT,
     dataSpecs: {
-      dataType: DataSpecsDataType.INT
+      dataType: IoTDataSpecsDataTypeEnum.INT
     }
   }
 })
@@ -136,11 +137,11 @@ const submitForm = async () => {
 /** 重置表单 */
 const resetForm = () => {
   formData.value = {
-    dataType: DataSpecsDataType.INT,
+    dataType: IoTDataSpecsDataTypeEnum.INT,
     property: {
-      dataType: DataSpecsDataType.INT,
+      dataType: IoTDataSpecsDataTypeEnum.INT,
       dataSpecs: {
-        dataType: DataSpecsDataType.INT
+        dataType: IoTDataSpecsDataTypeEnum.INT
       }
     }
   }

+ 44 - 33
src/views/iot/thingmodel/ThingModelProperty.vue

@@ -8,7 +8,7 @@
     <el-select v-model="property.dataType" placeholder="请选择数据类型" @change="handleChange">
       <!-- ARRAY 和 STRUCT 类型数据相互嵌套时,最多支持递归嵌套 2 层(父和子) -->
       <el-option
-        v-for="option in getDataTypeOptions"
+        v-for="option in getDataTypeOptions2"
         :key="option.value"
         :label="`${option.value}(${option.label})`"
         :value="option.value"
@@ -18,19 +18,21 @@
   <!-- 数值型配置 -->
   <ThingModelNumberDataSpecs
     v-if="
-      [DataSpecsDataType.INT, DataSpecsDataType.DOUBLE, DataSpecsDataType.FLOAT].includes(
-        property.dataType || ''
-      )
+      [
+        IoTDataSpecsDataTypeEnum.INT,
+        IoTDataSpecsDataTypeEnum.DOUBLE,
+        IoTDataSpecsDataTypeEnum.FLOAT
+      ].includes(property.dataType || '')
     "
     v-model="property.dataSpecs"
   />
   <!-- 枚举型配置 -->
   <ThingModelEnumDataSpecs
-    v-if="property.dataType === DataSpecsDataType.ENUM"
+    v-if="property.dataType === IoTDataSpecsDataTypeEnum.ENUM"
     v-model="property.dataSpecsList"
   />
   <!-- 布尔型配置 -->
-  <el-form-item v-if="property.dataType === DataSpecsDataType.BOOL" label="布尔值">
+  <el-form-item v-if="property.dataType === IoTDataSpecsDataTypeEnum.BOOL" label="布尔值">
     <template v-for="(item, index) in property.dataSpecsList" :key="item.value">
       <div class="flex items-center justify-start w-1/1 mb-5px">
         <span>{{ item.value }}</span>
@@ -54,7 +56,7 @@
   </el-form-item>
   <!-- 文本型配置 -->
   <el-form-item
-    v-if="property.dataType === DataSpecsDataType.TEXT"
+    v-if="property.dataType === IoTDataSpecsDataTypeEnum.TEXT"
     label="数据长度"
     prop="property.dataSpecs.length"
   >
@@ -63,26 +65,31 @@
     </el-input>
   </el-form-item>
   <!-- 时间型配置 -->
-  <el-form-item v-if="property.dataType === DataSpecsDataType.DATE" label="时间格式" prop="date">
+  <el-form-item
+    v-if="property.dataType === IoTDataSpecsDataTypeEnum.DATE"
+    label="时间格式"
+    prop="date"
+  >
     <el-input class="w-255px!" disabled placeholder="String 类型的 UTC 时间戳(毫秒)" />
   </el-form-item>
   <!-- 数组型配置-->
   <ThingModelArrayDataSpecs
-    v-if="property.dataType === DataSpecsDataType.ARRAY"
+    v-if="property.dataType === IoTDataSpecsDataTypeEnum.ARRAY"
     v-model="property.dataSpecs"
   />
   <!-- Struct 型配置-->
   <ThingModelStructDataSpecs
-    v-if="property.dataType === DataSpecsDataType.STRUCT"
+    v-if="property.dataType === IoTDataSpecsDataTypeEnum.STRUCT"
     v-model="property.dataSpecsList"
   />
   <el-form-item v-if="!isStructDataSpecs && !isParams" label="读写类型" prop="property.accessMode">
+    <!-- TODO @AI:枚举 -->
     <el-radio-group v-model="property.accessMode">
-      <el-radio :label="ThingModelAccessMode.READ_WRITE.value">
-        {{ ThingModelAccessMode.READ_WRITE.label }}
+      <el-radio :label="IoTThingModelAccessModeEnum.READ_WRITE.value">
+        {{ IoTThingModelAccessModeEnum.READ_WRITE.label }}
       </el-radio>
-      <el-radio :label="ThingModelAccessMode.READ_ONLY.value">
-        {{ ThingModelAccessMode.READ_ONLY.label }}
+      <el-radio :label="IoTThingModelAccessModeEnum.READ_ONLY.value">
+        {{ IoTThingModelAccessModeEnum.READ_ONLY.label }}
       </el-radio>
     </el-radio-group>
   </el-form-item>
@@ -90,20 +97,19 @@
 
 <script lang="ts" setup>
 import { useVModel } from '@vueuse/core'
-import {
-  DataSpecsDataType,
-  dataTypeOptions,
-  ThingModelAccessMode,
-  validateBoolName
-} from './config'
 import {
   ThingModelArrayDataSpecs,
   ThingModelEnumDataSpecs,
   ThingModelNumberDataSpecs,
   ThingModelStructDataSpecs
 } from './dataSpecs'
-import { ThingModelProperty } from '@/api/iot/thingmodel'
+import { ThingModelProperty, validateBoolName } from '@/api/iot/thingmodel'
 import { isEmpty } from '@/utils/is'
+import {
+  getDataTypeOptions,
+  IoTDataSpecsDataTypeEnum,
+  IoTThingModelAccessModeEnum
+} from '@/views/iot/utils/constants'
 
 /** IoT 物模型属性 */
 defineOptions({ name: 'ThingModelProperty' })
@@ -111,12 +117,14 @@ defineOptions({ name: 'ThingModelProperty' })
 const props = defineProps<{ modelValue: any; isStructDataSpecs?: boolean; isParams?: boolean }>()
 const emits = defineEmits(['update:modelValue'])
 const property = useVModel(props, 'modelValue', emits) as Ref<ThingModelProperty>
-const getDataTypeOptions = computed(() => {
+const getDataTypeOptions2 = computed(() => {
   return !props.isStructDataSpecs
-    ? dataTypeOptions
-    : dataTypeOptions.filter(
-        (item) =>
-          !([DataSpecsDataType.STRUCT, DataSpecsDataType.ARRAY] as any[]).includes(item.value)
+    ? getDataTypeOptions()
+    : getDataTypeOptions().filter(
+        (item: any) =>
+          !([IoTDataSpecsDataTypeEnum.STRUCT, IoTDataSpecsDataTypeEnum.ARRAY] as any[]).includes(
+            item.value
+          )
       )
 }) // 获得数据类型列表
 
@@ -125,20 +133,23 @@ const handleChange = (dataType: any) => {
   property.value.dataSpecs = {}
   property.value.dataSpecsList = []
   // 不是列表型数据才设置 dataSpecs.dataType
-  ![DataSpecsDataType.ENUM, DataSpecsDataType.BOOL, DataSpecsDataType.STRUCT].includes(dataType) &&
-    (property.value.dataSpecs.dataType = dataType)
+  ![
+    IoTDataSpecsDataTypeEnum.ENUM,
+    IoTDataSpecsDataTypeEnum.BOOL,
+    IoTDataSpecsDataTypeEnum.STRUCT
+  ].includes(dataType) && (property.value.dataSpecs.dataType = dataType)
   switch (dataType) {
-    case DataSpecsDataType.ENUM:
+    case IoTDataSpecsDataTypeEnum.ENUM:
       property.value.dataSpecsList.push({
-        dataType: DataSpecsDataType.ENUM,
+        dataType: IoTDataSpecsDataTypeEnum.ENUM,
         name: '', // 枚举项的名称
         value: undefined // 枚举值
       })
       break
-    case DataSpecsDataType.BOOL:
+    case IoTDataSpecsDataTypeEnum.BOOL:
       for (let i = 0; i < 2; i++) {
         property.value.dataSpecsList.push({
-          dataType: DataSpecsDataType.BOOL,
+          dataType: IoTDataSpecsDataTypeEnum.BOOL,
           name: '', // 布尔值的名称
           value: i // 布尔值
         })
@@ -154,7 +165,7 @@ watch(
     if (props.isStructDataSpecs || props.isParams) {
       return
     }
-    isEmpty(val) && (property.value.accessMode = ThingModelAccessMode.READ_WRITE.value)
+    isEmpty(val) && (property.value.accessMode = IoTThingModelAccessModeEnum.READ_WRITE.value)
   },
   { immediate: true }
 )

+ 13 - 8
src/views/iot/thingmodel/ThingModelService.vue

@@ -6,24 +6,25 @@
     prop="service.callType"
   >
     <el-radio-group v-model="service.callType">
-      <el-radio :value="ThingModelServiceCallType.ASYNC.value">
-        {{ ThingModelServiceCallType.ASYNC.label }}
+      <!-- TODO @AI:使用 IoTThingModelServiceCallTypeEnum 处理下 -->
+      <el-radio :value="IoTThingModelServiceCallTypeEnum.ASYNC.value">
+        {{ IoTThingModelServiceCallTypeEnum.ASYNC.label }}
       </el-radio>
-      <el-radio :value="ThingModelServiceCallType.SYNC.value">
-        {{ ThingModelServiceCallType.SYNC.label }}
+      <el-radio :value="IoTThingModelServiceCallTypeEnum.SYNC.value">
+        {{ IoTThingModelServiceCallTypeEnum.SYNC.label }}
       </el-radio>
     </el-radio-group>
   </el-form-item>
   <el-form-item label="输入参数">
     <ThingModelInputOutputParam
       v-model="service.inputParams"
-      :direction="ThingModelParamDirection.INPUT"
+      :direction="IoTThingModelParamDirectionEnum.INPUT"
     />
   </el-form-item>
   <el-form-item label="输出参数">
     <ThingModelInputOutputParam
       v-model="service.outputParams"
-      :direction="ThingModelParamDirection.OUTPUT"
+      :direction="IoTThingModelParamDirectionEnum.OUTPUT"
     />
   </el-form-item>
 </template>
@@ -32,8 +33,11 @@
 import ThingModelInputOutputParam from './ThingModelInputOutputParam.vue'
 import { useVModel } from '@vueuse/core'
 import { ThingModelService } from '@/api/iot/thingmodel'
-import { ThingModelParamDirection, ThingModelServiceCallType } from './config'
 import { isEmpty } from '@/utils/is'
+import {
+  IoTThingModelParamDirectionEnum,
+  IoTThingModelServiceCallTypeEnum
+} from '@/views/iot/utils/constants'
 
 /** IoT 物模型服务 */
 defineOptions({ name: 'ThingModelService' })
@@ -45,7 +49,8 @@ const service = useVModel(props, 'modelValue', emits) as Ref<ThingModelService>
 // 默认选中,ASYNC 异步
 watch(
   () => service.value.callType,
-  (val: string) => isEmpty(val) && (service.value.callType = ThingModelServiceCallType.ASYNC.value),
+  (val: string) =>
+    isEmpty(val) && (service.value.callType = IoTThingModelServiceCallTypeEnum.ASYNC.value),
   { immediate: true }
 )
 </script>

+ 32 - 20
src/views/iot/thingmodel/components/DataDefinition.vue

@@ -1,56 +1,68 @@
 <template>
   <!-- 属性 -->
-  <template v-if="data.type === ThingModelType.PROPERTY">
+  <template v-if="data.type === IoTThingModelTypeEnum.PROPERTY">
     <!-- 非列表型:数值 -->
     <div
       v-if="
-        [DataSpecsDataType.INT, DataSpecsDataType.DOUBLE, DataSpecsDataType.FLOAT].includes(
-          data.property.dataType
-        )
+        [
+          IoTDataSpecsDataTypeEnum.INT,
+          IoTDataSpecsDataTypeEnum.DOUBLE,
+          IoTDataSpecsDataTypeEnum.FLOAT
+        ].includes(data.property.dataType)
       "
     >
       取值范围:{{ `${data.property.dataSpecs.min}~${data.property.dataSpecs.max}` }}
     </div>
     <!-- 非列表型:文本 -->
-    <div v-if="DataSpecsDataType.TEXT === data.property.dataType">
+    <div v-if="IoTDataSpecsDataTypeEnum.TEXT === data.property.dataType">
       数据长度:{{ data.property.dataSpecs.length }}
     </div>
     <!-- 列表型: 数组、结构、时间(特殊) -->
     <div
       v-if="
-        [DataSpecsDataType.ARRAY, DataSpecsDataType.STRUCT, DataSpecsDataType.DATE].includes(
-          data.property.dataType
-        )
+        [
+          IoTDataSpecsDataTypeEnum.ARRAY,
+          IoTDataSpecsDataTypeEnum.STRUCT,
+          IoTDataSpecsDataTypeEnum.DATE
+        ].includes(data.property.dataType)
       "
     >
       -
     </div>
     <!-- 列表型: 布尔值、枚举 -->
-    <div v-if="[DataSpecsDataType.BOOL, DataSpecsDataType.ENUM].includes(data.property.dataType)">
-      <div> {{ DataSpecsDataType.BOOL === data.property.dataType ? '布尔值' : '枚举值' }}:</div>
+    <div
+      v-if="
+        [IoTDataSpecsDataTypeEnum.BOOL, IoTDataSpecsDataTypeEnum.ENUM].includes(
+          data.property.dataType
+        )
+      "
+    >
+      <div>
+        {{ IoTDataSpecsDataTypeEnum.BOOL === data.property.dataType ? '布尔值' : '枚举值' }}:
+      </div>
       <div v-for="item in data.property.dataSpecsList" :key="item.value">
         {{ `${item.name}-${item.value}` }}
       </div>
     </div>
   </template>
   <!-- 服务 -->
-  <div v-if="data.type === ThingModelType.SERVICE">
-    调用方式:{{ getCallTypeByValue(data.service!.callType) }}
+  <div v-if="data.type === IoTThingModelTypeEnum.SERVICE">
+    调用方式:{{ getThingModelServiceCallTypeLabel(data.service!.callType) }}
   </div>
   <!-- 事件 -->
-  <div v-if="data.type === ThingModelType.EVENT">
-    事件类型:{{ getEventTypeByValue(data.event!.type) }}
+  <div v-if="data.type === IoTThingModelTypeEnum.EVENT">
+    事件类型:{{ getEventTypeLabel(data.event!.type) }}
   </div>
 </template>
 
 <script lang="ts" setup>
-import {
-  DataSpecsDataType,
-  getCallTypeByValue,
-  getEventTypeByValue,
-  ThingModelType
-} from '@/views/iot/thingmodel/config'
 import { ThingModelData } from '@/api/iot/thingmodel'
+import {
+  getEventTypeLabel,
+  getThingModelServiceCallTypeLabel,
+  IoTDataSpecsDataTypeEnum,
+  IoTThingModelTypeEnum
+} from '@/views/iot/utils/constants'
 
 /** 数据定义展示组件 */
 defineOptions({ name: 'DataDefinition' })

+ 0 - 214
src/views/iot/thingmodel/config.ts

@@ -1,214 +0,0 @@
-import { isEmpty } from '@/utils/is'
-
-/** dataSpecs 数值型数据结构 */
-export interface DataSpecsNumberDataVO {
-  dataType: 'int' | 'float' | 'double' // 数据类型,取值为 INT、FLOAT 或 DOUBLE
-  max: string // 最大值,必须与 dataType 设置一致,且为 STRING 类型
-  min: string // 最小值,必须与 dataType 设置一致,且为 STRING 类型
-  step: string // 步长,必须与 dataType 设置一致,且为 STRING 类型
-  precise?: string // 精度,当 dataType 为 FLOAT 或 DOUBLE 时可选
-  defaultValue?: string // 默认值,可选
-  unit: string // 单位的符号
-  unitName: string // 单位的名称
-}
-
-/** dataSpecs 枚举型数据结构 */
-export interface DataSpecsEnumOrBoolDataVO {
-  dataType: 'enum' | 'bool'
-  defaultValue?: string // 默认值,可选
-  name: string // 枚举项的名称
-  value: number | undefined // 枚举值
-}
-
-/** 属性值的数据类型 */
-export const DataSpecsDataType = {
-  INT: 'int',
-  FLOAT: 'float',
-  DOUBLE: 'double',
-  ENUM: 'enum',
-  BOOL: 'bool',
-  TEXT: 'text',
-  DATE: 'date',
-  STRUCT: 'struct',
-  ARRAY: 'array'
-} as const
-
-/** 物体模型数据类型配置项 */
-export const dataTypeOptions = [
-  { value: DataSpecsDataType.INT, label: '整数型' },
-  { value: DataSpecsDataType.FLOAT, label: '单精度浮点型' },
-  { value: DataSpecsDataType.DOUBLE, label: '双精度浮点型' },
-  { value: DataSpecsDataType.ENUM, label: '枚举型' },
-  { value: DataSpecsDataType.BOOL, label: '布尔型' },
-  { value: DataSpecsDataType.TEXT, label: '文本型' },
-  { value: DataSpecsDataType.DATE, label: '时间型' },
-  { value: DataSpecsDataType.STRUCT, label: '结构体' },
-  { value: DataSpecsDataType.ARRAY, label: '数组' }
-]
-
-/** 获得物体模型数据类型配置项名称 */
-export const getDataTypeOptionsLabel = (value: string) => {
-  if (isEmpty(value)) {
-    return value
-  }
-  const dataType = dataTypeOptions.find((option) => option.value === value)
-  return dataType && `${dataType.value}(${dataType.label})`
-}
-
-// TODO @puhui999:使用 ThingModelTypeEnum 替换
-// IOT 产品物模型类型枚举类
-export const ThingModelType = {
-  PROPERTY: 1, // 属性
-  SERVICE: 2, // 服务
-  EVENT: 3 // 事件
-} as const
-
-// IOT 产品物模型访问模式枚举类
-export const ThingModelAccessMode = {
-  READ_WRITE: {
-    label: '读写',
-    value: 'rw'
-  },
-  READ_ONLY: {
-    label: '只读',
-    value: 'r'
-  }
-} as const
-
-// IOT 产品物模型服务调用方式枚举
-export const ThingModelServiceCallType = {
-  ASYNC: {
-    label: '异步调用',
-    value: 'async'
-  },
-  SYNC: {
-    label: '同步调用',
-    value: 'sync'
-  }
-} as const
-export const getCallTypeByValue = (value: string): string | undefined =>
-  Object.values(ThingModelServiceCallType).find((type) => type.value === value)?.label
-
-// IOT 产品物模型事件类型枚举
-export const ThingModelEventType = {
-  INFO: {
-    label: '信息',
-    value: 'info'
-  },
-  ALERT: {
-    label: '告警',
-    value: 'alert'
-  },
-  ERROR: {
-    label: '故障',
-    value: 'error'
-  }
-} as const
-export const getEventTypeByValue = (value: string): string | undefined =>
-  Object.values(ThingModelEventType).find((type) => type.value === value)?.label
-
-// IOT 产品物模型参数是输入参数还是输出参数
-export const ThingModelParamDirection = {
-  INPUT: 'input', // 输入参数
-  OUTPUT: 'output' // 输出参数
-} as const
-
-/** 公共校验规则 */
-export const ThingModelFormRules = {
-  name: [
-    { required: true, message: '功能名称不能为空', trigger: 'blur' },
-    {
-      pattern: /^[\u4e00-\u9fa5a-zA-Z0-9][\u4e00-\u9fa5a-zA-Z0-9\-_/\.]{0,29}$/,
-      message:
-        '支持中文、大小写字母、日文、数字、短划线、下划线、斜杠和小数点,必须以中文、英文或数字开头,不超过 30 个字符',
-      trigger: 'blur'
-    }
-  ],
-  type: [{ required: true, message: '功能类型不能为空', trigger: 'blur' }],
-  identifier: [
-    { required: true, message: '标识符不能为空', trigger: 'blur' },
-    {
-      pattern: /^[a-zA-Z0-9_]{1,50}$/,
-      message: '支持大小写字母、数字和下划线,不超过 50 个字符',
-      trigger: 'blur'
-    },
-    {
-      validator: (_: any, value: string, callback: any) => {
-        const reservedKeywords = ['set', 'get', 'post', 'property', 'event', 'time', 'value']
-        if (reservedKeywords.includes(value)) {
-          callback(
-            new Error(
-              'set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义'
-            )
-          )
-        } else if (/^\d+$/.test(value)) {
-          callback(new Error('标识符不能是纯数字'))
-        } else {
-          callback()
-        }
-      },
-      trigger: 'blur'
-    }
-  ],
-  'property.dataSpecs.childDataType': [{ required: true, message: '元素类型不能为空' }],
-  'property.dataSpecs.size': [
-    { required: true, message: '元素个数不能为空' },
-    {
-      validator: (_: any, value: any, callback: any) => {
-        if (isEmpty(value)) {
-          callback(new Error('元素个数不能为空'))
-          return
-        }
-        if (isNaN(Number(value))) {
-          callback(new Error('元素个数必须是数字'))
-          return
-        }
-        callback()
-      },
-      trigger: 'blur'
-    }
-  ],
-  'property.dataSpecs.length': [
-    { required: true, message: '请输入文本字节长度', trigger: 'blur' },
-    {
-      validator: (_: any, value: any, callback: any) => {
-        if (isEmpty(value)) {
-          callback(new Error('文本长度不能为空'))
-          return
-        }
-        if (isNaN(Number(value))) {
-          callback(new Error('文本长度必须是数字'))
-          return
-        }
-        callback()
-      },
-      trigger: 'blur'
-    }
-  ],
-  'property.accessMode': [{ required: true, message: '请选择读写类型', trigger: 'change' }]
-}
-
-/** 校验布尔值名称 */
-export const validateBoolName = (_: any, value: string, callback: any) => {
-  if (isEmpty(value)) {
-    callback(new Error('布尔值名称不能为空'))
-    return
-  }
-  // 检查开头字符
-  if (!/^[\u4e00-\u9fa5a-zA-Z0-9]/.test(value)) {
-    callback(new Error('布尔值名称必须以中文、英文字母或数字开头'))
-    return
-  }
-  // 检查整体格式
-  if (!/^[\u4e00-\u9fa5a-zA-Z0-9][a-zA-Z0-9\u4e00-\u9fa5_-]*$/.test(value)) {
-    callback(new Error('布尔值名称只能包含中文、英文字母、数字、下划线和短划线'))
-    return
-  }
-  // 检查长度(一个中文算一个字符)
-  if (value.length > 20) {
-    callback(new Error('布尔值名称长度不能超过 20 个字符'))
-    return
-  }
-
-  callback()
-}

+ 9 - 5
src/views/iot/thingmodel/dataSpecs/ThingModelArrayDataSpecs.vue

@@ -2,11 +2,15 @@
 <template>
   <el-form-item label="元素类型" prop="property.dataSpecs.childDataType">
     <el-radio-group v-model="dataSpecs.childDataType" @change="handleChange">
-      <template v-for="item in dataTypeOptions" :key="item.value">
+      <template v-for="item in getDataTypeOptions()" :key="item.value">
         <el-radio
           v-if="
             !(
-              [DataSpecsDataType.ENUM, DataSpecsDataType.ARRAY, DataSpecsDataType.DATE] as any[]
+              [
+                IoTDataSpecsDataTypeEnum.ENUM,
+                IoTDataSpecsDataTypeEnum.ARRAY,
+                IoTDataSpecsDataTypeEnum.DATE
+              ] as any[]
             ).includes(item.value)
           "
           :value="item.value"
@@ -22,15 +26,15 @@
   </el-form-item>
   <!-- Struct 型配置-->
   <ThingModelStructDataSpecs
-    v-if="dataSpecs.childDataType === DataSpecsDataType.STRUCT"
+    v-if="dataSpecs.childDataType === IoTDataSpecsDataTypeEnum.STRUCT"
     v-model="dataSpecs.dataSpecsList"
   />
 </template>
 
 <script lang="ts" setup>
 import { useVModel } from '@vueuse/core'
-import { DataSpecsDataType, dataTypeOptions } from '../config'
 import ThingModelStructDataSpecs from './ThingModelStructDataSpecs.vue'
+import { getDataTypeOptions, IoTDataSpecsDataTypeEnum } from '@/views/iot/utils/constants'
 
 /** 数组型的 dataSpecs 配置组件 */
 defineOptions({ name: 'ThingModelArrayDataSpecs' })
@@ -41,7 +45,7 @@ const dataSpecs = useVModel(props, 'modelValue', emits) as Ref<any>
 
 /** 元素类型改变时间。当值为 struct 时,对 dataSpecs 中的 dataSpecsList 进行初始化 */
 const handleChange = (val: string) => {
-  if (val !== DataSpecsDataType.STRUCT) {
+  if (val !== IoTDataSpecsDataTypeEnum.STRUCT) {
     return
   }
 

+ 3 - 2
src/views/iot/thingmodel/dataSpecs/ThingModelEnumDataSpecs.vue

@@ -44,8 +44,9 @@
 
 <script lang="ts" setup>
 import { useVModel } from '@vueuse/core'
-import { DataSpecsDataType, DataSpecsEnumOrBoolDataVO } from '../config'
 import { isEmpty } from '@/utils/is'
+import { IoTDataSpecsDataTypeEnum } from '@/views/iot/utils/constants'
+import { DataSpecsEnumOrBoolDataVO } from '@/api/iot/thingmodel'
 
 /** 枚举型的 dataSpecs 配置组件 */
 defineOptions({ name: 'ThingModelEnumDataSpecs' })
@@ -58,7 +59,7 @@ const message = useMessage()
 /** 添加枚举项 */
 const addEnum = () => {
   dataSpecsList.value.push({
-    dataType: DataSpecsDataType.ENUM,
+    dataType: IoTDataSpecsDataTypeEnum.ENUM,
     name: '', // 枚举项的名称
     value: undefined // 枚举值
   })

+ 1 - 1
src/views/iot/thingmodel/dataSpecs/ThingModelNumberDataSpecs.vue

@@ -59,8 +59,8 @@
 
 <script lang="ts" setup>
 import { useVModel } from '@vueuse/core'
-import { DataSpecsNumberDataVO } from '../config'
 import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
+import { DataSpecsNumberDataVO } from '@/api/iot/thingmodel'
 
 /** 数值型的 dataSpecs 配置组件 */
 defineOptions({ name: 'ThingModelNumberDataSpecs' })

+ 7 - 6
src/views/iot/thingmodel/dataSpecs/ThingModelStructDataSpecs.vue

@@ -49,8 +49,9 @@
 <script lang="ts" setup>
 import { useVModel } from '@vueuse/core'
 import ThingModelProperty from '../ThingModelProperty.vue'
-import { DataSpecsDataType, ThingModelFormRules } from '../config'
 import { isEmpty } from '@/utils/is'
+import { IoTDataSpecsDataTypeEnum } from '@/views/iot/utils/constants'
+import { ThingModelFormRules } from '@/api/iot/thingmodel'
 
 /** Struct 型的 dataSpecs 配置组件 */
 defineOptions({ name: 'ThingModelStructDataSpecs' })
@@ -64,9 +65,9 @@ const formLoading = ref(false) // 表单的加载中:1)修改时的数据加
 const structFormRef = ref() // 表单 ref
 const formData = ref<any>({
   property: {
-    dataType: DataSpecsDataType.INT,
+    dataType: IoTDataSpecsDataTypeEnum.INT,
     dataSpecs: {
-      dataType: DataSpecsDataType.INT
+      dataType: IoTDataSpecsDataTypeEnum.INT
     }
   }
 })
@@ -107,7 +108,7 @@ const submitForm = async () => {
       identifier: data.identifier,
       name: data.name,
       description: data.description,
-      dataType: DataSpecsDataType.STRUCT,
+      dataType: IoTDataSpecsDataTypeEnum.STRUCT,
       childDataType: data.property.dataType,
       dataSpecs:
         !!data.property.dataSpecs && Object.keys(data.property.dataSpecs).length > 1
@@ -137,9 +138,9 @@ const submitForm = async () => {
 const resetForm = () => {
   formData.value = {
     property: {
-      dataType: DataSpecsDataType.INT,
+      dataType: IoTDataSpecsDataTypeEnum.INT,
       dataSpecs: {
-        dataType: DataSpecsDataType.INT
+        dataType: IoTDataSpecsDataTypeEnum.INT
       }
     }
   }

+ 5 - 7
src/views/iot/thingmodel/index.vue

@@ -55,7 +55,7 @@
         <el-table-column align="center" label="标识符" prop="identifier" />
         <el-table-column align="center" label="数据类型" prop="identifier">
           <template #default="{ row }">
-            {{ dataTypeOptionsLabel(row.property?.dataType) ?? '-' }}
+            {{ getDataTypeOptionsLabel(row.property?.dataType) ?? '-' }}
           </template>
         </el-table-column>
         <el-table-column align="left" label="数据定义" prop="identifier">
@@ -96,7 +96,7 @@
 
   <!-- 表单弹窗:添加/修改 -->
   <ThingModelForm ref="formRef" @success="getList" />
-  <ThingModelTSL ref="thingModelTSLRef" />
+  <ThingModelTSL ref="tslRef" />
 </template>
 <script lang="ts" setup>
 import { ThingModelApi, ThingModelData } from '@/api/iot/thingmodel'
@@ -104,8 +104,7 @@ import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
 import ThingModelForm from './ThingModelForm.vue'
 import ThingModelTSL from './ThingModelTSL.vue'
 import { ProductVO } from '@/api/iot/product/product'
-import { IOT_PROVIDE_KEY } from '@/views/iot/utils/constants'
-import { getDataTypeOptionsLabel } from './config'
+import { getDataTypeOptionsLabel, IOT_PROVIDE_KEY } from '@/views/iot/utils/constants'
 import { DataDefinition } from './components'
 
 defineOptions({ name: 'IoTThingModel' })
@@ -125,7 +124,6 @@ const queryParams = reactive({
 
 const queryFormRef = ref() // 搜索的表单
 const product = inject<Ref<ProductVO>>(IOT_PROVIDE_KEY.PRODUCT) // 注入产品信息
-const dataTypeOptionsLabel = computed(() => (value: string) => getDataTypeOptionsLabel(value)) // 解析数据类型
 
 /** 查询列表 */
 const getList = async () => {
@@ -153,9 +151,9 @@ const openForm = (type: string, id?: number) => {
 }
 
 /** 展示物模型 TSL */
-const thingModelTSLRef = ref()
+const tslRef = ref()
 const openTSL = () => {
-  thingModelTSLRef.value?.open()
+  tslRef.value?.open()
 }
 
 /** 删除按钮操作 */

+ 95 - 0
src/views/iot/utils/constants.ts

@@ -1,8 +1,17 @@
+import { isEmpty } from '@/utils/is'
+
 /** iot 依赖注入 KEY */
 export const IOT_PROVIDE_KEY = {
   PRODUCT: 'IOT_PRODUCT'
 }
 
+// IOT 产品物模型类型枚举类
+export const IoTThingModelTypeEnum = {
+  PROPERTY: 1, // 属性
+  SERVICE: 2, // 服务
+  EVENT: 3 // 事件
+} as const
+
 /**
  * IoT 设备消息的方法枚举
  */
@@ -54,3 +63,89 @@ export const IotThingModelTypeEnum = {
   SERVICE: 2, // 服务
   EVENT: 3 // 事件
 }
+
+// IOT 产品物模型服务调用方式枚举
+export const IoTThingModelServiceCallTypeEnum = {
+  ASYNC: {
+    label: '异步调用',
+    value: 'async'
+  },
+  SYNC: {
+    label: '同步调用',
+    value: 'sync'
+  }
+} as const
+export const getThingModelServiceCallTypeLabel = (value: string): string | undefined =>
+  Object.values(IoTThingModelServiceCallTypeEnum).find((type) => type.value === value)?.label
+
+// IOT 产品物模型事件类型枚举
+export const IoTThingModelEventTypeEnum = {
+  INFO: {
+    label: '信息',
+    value: 'info'
+  },
+  ALERT: {
+    label: '告警',
+    value: 'alert'
+  },
+  ERROR: {
+    label: '故障',
+    value: 'error'
+  }
+} as const
+export const getEventTypeLabel = (value: string): string | undefined =>
+  Object.values(IoTThingModelEventTypeEnum).find((type) => type.value === value)?.label
+
+// IOT 产品物模型参数是输入参数还是输出参数
+export const IoTThingModelParamDirectionEnum = {
+  INPUT: 'input', // 输入参数
+  OUTPUT: 'output' // 输出参数
+} as const
+
+// IOT 产品物模型访问模式枚举类
+export const IoTThingModelAccessModeEnum = {
+  READ_WRITE: {
+    label: '读写',
+    value: 'rw'
+  },
+  READ_ONLY: {
+    label: '只读',
+    value: 'r'
+  }
+} as const
+
+/** 属性值的数据类型 */
+export const IoTDataSpecsDataTypeEnum = {
+  INT: 'int',
+  FLOAT: 'float',
+  DOUBLE: 'double',
+  ENUM: 'enum',
+  BOOL: 'bool',
+  TEXT: 'text',
+  DATE: 'date',
+  STRUCT: 'struct',
+  ARRAY: 'array'
+} as const
+
+export const getDataTypeOptions = () => {
+  return [
+    { value: IoTDataSpecsDataTypeEnum.INT, label: '整数型' },
+    { value: IoTDataSpecsDataTypeEnum.FLOAT, label: '单精度浮点型' },
+    { value: IoTDataSpecsDataTypeEnum.DOUBLE, label: '双精度浮点型' },
+    { value: IoTDataSpecsDataTypeEnum.ENUM, label: '枚举型' },
+    { value: IoTDataSpecsDataTypeEnum.BOOL, label: '布尔型' },
+    { value: IoTDataSpecsDataTypeEnum.TEXT, label: '文本型' },
+    { value: IoTDataSpecsDataTypeEnum.DATE, label: '时间型' },
+    { value: IoTDataSpecsDataTypeEnum.STRUCT, label: '结构体' },
+    { value: IoTDataSpecsDataTypeEnum.ARRAY, label: '数组' }
+  ]
+}
+
+/** 获得物体模型数据类型配置项名称 */
+export const getDataTypeOptionsLabel = (value: string) => {
+  if (isEmpty(value)) {
+    return value
+  }
+  const dataType = getDataTypeOptions().find((option) => option.value === value)
+  return dataType && `${dataType.value}(${dataType.label})`
+}