Files
pig-farm-controller-fe/src/components/DeviceForm.vue
2025-09-20 16:37:26 +08:00

332 lines
9.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<el-dialog
:model-value="visible"
:title="title"
@close="handleClose"
:close-on-click-modal="false"
width="600px"
>
<el-form
ref="formRef"
:model="formData"
:rules="rules"
label-width="120px"
@submit.prevent
>
<!-- 基础信息 -->
<el-form-item label="设备名" prop="name">
<el-input v-model="formData.name" />
</el-form-item>
<el-form-item label="设备类型" prop="type">
<el-select v-model="formData.type" @change="handleTypeChange">
<el-option label="区域主控" value="area_controller" />
<el-option label="普通设备" value="device" />
</el-select>
</el-form-item>
<el-form-item label="设备位置描述" prop="location">
<el-input v-model="formData.location" type="textarea" />
</el-form-item>
<!-- 区域主控类型额外字段 -->
<div v-if="formData.type === 'area_controller'">
<el-form-item label="LoRa地址" prop="loraAddress">
<el-input v-model="formData.loraAddress" />
</el-form-item>
</div>
<!-- 普通设备类型额外字段 -->
<div v-if="formData.type === 'device'">
<el-form-item label="区域主控" prop="parentControllerId">
<el-select v-model="formData.parentControllerId" placeholder="请选择区域主控">
<el-option
v-for="controller in areaControllers"
:key="controller.id"
:label="controller.name"
:value="controller.id"
/>
</el-select>
</el-form-item>
<el-form-item label="设备种类" prop="subType">
<el-select v-model="formData.subType" placeholder="请选择设备种类">
<el-option label="风扇" value="fan" />
<el-option label="温度传感器" value="temperature" />
<el-option label="湿度传感器" value="humidity" />
<el-option label="氨气传感器" value="ammonia" />
<el-option label="饲料阀门" value="feed_valve" />
<el-option label="水帘" value="water_curtain" />
</el-select>
</el-form-item>
<el-form-item label="485总线号" prop="busNumber">
<el-input-number
v-model="formData.busNumber"
:min="0"
controls-position="right"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="485总线地址" prop="busAddress">
<el-input-number
v-model="formData.busAddress"
:min="0"
controls-position="right"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="继电器通道号" prop="relayChannel">
<el-input-number
v-model="formData.relayChannel"
:min="0"
controls-position="right"
style="width: 100%"
/>
</el-form-item>
</div>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="loading">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script>
import { ref, reactive, onMounted, watch, computed } from 'vue';
import { ElMessage } from 'element-plus';
import { DeviceApi } from '../api/device.js';
export default {
name: 'DeviceForm',
props: {
visible: {
type: Boolean,
default: false
},
deviceData: {
type: Object,
default: () => ({})
},
isEdit: {
type: Boolean,
default: false
}
},
emits: ['update:visible', 'success', 'cancel'],
setup(props, { emit }) {
// 表单引用
const formRef = ref(null);
// 加载状态
const loading = ref(false);
// 区域主控列表
const areaControllers = ref([]);
// 表单数据
const formData = reactive({
id: '',
name: '',
type: '',
location: '',
// 区域主控字段
loraAddress: '',
// 普通设备字段
parentControllerId: '',
subType: '',
busNumber: 0,
busAddress: 0,
relayChannel: 0
});
// 表单验证规则
const rules = {
name: [
{ required: true, message: '请输入设备名', trigger: 'blur' }
],
type: [
{ required: true, message: '请选择设备类型', trigger: 'change' }
],
location: [
{ required: true, message: '请输入设备位置描述', trigger: 'blur' }
],
loraAddress: [
{ required: true, message: '请输入LoRa地址', trigger: 'blur' }
],
parentControllerId: [
{ required: true, message: '请选择区域主控', trigger: 'change' }
],
subType: [
{ required: true, message: '请选择设备种类', trigger: 'change' }
],
busNumber: [
{ required: true, message: '请输入485总线号', trigger: 'blur' }
],
busAddress: [
{ required: true, message: '请输入485总线地址', trigger: 'blur' }
],
relayChannel: [
{ required: true, message: '请输入继电器通道号', trigger: 'blur' }
]
};
// 标题计算
const title = computed(() => {
return props.isEdit ? '编辑设备' : '添加设备';
});
// 处理类型切换
const handleTypeChange = (value) => {
// 清除之前的数据
if (value === 'area_controller') {
// 清除普通设备字段
formData.parentControllerId = '';
formData.subType = '';
formData.busNumber = 0;
formData.busAddress = 0;
formData.relayChannel = 0;
} else {
// 清除区域主控字段
formData.loraAddress = '';
}
};
// 获取区域主控列表
const loadAreaControllers = async () => {
try {
const response = await DeviceApi.list();
// 筛选出类型为区域主控的设备
areaControllers.value = response.data.filter(device => device.type === 'area_controller');
} catch (error) {
console.error('获取区域主控列表失败:', error);
areaControllers.value = [];
}
};
// 关闭对话框
const handleClose = () => {
emit('update:visible', false);
emit('cancel');
};
// 提交表单
const handleSubmit = async () => {
await formRef.value.validate(async (valid) => {
if (valid) {
loading.value = true;
try {
let result;
if (props.isEdit) {
// 编辑设备
result = await DeviceApi.update(formData.id, getSubmitData());
} else {
// 创建设备
result = await DeviceApi.create(getSubmitData());
}
emit('success', result);
handleClose();
} catch (error) {
console.error('保存设备失败:', error);
ElMessage.error(props.isEdit ? '编辑设备失败' : '创建设备失败');
} finally {
loading.value = false;
}
}
});
};
// 获取提交数据
const getSubmitData = () => {
const data = {
name: formData.name,
type: formData.type,
location: formData.location
};
// 添加properties字段作为JSON对象传递
const properties = {};
if (formData.type === 'area_controller') {
properties.lora_address = formData.loraAddress;
} else if (formData.type === 'device') {
properties.bus_number = formData.busNumber;
properties.bus_address = formData.busAddress;
properties.relay_channel = formData.relayChannel;
data.parent_id = formData.parentControllerId;
data.sub_type = formData.subType;
}
// 直接使用properties对象不进行序列化
data.properties = properties;
return data;
};
// 监听设备数据变化
watch(() => props.deviceData, (newVal) => {
if (newVal && Object.keys(newVal).length > 0) {
// 填充表单数据
Object.keys(formData).forEach(key => {
if (newVal[key] !== undefined) {
formData[key] = newVal[key];
}
});
// 处理properties对象的数据填充
if (newVal.properties) {
const props = typeof newVal.properties === 'string' ? JSON.parse(newVal.properties) : newVal.properties;
if (formData.type === 'area_controller') {
formData.loraAddress = props.lora_address || '';
} else if (formData.type === 'device') {
formData.busNumber = props.bus_number || 0;
formData.busAddress = props.bus_address || 0;
formData.relayChannel = props.relay_channel || 0;
}
}
} else {
// 重置表单数据
Object.keys(formData).forEach(key => {
if (key === 'busNumber' || key === 'busAddress' || key === 'relayChannel') {
formData[key] = 0;
} else {
formData[key] = '';
}
});
}
}, { immediate: true });
// 组件挂载时加载区域主控列表
onMounted(() => {
loadAreaControllers();
});
return {
formRef,
loading,
areaControllers,
formData,
rules,
title,
handleTypeChange,
handleClose,
handleSubmit
};
}
};
</script>
<style scoped>
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
</style>