Files
iot-device-management-frontend/apps/web-antd/src/views/iot/thingmodel/modules/ThingModelForm.vue

244 lines
6.9 KiB
Vue
Raw Normal View History

<!-- 产品的物模型表单 -->
2025-10-10 20:26:17 +08:00
<script lang="ts" setup>
import type { Ref } from 'vue';
2025-10-13 10:17:19 +08:00
import type { IotProductApi } from '#/api/iot/product/product';
2025-10-10 20:26:17 +08:00
import type { ThingModelData } from '#/api/iot/thingmodel';
2025-10-10 20:26:17 +08:00
import { inject, ref } from 'vue';
2025-10-13 10:17:19 +08:00
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { $t } from '@vben/locales';
import { cloneDeep } from '@vben/utils';
2025-10-10 20:26:17 +08:00
2025-10-13 10:41:08 +08:00
import { Button, Form, Input, message, Radio } from 'ant-design-vue';
2025-10-10 20:26:17 +08:00
2025-10-13 10:41:08 +08:00
import {
createThingModel,
getThingModel,
updateThingModel,
} from '#/api/iot/thingmodel';
import {
IOT_PROVIDE_KEY,
IoTDataSpecsDataTypeEnum,
2025-10-10 20:26:17 +08:00
IoTThingModelTypeEnum,
} from '#/views/iot/utils/constants';
import ThingModelEvent from './ThingModelEvent.vue';
import ThingModelProperty from './ThingModelProperty.vue';
import ThingModelService from './ThingModelService.vue';
/** IoT 物模型数据表单 */
2025-10-10 20:26:17 +08:00
defineOptions({ name: 'IoTThingModelForm' });
2025-10-10 20:26:17 +08:00
/** 提交表单 */
const emit = defineEmits(['success']);
2025-10-13 10:17:19 +08:00
const product = inject<Ref<IotProductApi.Product>>(IOT_PROVIDE_KEY.PRODUCT); // 注入产品信息
2025-10-10 20:26:17 +08:00
const dialogVisible = ref(false); // 弹窗的是否展示
const dialogTitle = ref(''); // 弹窗的标题
const formLoading = ref(false); // 表单的加载中1修改时的数据加载2提交的按钮禁用
const formType = ref(''); // 表单的类型create - 新增update - 修改
const formData = ref<ThingModelData>({
2025-10-13 10:41:08 +08:00
type: IoTThingModelTypeEnum.PROPERTY.toString(),
dataType: IoTDataSpecsDataTypeEnum.INT,
property: {
dataType: IoTDataSpecsDataTypeEnum.INT,
dataSpecs: {
2025-10-10 20:26:17 +08:00
dataType: IoTDataSpecsDataTypeEnum.INT,
},
},
service: {},
2025-10-10 20:26:17 +08:00
event: {},
2025-10-13 10:41:08 +08:00
});
2025-10-10 20:26:17 +08:00
const formRef = ref(); // 表单 Ref
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
2025-10-10 20:26:17 +08:00
dialogVisible.value = true;
2025-10-13 10:17:19 +08:00
dialogTitle.value = $t(`action.${type}`);
2025-10-10 20:26:17 +08:00
formType.value = type;
resetForm();
if (id) {
2025-10-10 20:26:17 +08:00
formLoading.value = true;
try {
2025-10-13 10:17:19 +08:00
formData.value = await getThingModel(id);
// 情况一:属性初始化
2025-10-10 20:26:17 +08:00
if (
!formData.value.property ||
Object.keys(formData.value.property).length === 0
) {
formData.value.dataType = IoTDataSpecsDataTypeEnum.INT;
formData.value.property = {
dataType: IoTDataSpecsDataTypeEnum.INT,
dataSpecs: {
2025-10-10 20:26:17 +08:00
dataType: IoTDataSpecsDataTypeEnum.INT,
},
};
}
// 情况二:服务初始化
2025-10-10 20:26:17 +08:00
if (
!formData.value.service ||
Object.keys(formData.value.service).length === 0
) {
formData.value.service = {};
}
// 情况三:事件初始化
2025-10-10 20:26:17 +08:00
if (
!formData.value.event ||
Object.keys(formData.value.event).length === 0
) {
formData.value.event = {};
}
} finally {
2025-10-10 20:26:17 +08:00
formLoading.value = false;
}
}
2025-10-10 20:26:17 +08:00
};
defineExpose({ open, close: () => (dialogVisible.value = false) });
2025-10-13 10:17:19 +08:00
async function submitForm() {
2025-10-10 20:26:17 +08:00
await formRef.value.validate();
formLoading.value = true;
try {
2025-10-10 20:26:17 +08:00
const data = cloneDeep(formData.value) as ThingModelData;
// 信息补全
2025-10-10 20:26:17 +08:00
data.productId = product!.value.id;
data.productKey = product!.value.productKey;
fillExtraAttributes(data);
2025-10-13 10:17:19 +08:00
await (formType.value === 'create'
? createThingModel(data)
: updateThingModel(data));
message.success($t('ui.actionMessage.operationSuccess'));
// 关闭弹窗
2025-10-10 20:26:17 +08:00
dialogVisible.value = false;
emit('success');
} finally {
2025-10-10 20:26:17 +08:00
formLoading.value = false;
}
2025-10-13 10:17:19 +08:00
}
/** 填写额外的属性(处理不同类型的情况) */
2025-10-13 10:17:19 +08:00
function fillExtraAttributes(data: any) {
// 属性
if (data.type === IoTThingModelTypeEnum.PROPERTY) {
2025-10-10 20:26:17 +08:00
removeDataSpecs(data.property);
data.dataType = data.property.dataType;
data.property.identifier = data.identifier;
data.property.name = data.name;
delete data.service;
delete data.event;
}
// 服务
if (data.type === IoTThingModelTypeEnum.SERVICE) {
2025-10-10 20:26:17 +08:00
removeDataSpecs(data.service);
data.dataType = data.service.dataType;
data.service.identifier = data.identifier;
data.service.name = data.name;
delete data.property;
delete data.event;
}
// 事件
if (data.type === IoTThingModelTypeEnum.EVENT) {
2025-10-10 20:26:17 +08:00
removeDataSpecs(data.event);
data.dataType = data.event.dataType;
data.event.identifier = data.identifier;
data.event.name = data.name;
delete data.property;
delete data.service;
}
2025-10-13 10:17:19 +08:00
}
/** 处理 dataSpecs 为空的情况 */
2025-10-13 10:17:19 +08:00
function removeDataSpecs(val: any) {
if (!val.dataSpecs || Object.keys(val.dataSpecs).length === 0) {
2025-10-10 20:26:17 +08:00
delete val.dataSpecs;
}
if (!val.dataSpecsList || val.dataSpecsList.length === 0) {
2025-10-10 20:26:17 +08:00
delete val.dataSpecsList;
}
2025-10-13 10:17:19 +08:00
}
/** 重置表单 */
2025-10-13 10:17:19 +08:00
function resetForm() {
formData.value = {
2025-10-13 10:17:19 +08:00
type: IoTThingModelTypeEnum.PROPERTY.toString(),
dataType: IoTDataSpecsDataTypeEnum.INT,
property: {
dataType: IoTDataSpecsDataTypeEnum.INT,
dataSpecs: {
2025-10-10 20:26:17 +08:00
dataType: IoTDataSpecsDataTypeEnum.INT,
},
},
service: {},
2025-10-10 20:26:17 +08:00
event: {},
2025-10-13 10:17:19 +08:00
};
2025-10-10 20:26:17 +08:00
formRef.value?.resetFields();
2025-10-13 10:17:19 +08:00
}
</script>
2025-10-10 20:26:17 +08:00
<template>
2025-10-13 10:41:08 +08:00
<Modal v-model="dialogVisible" :title="dialogTitle">
<Form
2025-10-10 20:26:17 +08:00
ref="formRef"
:loading="formLoading"
:model="formData"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
>
2025-10-13 10:41:08 +08:00
<Form.Item label="功能类型" name="type">
<Radio.Group v-model:value="formData.type">
<Radio.Button
v-for="(dict, index) in getDictOptions(
2025-10-13 10:17:19 +08:00
DICT_TYPE.IOT_THING_MODEL_TYPE,
'number',
)"
2025-10-13 10:41:08 +08:00
:key="index"
2025-10-10 20:26:17 +08:00
:value="dict.value"
>
{{ dict.label }}
2025-10-13 10:41:08 +08:00
</Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item label="功能名称" name="name">
<Input v-model:value="formData.name" placeholder="请输入功能名称" />
</Form.Item>
<Form.Item label="标识符" name="identifier">
<Input v-model:value="formData.identifier" placeholder="请输入标识符" />
</Form.Item>
2025-10-10 20:26:17 +08:00
<!-- 属性配置 -->
<ThingModelProperty
2025-10-13 10:41:08 +08:00
v-if="formData.type === IoTThingModelTypeEnum.PROPERTY.toString()"
2025-10-10 20:26:17 +08:00
v-model="formData.property"
/>
<!-- 服务配置 -->
<ThingModelService
2025-10-13 10:41:08 +08:00
v-if="formData.type === IoTThingModelTypeEnum.SERVICE.toString()"
2025-10-10 20:26:17 +08:00
v-model="formData.service"
/>
<!-- 事件配置 -->
<ThingModelEvent
2025-10-13 10:41:08 +08:00
v-if="formData.type === IoTThingModelTypeEnum.EVENT.toString()"
2025-10-10 20:26:17 +08:00
v-model="formData.event"
/>
2025-10-13 10:41:08 +08:00
<Form.Item label="描述" name="desc">
<Input.TextArea
v-model:value="formData.desc"
2025-10-10 20:26:17 +08:00
:maxlength="200"
:rows="3"
placeholder="请输入属性描述"
/>
2025-10-13 10:41:08 +08:00
</Form.Item>
</Form>
2025-10-10 20:26:17 +08:00
<template #footer>
2025-10-13 10:41:08 +08:00
<Button :disabled="formLoading" type="primary" @click="submitForm">
2025-10-10 20:26:17 +08:00
2025-10-13 10:41:08 +08:00
</Button>
<Button @click="dialogVisible = false"> </Button>
2025-10-10 20:26:17 +08:00
</template>
2025-10-13 10:41:08 +08:00
</Modal>
2025-10-10 20:26:17 +08:00
</template>