mirror of
https://gitee.com/myxzgzs/boyue-ui-admin-vue3
synced 2025-08-08 16:32:43 +08:00
【代码完善】IOT: ThingModel StructDataSpecs 组件
This commit is contained in:
parent
d5f3d4006a
commit
ce7dc927ae
@ -8,8 +8,8 @@
|
||||
<el-tab-pane label="Topic 类列表" name="topic">
|
||||
<ProductTopic v-if="activeTab === 'topic'" :product="product" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="功能定义" lazy name="thinkModel">
|
||||
<IoTProductThinkModel ref="thinkModelRef" />
|
||||
<el-tab-pane label="功能定义" lazy name="thingModel">
|
||||
<IoTProductThingModel ref="thingModelRef" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="消息解析" name="message" />
|
||||
<el-tab-pane label="服务端订阅" name="subscription" />
|
||||
@ -22,7 +22,7 @@ import { DeviceApi } from '@/api/iot/device/device'
|
||||
import ProductDetailsHeader from './ProductDetailsHeader.vue'
|
||||
import ProductDetailsInfo from './ProductDetailsInfo.vue'
|
||||
import ProductTopic from './ProductTopic.vue'
|
||||
import IoTProductThinkModel from '@/views/iot/thingmodel/index.vue'
|
||||
import IoTProductThingModel from '@/views/iot/thingmodel/index.vue'
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { IOT_PROVIDE_KEY } from '@/views/iot/utils/constants'
|
||||
|
@ -97,7 +97,9 @@
|
||||
</div>
|
||||
<div class="mb-2.5 last:mb-0">
|
||||
<span class="text-[#717c8e] mr-2.5">产品标识</span>
|
||||
<span class="text-[#0b1d30] whitespace-normal break-all">{{ item.productKey }}</span>
|
||||
<span class="text-[#0b1d30] whitespace-normal break-all">
|
||||
{{ item.productKey }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-[100px] h-[100px]">
|
||||
@ -309,7 +311,7 @@ const openObjectModel = (item: ProductVO) => {
|
||||
push({
|
||||
name: 'IoTProductDetail',
|
||||
params: { id: item.id },
|
||||
query: { tab: 'thinkModel' }
|
||||
query: { tab: 'thingModel' }
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,9 @@
|
||||
prop="property.dataType"
|
||||
>
|
||||
<el-select v-model="property.dataType" placeholder="请选择数据类型" @change="handleChange">
|
||||
<!-- ARRAY 和 STRUCT 类型数据相互嵌套时,最多支持递归嵌套2层(父和子) -->
|
||||
<el-option
|
||||
v-for="option in dataTypeOptions"
|
||||
v-for="option in getDataTypeOptions"
|
||||
:key="option.value"
|
||||
:label="option.label"
|
||||
:value="option.value"
|
||||
@ -14,7 +15,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 数值型配置 -->
|
||||
<ThingModelNumberTypeDataSpecs
|
||||
<ThingModelNumberDataSpecs
|
||||
v-if="
|
||||
[DataSpecsDataType.INT, DataSpecsDataType.DOUBLE, DataSpecsDataType.FLOAT].includes(
|
||||
property.dataType || ''
|
||||
@ -23,7 +24,7 @@
|
||||
v-model="property.dataSpecs"
|
||||
/>
|
||||
<!-- 枚举型配置 -->
|
||||
<ThingModelEnumTypeDataSpecs
|
||||
<ThingModelEnumDataSpecs
|
||||
v-if="property.dataType === DataSpecsDataType.ENUM"
|
||||
v-model="property.dataSpecsList"
|
||||
/>
|
||||
@ -74,12 +75,17 @@
|
||||
<el-input class="w-255px!" disabled placeholder="String类型的UTC时间戳(毫秒)" />
|
||||
</el-form-item>
|
||||
<!-- 数组型配置-->
|
||||
<ThingModelArrayTypeDataSpecs
|
||||
<ThingModelArrayDataSpecs
|
||||
v-if="property.dataType === DataSpecsDataType.ARRAY"
|
||||
v-model="property.dataSpecs"
|
||||
/>
|
||||
<!-- TODO puhui999: Struct 属性待完善 -->
|
||||
<!-- Struct 型配置-->
|
||||
<ThingModelStructDataSpecs
|
||||
v-if="property.dataType === DataSpecsDataType.STRUCT"
|
||||
v-model="property.dataSpecsList"
|
||||
/>
|
||||
<el-form-item
|
||||
v-if="!isStructDataSpecs"
|
||||
:rules="[{ required: true, message: '请选择读写类型', trigger: 'change' }]"
|
||||
label="读写类型"
|
||||
prop="property.accessMode"
|
||||
@ -104,20 +110,28 @@
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import { DataSpecsDataType, dataTypeOptions } from './config'
|
||||
import {
|
||||
ThingModelArrayTypeDataSpecs,
|
||||
ThingModelEnumTypeDataSpecs,
|
||||
ThingModelNumberTypeDataSpecs
|
||||
ThingModelArrayDataSpecs,
|
||||
ThingModelEnumDataSpecs,
|
||||
ThingModelNumberDataSpecs
|
||||
} from './dataSpecs'
|
||||
import { ThingModelProperty } from 'src/api/iot/thingmodel'
|
||||
import ThingModelStructDataSpecs from './ThingModelStructDataSpecs.vue'
|
||||
import { ThingModelProperty } from '@/api/iot/thingmodel'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
|
||||
/** IoT 物模型数据 */
|
||||
defineOptions({ name: 'ThingModelDataSpecs' })
|
||||
|
||||
const props = defineProps<{ modelValue: any }>()
|
||||
const props = defineProps<{ modelValue: any; isStructDataSpecs?: boolean }>()
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
const property = useVModel(props, 'modelValue', emits) as Ref<ThingModelProperty>
|
||||
|
||||
const getDataTypeOptions = computed(() => {
|
||||
return !props.isStructDataSpecs
|
||||
? dataTypeOptions
|
||||
: dataTypeOptions.filter(
|
||||
(item) =>
|
||||
!([DataSpecsDataType.STRUCT, DataSpecsDataType.ARRAY] as any[]).includes(item.value)
|
||||
)
|
||||
}) // 获得数据类型列表
|
||||
/** 属性值的数据类型切换时初始化相关数据 */
|
||||
const handleChange = (dataType: any) => {
|
||||
property.value.dataSpecsList = []
|
||||
|
@ -4,7 +4,7 @@
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
:rules="ThingModelFormRules"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="功能类型" prop="type">
|
||||
@ -41,9 +41,9 @@
|
||||
<script lang="ts" setup>
|
||||
import { ProductVO } from '@/api/iot/product/product'
|
||||
import ThingModelDataSpecs from './ThingModelDataSpecs.vue'
|
||||
import { ProductFunctionTypeEnum, ThingModelApi, ThingModelData } from 'src/api/iot/thingmodel'
|
||||
import { ProductFunctionTypeEnum, ThingModelApi, ThingModelData } from '@/api/iot/thingmodel'
|
||||
import { IOT_PROVIDE_KEY } from '@/views/iot/utils/constants'
|
||||
import { DataSpecsDataType } from './config'
|
||||
import { DataSpecsDataType, ThingModelFormRules } from './config'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
|
||||
@ -69,43 +69,7 @@ const formData = ref<ThingModelData>({
|
||||
}
|
||||
}
|
||||
})
|
||||
const formRules = reactive({
|
||||
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'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
|
149
src/views/iot/thingmodel/ThingModelStructDataSpecs.vue
Normal file
149
src/views/iot/thingmodel/ThingModelStructDataSpecs.vue
Normal file
@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<!-- struct 数据展示 -->
|
||||
<el-form-item label="JSON 对象">
|
||||
<div
|
||||
v-for="(item, index) in dataSpecsList"
|
||||
:key="index"
|
||||
class="w-1/1 struct-item flex justify-between px-10px mb-10px"
|
||||
>
|
||||
<span>参数名称:{{ item.name }}</span>
|
||||
<div class="btn">
|
||||
<el-button link type="primary" @click="openStructForm(item)">编辑</el-button>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button link type="danger" @click="deleteStructItem(index)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-button link type="primary" @click="openStructForm(null)">+新增参数</el-button>
|
||||
</el-form-item>
|
||||
|
||||
<!-- struct 表单 -->
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle" append-to-body>
|
||||
<el-form
|
||||
ref="structFormRef"
|
||||
v-loading="formLoading"
|
||||
:model="formData"
|
||||
:rules="ThingModelFormRules"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="参数名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入功能名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="标识符" prop="identifier">
|
||||
<el-input v-model="formData.identifier" placeholder="请输入标识符" />
|
||||
</el-form-item>
|
||||
<!-- 属性配置 -->
|
||||
<ThingModelDataSpecs v-model="formData.property" is-struct-data-specs />
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import ThingModelDataSpecs from '@/views/iot/thingmodel/ThingModelDataSpecs.vue'
|
||||
import { DataSpecsDataType, ThingModelFormRules } from '@/views/iot/thingmodel/config'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
|
||||
/** Struct 型的 dataSpecs 配置组件 */
|
||||
defineOptions({ name: 'ThingModelStructDataSpecs' })
|
||||
|
||||
const props = defineProps<{ modelValue: any }>()
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
const dataSpecsList = useVModel(props, 'modelValue', emits) as Ref<any[]>
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('新增参数') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const structFormRef = ref() // 表单 ref
|
||||
const formData = ref<any>({
|
||||
property: {
|
||||
dataType: DataSpecsDataType.INT,
|
||||
dataSpecs: {
|
||||
dataType: DataSpecsDataType.INT
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/** 打开 struct 表单 */
|
||||
const openStructForm = (val: any) => {
|
||||
dialogVisible.value = true
|
||||
resetForm()
|
||||
if (isEmpty(val)) {
|
||||
return
|
||||
}
|
||||
// 编辑时回显数据
|
||||
formData.value = {
|
||||
identifier: val.identifier,
|
||||
name: val.name,
|
||||
description: val.description,
|
||||
property: {
|
||||
dataType: val.childDataType,
|
||||
dataSpecs: val.dataSpecs,
|
||||
dataSpecsList: val.dataSpecsList
|
||||
}
|
||||
}
|
||||
}
|
||||
/** 删除 struct 项 */
|
||||
const deleteStructItem = (index: number) => {
|
||||
dataSpecsList.value.splice(index, 1)
|
||||
}
|
||||
|
||||
/** 添加参数 */
|
||||
const submitForm = async () => {
|
||||
await structFormRef.value.validate()
|
||||
|
||||
try {
|
||||
const data = unref(formData)
|
||||
// 构建数据对象
|
||||
const item = {
|
||||
identifier: data.identifier,
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
dataType: DataSpecsDataType.STRUCT,
|
||||
childDataType: data.property.dataType,
|
||||
dataSpecs:
|
||||
!!data.property.dataSpecs && Object.keys(data.property.dataSpecs).length > 1
|
||||
? data.property.dataSpecs
|
||||
: undefined,
|
||||
dataSpecsList: data.property.dataSpecsList
|
||||
}
|
||||
|
||||
// 查找是否已有相同 identifier 的项
|
||||
const existingIndex = dataSpecsList.value.findIndex(
|
||||
(spec) => spec.identifier === data.identifier
|
||||
)
|
||||
if (existingIndex > -1) {
|
||||
// 更新已有项
|
||||
dataSpecsList.value[existingIndex] = item
|
||||
} else {
|
||||
// 添加新项
|
||||
dataSpecsList.value.push(item)
|
||||
}
|
||||
} finally {
|
||||
// 隐藏对话框
|
||||
dialogVisible.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
property: {
|
||||
dataType: DataSpecsDataType.INT,
|
||||
dataSpecs: {
|
||||
dataType: DataSpecsDataType.INT
|
||||
}
|
||||
}
|
||||
}
|
||||
structFormRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.struct-item {
|
||||
background-color: #e4f2fd;
|
||||
}
|
||||
</style>
|
@ -1,3 +1,5 @@
|
||||
import {isEmpty} from '@/utils/is'
|
||||
|
||||
/** dataSpecs 数值型数据结构 */
|
||||
export interface DataSpecsNumberDataVO {
|
||||
dataType: 'int' | 'float' | 'double' // 数据类型,取值为 INT、FLOAT 或 DOUBLE
|
||||
@ -48,3 +50,60 @@ export const dataTypeOptions = [
|
||||
export const getDataTypeOptionsLabel = (value: string) => {
|
||||
return dataTypeOptions.find((option) => option.value === value)?.label
|
||||
}
|
||||
|
||||
/** 公共校验规则 */
|
||||
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'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,9 +1,5 @@
|
||||
<template>
|
||||
<el-form-item
|
||||
:rules="[{ required: true, message: '元素类型不能为空' }]"
|
||||
label="元素类型"
|
||||
prop="property.dataSpecs.childDataType"
|
||||
>
|
||||
<el-form-item label="元素类型" prop="property.dataSpecs.childDataType">
|
||||
<el-radio-group v-model="dataSpecs.childDataType">
|
||||
<template v-for="item in dataTypeOptions" :key="item.value">
|
||||
<el-radio
|
||||
@ -19,14 +15,7 @@
|
||||
</template>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:rules="[
|
||||
{ required: true, message: '元素个数不能为空' },
|
||||
{ validator: validateSize, trigger: 'blur' }
|
||||
]"
|
||||
label="元素个数"
|
||||
prop="property.dataSpecs.size"
|
||||
>
|
||||
<el-form-item label="元素个数" prop="property.dataSpecs.size">
|
||||
<el-input v-model="dataSpecs.size" placeholder="请输入数组中的元素个数" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
@ -34,29 +23,13 @@
|
||||
<script lang="ts" setup>
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import { DataSpecsDataType, dataTypeOptions } from '../config'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
|
||||
// TODO @puhui999:参数校验,是不是还是定义一个变量,统一管,好阅读点哈?
|
||||
|
||||
/** 数组型的 dataSpecs 配置组件 */
|
||||
defineOptions({ name: 'ThingModelArrayTypeDataSpecs' })
|
||||
defineOptions({ name: 'ThingModelArrayDataSpecs' })
|
||||
|
||||
const props = defineProps<{ modelValue: any }>()
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
const dataSpecs = useVModel(props, 'modelValue', emits) as Ref<any>
|
||||
|
||||
/** 校验元素个数 */
|
||||
const validateSize = (_: any, value: any, callback: any) => {
|
||||
if (isEmpty(value)) {
|
||||
callback(new Error('元素个数不能为空'))
|
||||
return
|
||||
}
|
||||
if (isNaN(Number(value))) {
|
||||
callback(new Error('元素个数必须是数字'))
|
||||
return
|
||||
}
|
||||
callback()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -48,7 +48,7 @@ import { DataSpecsDataType, DataSpecsEnumOrBoolDataVO } from '../config'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
|
||||
/** 枚举型的 dataSpecs 配置组件 */
|
||||
defineOptions({ name: 'ThingModelEnumTypeDataSpecs' })
|
||||
defineOptions({ name: 'ThingModelEnumDataSpecs' })
|
||||
|
||||
const props = defineProps<{ modelValue: any }>()
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
@ -113,7 +113,6 @@ const validateEnumName = (_: any, value: string, callback: any) => {
|
||||
callback(new Error('枚举描述长度不能超过20个字符'))
|
||||
return
|
||||
}
|
||||
|
||||
callback()
|
||||
}
|
||||
|
||||
@ -147,7 +146,6 @@ const validateEnumList = (_: any, __: any, callback: any) => {
|
||||
callback(new Error('存在重复的枚举值'))
|
||||
return
|
||||
}
|
||||
|
||||
callback()
|
||||
}
|
||||
</script>
|
@ -62,7 +62,7 @@ import { UnifyUnitSpecsDTO } from '@/views/iot/utils/constants'
|
||||
import { DataSpecsNumberDataVO } from '../config'
|
||||
|
||||
/** 数值型的 dataSpecs 配置组件 */
|
||||
defineOptions({ name: 'ThingModelNumberTypeDataSpecs' })
|
||||
defineOptions({ name: 'ThingModelNumberDataSpecs' })
|
||||
|
||||
const props = defineProps<{ modelValue: any }>()
|
||||
const emits = defineEmits(['update:modelValue'])
|
@ -1,5 +1,5 @@
|
||||
import ThingModelEnumTypeDataSpecs from './ThingModelEnumTypeDataSpecs.vue'
|
||||
import ThingModelNumberTypeDataSpecs from './ThingModelNumberTypeDataSpecs.vue'
|
||||
import ThingModelArrayTypeDataSpecs from './ThingModelArrayTypeDataSpecs.vue'
|
||||
import ThingModelEnumDataSpecs from './ThingModelEnumDataSpecs.vue'
|
||||
import ThingModelNumberDataSpecs from './ThingModelNumberDataSpecs.vue'
|
||||
import ThingModelArrayDataSpecs from './ThingModelArrayDataSpecs.vue'
|
||||
|
||||
export { ThingModelEnumTypeDataSpecs, ThingModelNumberTypeDataSpecs, ThingModelArrayTypeDataSpecs }
|
||||
export { ThingModelEnumDataSpecs, ThingModelNumberDataSpecs, ThingModelArrayDataSpecs }
|
||||
|
@ -1,4 +1,3 @@
|
||||
<!-- TODO 目录,应该是 thinkModel 哈。 -->
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
@ -100,7 +99,7 @@
|
||||
<ThingModelForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ThingModelApi, ThingModelData } from 'src/api/iot/thingmodel'
|
||||
import { ThingModelApi, ThingModelData } from '@/api/iot/thingmodel'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import ThingModelForm from './ThingModelForm.vue'
|
||||
import { ProductVO } from '@/api/iot/product/product'
|
||||
|
Loading…
x
Reference in New Issue
Block a user