【功能完善】IOT: ThingModel 服务和事件
parent
d7c33b454f
commit
5391720b8b
|
@ -38,59 +38,6 @@ export interface ThingModelService {
|
|||
[key: string]: any
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// IOT 产品物模型事件类型枚举
|
||||
export const ThingModelServiceEventType = {
|
||||
INFO: {
|
||||
label: '信息',
|
||||
value: 'info'
|
||||
},
|
||||
ALERT: {
|
||||
label: '告警',
|
||||
value: 'alert'
|
||||
},
|
||||
ERROR: {
|
||||
label: '故障',
|
||||
value: 'error'
|
||||
}
|
||||
} as const
|
||||
|
||||
// IOT 产品物模型参数是输入参数还是输出参数
|
||||
export const ThingModelParamDirection = {
|
||||
INPUT: 'input', // 输入参数
|
||||
OUTPUT: 'output' // 输出参数
|
||||
} as const
|
||||
|
||||
// IoT 产品物模型 API
|
||||
export const ThingModelApi = {
|
||||
// 查询产品物模型分页
|
||||
|
|
|
@ -2,25 +2,33 @@
|
|||
<el-form-item
|
||||
:rules="[{ required: true, message: '请选择事件类型', trigger: 'change' }]"
|
||||
label="事件类型"
|
||||
prop="thingModelEvent.type"
|
||||
prop="event.type"
|
||||
>
|
||||
<el-radio-group v-model="thingModelEvent.type">
|
||||
<el-radio :value="ThingModelServiceEventType.INFO.value">
|
||||
{{ ThingModelServiceEventType.INFO.label }}
|
||||
<el-radio :value="ThingModelEventType.INFO.value">
|
||||
{{ ThingModelEventType.INFO.label }}
|
||||
</el-radio>
|
||||
<el-radio :value="ThingModelServiceEventType.ALERT.value">
|
||||
{{ ThingModelServiceEventType.ALERT.label }}
|
||||
<el-radio :value="ThingModelEventType.ALERT.value">
|
||||
{{ ThingModelEventType.ALERT.label }}
|
||||
</el-radio>
|
||||
<el-radio :value="ThingModelServiceEventType.ERROR.value">
|
||||
{{ ThingModelServiceEventType.ERROR.label }}
|
||||
<el-radio :value="ThingModelEventType.ERROR.value">
|
||||
{{ ThingModelEventType.ERROR.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="输出参数">
|
||||
<ThingModelInputOutputParam
|
||||
v-model="thingModelEvent.outputParams"
|
||||
:direction="ThingModelParamDirection.OUTPUT"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ThingModelInputOutputParam from './ThingModelInputOutputParam.vue'
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import { ThingModelEvent, ThingModelServiceEventType } from '@/api/iot/thingmodel'
|
||||
import { ThingModelEvent } from '@/api/iot/thingmodel'
|
||||
import { ThingModelParamDirection, ThingModelEventType } from './config'
|
||||
|
||||
/** IoT 物模型事件 */
|
||||
defineOptions({ name: 'ThingModelEvent' })
|
||||
|
|
|
@ -36,6 +36,15 @@
|
|||
/>
|
||||
<!-- 事件配置 -->
|
||||
<ThingModelEvent v-if="formData.type === ThingModelType.EVENT" v-model="formData.event" />
|
||||
<el-form-item label="描述" prop="description">
|
||||
<el-input
|
||||
v-model="formData.description"
|
||||
:maxlength="200"
|
||||
:rows="3"
|
||||
placeholder="请输入属性描述"
|
||||
type="textarea"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
|
@ -50,9 +59,9 @@ 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, ThingModelType } from '@/api/iot/thingmodel'
|
||||
import { ThingModelApi, ThingModelData } from '@/api/iot/thingmodel'
|
||||
import { IOT_PROVIDE_KEY } from '@/views/iot/utils/constants'
|
||||
import { DataSpecsDataType, ThingModelFormRules } from './config'
|
||||
import { DataSpecsDataType, ThingModelFormRules, ThingModelType } from './config'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
|
@ -111,7 +120,6 @@ const submitForm = async () => {
|
|||
// 信息补全
|
||||
data.productId = product!.value.id
|
||||
data.productKey = product!.value.productKey
|
||||
data.description = data.property.description
|
||||
data.dataType = data.property.dataType
|
||||
data.property.identifier = data.identifier
|
||||
data.property.name = data.name
|
||||
|
@ -164,7 +172,9 @@ const resetForm = () => {
|
|||
dataSpecs: {
|
||||
dataType: DataSpecsDataType.INT
|
||||
}
|
||||
}
|
||||
},
|
||||
service: {},
|
||||
event: {}
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
<template>
|
||||
<div
|
||||
v-for="(item, index) in thingModelParams"
|
||||
:key="index"
|
||||
class="w-1/1 param-item flex justify-between px-10px mb-10px"
|
||||
>
|
||||
<span>参数名称:{{ item.name }}</span>
|
||||
<div class="btn">
|
||||
<el-button link type="primary" @click="openParamForm(item)">编辑</el-button>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button link type="danger" @click="deleteParamItem(index)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-button link type="primary" @click="openParamForm(null)">+新增参数</el-button>
|
||||
|
||||
<!-- param 表单 -->
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle" append-to-body>
|
||||
<el-form
|
||||
ref="paramFormRef"
|
||||
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>
|
||||
<!-- 属性配置 -->
|
||||
<ThingModelProperty v-model="formData.property" is-params />
|
||||
</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 ThingModelProperty from './ThingModelProperty.vue'
|
||||
import { DataSpecsDataType, ThingModelFormRules } from './config'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
|
||||
/** 输入输出参数配置组件 */
|
||||
defineOptions({ name: 'ThingModelInputOutputParam' })
|
||||
|
||||
const props = defineProps<{ modelValue: any; direction: string }>()
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
const thingModelParams = useVModel(props, 'modelValue', emits) as Ref<any[]>
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('新增参数') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const paramFormRef = ref() // 表单 ref
|
||||
const formData = ref<any>({
|
||||
dataType: DataSpecsDataType.INT,
|
||||
property: {
|
||||
dataType: DataSpecsDataType.INT,
|
||||
dataSpecs: {
|
||||
dataType: DataSpecsDataType.INT
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/** 打开 param 表单 */
|
||||
const openParamForm = (val: any) => {
|
||||
dialogVisible.value = true
|
||||
resetForm()
|
||||
if (isEmpty(val)) {
|
||||
return
|
||||
}
|
||||
// 编辑时回显数据
|
||||
formData.value = {
|
||||
identifier: val.identifier,
|
||||
name: val.name,
|
||||
description: val.description,
|
||||
property: {
|
||||
dataType: val.dataType,
|
||||
dataSpecs: val.dataSpecs,
|
||||
dataSpecsList: val.dataSpecsList
|
||||
}
|
||||
}
|
||||
}
|
||||
/** 删除 param 项 */
|
||||
const deleteParamItem = (index: number) => {
|
||||
thingModelParams.value.splice(index, 1)
|
||||
}
|
||||
|
||||
/** 添加参数 */
|
||||
const submitForm = async () => {
|
||||
// 初始化参数列表
|
||||
if (isEmpty(thingModelParams.value)) {
|
||||
thingModelParams.value = []
|
||||
}
|
||||
// 校验参数
|
||||
await paramFormRef.value.validate()
|
||||
try {
|
||||
const data = unref(formData)
|
||||
// 构建数据对象
|
||||
const item = {
|
||||
identifier: data.identifier,
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
dataType: data.property.dataType,
|
||||
paraOrder: 0, // TODO @puhui999: 先写死默认看看后续
|
||||
direction: props.direction,
|
||||
dataSpecs:
|
||||
!!data.property.dataSpecs && Object.keys(data.property.dataSpecs).length > 1
|
||||
? data.property.dataSpecs
|
||||
: undefined,
|
||||
dataSpecsList: isEmpty(data.property.dataSpecsList) ? undefined : data.property.dataSpecsList
|
||||
}
|
||||
|
||||
// 查找是否已有相同 identifier 的项
|
||||
const existingIndex = thingModelParams.value.findIndex(
|
||||
(spec) => spec.identifier === data.identifier
|
||||
)
|
||||
if (existingIndex > -1) {
|
||||
// 更新已有项
|
||||
thingModelParams.value[existingIndex] = item
|
||||
} else {
|
||||
// 添加新项
|
||||
thingModelParams.value.push(item)
|
||||
}
|
||||
} finally {
|
||||
// 隐藏对话框
|
||||
dialogVisible.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
dataType: DataSpecsDataType.INT,
|
||||
property: {
|
||||
dataType: DataSpecsDataType.INT,
|
||||
dataSpecs: {
|
||||
dataType: DataSpecsDataType.INT
|
||||
}
|
||||
}
|
||||
}
|
||||
paramFormRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.param-item {
|
||||
background-color: #e4f2fd;
|
||||
}
|
||||
</style>
|
|
@ -75,7 +75,7 @@
|
|||
v-if="property.dataType === DataSpecsDataType.STRUCT"
|
||||
v-model="property.dataSpecsList"
|
||||
/>
|
||||
<el-form-item v-if="!isStructDataSpecs" label="读写类型" prop="property.accessMode">
|
||||
<el-form-item v-if="!isStructDataSpecs && !isParams" label="读写类型" prop="property.accessMode">
|
||||
<el-radio-group v-model="property.accessMode">
|
||||
<el-radio :label="ThingModelAccessMode.READ_WRITE.value">
|
||||
{{ ThingModelAccessMode.READ_WRITE.label }}
|
||||
|
@ -85,32 +85,28 @@
|
|||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="属性描述" prop="description">
|
||||
<el-input
|
||||
v-model="property.description"
|
||||
:maxlength="200"
|
||||
:rows="3"
|
||||
placeholder="请输入属性描述"
|
||||
type="textarea"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import { DataSpecsDataType, dataTypeOptions, validateBoolName } from './config'
|
||||
import {
|
||||
DataSpecsDataType,
|
||||
dataTypeOptions,
|
||||
ThingModelAccessMode,
|
||||
validateBoolName
|
||||
} from './config'
|
||||
import {
|
||||
ThingModelArrayDataSpecs,
|
||||
ThingModelEnumDataSpecs,
|
||||
ThingModelNumberDataSpecs,
|
||||
ThingModelStructDataSpecs
|
||||
} from './dataSpecs'
|
||||
import { ThingModelAccessMode, ThingModelProperty } from '@/api/iot/thingmodel'
|
||||
import { ThingModelProperty } from '@/api/iot/thingmodel'
|
||||
|
||||
/** IoT 物模型数据 */
|
||||
/** IoT 物模型属性 */
|
||||
defineOptions({ name: 'ThingModelProperty' })
|
||||
|
||||
const props = defineProps<{ modelValue: any; isStructDataSpecs?: boolean }>()
|
||||
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(() => {
|
||||
|
|
|
@ -13,11 +13,25 @@
|
|||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="输入参数">
|
||||
<ThingModelInputOutputParam
|
||||
v-model="service.inputParams"
|
||||
:direction="ThingModelParamDirection.INPUT"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="输出参数">
|
||||
<ThingModelInputOutputParam
|
||||
v-model="service.outputParams"
|
||||
:direction="ThingModelParamDirection.OUTPUT"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ThingModelInputOutputParam from './ThingModelInputOutputParam.vue'
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import { ThingModelService, ThingModelServiceCallType } from '@/api/iot/thingmodel'
|
||||
import { ThingModelService } from '@/api/iot/thingmodel'
|
||||
import { ThingModelParamDirection, ThingModelServiceCallType } from './config'
|
||||
|
||||
/** IoT 物模型服务 */
|
||||
defineOptions({ name: 'ThingModelService' })
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {isEmpty} from '@/utils/is'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
|
||||
/** dataSpecs 数值型数据结构 */
|
||||
export interface DataSpecsNumberDataVO {
|
||||
|
@ -48,9 +48,69 @@ export const dataTypeOptions = [
|
|||
|
||||
/** 获得物体模型数据类型配置项名称 */
|
||||
export const getDataTypeOptionsLabel = (value: string) => {
|
||||
if (isEmpty(value)) {
|
||||
return value
|
||||
}
|
||||
return dataTypeOptions.find((option) => option.value === value)?.label
|
||||
}
|
||||
|
||||
// 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: [
|
||||
|
|
|
@ -56,13 +56,63 @@
|
|||
<el-table-column align="center" label="标识符" prop="identifier" />
|
||||
<el-table-column align="center" label="数据类型" prop="identifier">
|
||||
<template #default="{ row }">
|
||||
{{ dataTypeOptionsLabel(row.property.dataType) ?? '-' }}
|
||||
{{ dataTypeOptionsLabel(row.property?.dataType) ?? '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="数据定义" prop="identifier">
|
||||
<el-table-column align="left" label="数据定义" prop="identifier">
|
||||
<template #default="{ row }">
|
||||
<!-- TODO puhui999: 数据定义展示待完善 -->
|
||||
{{ row.property.dataSpecs ?? row.property.dataSpecsList }}
|
||||
<!-- 属性 -->
|
||||
<template v-if="row.type === ThingModelType.PROPERTY">
|
||||
<!-- 非列表型:数值 -->
|
||||
<div
|
||||
v-if="
|
||||
[
|
||||
DataSpecsDataType.INT,
|
||||
DataSpecsDataType.DOUBLE,
|
||||
DataSpecsDataType.FLOAT
|
||||
].includes(row.property.dataType)
|
||||
"
|
||||
>
|
||||
取值范围:{{ `${row.property.dataSpecs.min}~${row.property.dataSpecs.max}` }}
|
||||
</div>
|
||||
<!-- 非列表型:文本 -->
|
||||
<div v-if="DataSpecsDataType.TEXT === row.property.dataType">
|
||||
数据长度:{{ row.property.dataSpecs.length }}
|
||||
</div>
|
||||
<!-- 列表型: 数组、结构、时间(特殊) -->
|
||||
<div
|
||||
v-if="
|
||||
[
|
||||
DataSpecsDataType.ARRAY,
|
||||
DataSpecsDataType.STRUCT,
|
||||
DataSpecsDataType.DATE
|
||||
].includes(row.property.dataType)
|
||||
"
|
||||
>
|
||||
-
|
||||
</div>
|
||||
<!-- 列表型: 布尔值、枚举 -->
|
||||
<div
|
||||
v-if="
|
||||
[DataSpecsDataType.BOOL, DataSpecsDataType.ENUM].includes(row.property.dataType)
|
||||
"
|
||||
>
|
||||
<div>
|
||||
{{ DataSpecsDataType.BOOL === row.property.dataType ? '布尔值' : '枚举值' }}:
|
||||
</div>
|
||||
<div v-for="item in row.property.dataSpecsList" :key="item.value">
|
||||
{{ `${item.name}-${item.value}` }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 服务 -->
|
||||
<div v-if="row.type === ThingModelType.SERVICE">
|
||||
调用方式:{{ getCallTypeByValue(row.service.callType) }}
|
||||
</div>
|
||||
<!-- 事件 -->
|
||||
<div v-if="row.type === ThingModelType.EVENT">
|
||||
事件类型:{{ getEventTypeByValue(row.event.type) }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="操作">
|
||||
|
@ -104,7 +154,14 @@ import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
|||
import ThingModelForm from './ThingModelForm.vue'
|
||||
import { ProductVO } from '@/api/iot/product/product'
|
||||
import { IOT_PROVIDE_KEY } from '@/views/iot/utils/constants'
|
||||
import { getDataTypeOptionsLabel } from '@/views/iot/thingmodel/config'
|
||||
import {
|
||||
DataSpecsDataType,
|
||||
getCallTypeByValue,
|
||||
getDataTypeOptionsLabel,
|
||||
getEventTypeByValue,
|
||||
ThingModelType
|
||||
} from './config'
|
||||
import { ThingModelNumberDataSpecs } from '@/views/iot/thingmodel/dataSpecs'
|
||||
|
||||
defineOptions({ name: 'IoTProductThingModel' })
|
||||
|
||||
|
|
Loading…
Reference in New Issue