<!-- Description: 设备管理 - 新建/编辑 Author: 李亚光 Date: 2024-11-01 --> <script lang="ts" setup name="DeviceManageEdit"> import { ElMessage, type FormRules } from 'element-plus' import { ITEM_RENDER_EVT } from 'element-plus/es/components/virtual-list/src/defaults' import { getSceneAll } from '@/api/page/scene' import { getModelAll } from '@/api/page/model' import { addDevice, getModelRelations, updateDevice, updateModelRelations, updateSceneRelations } from '@/api/page/device' const emits = defineEmits(['refresh']) const dataFormRef = ref() const dialogFormVisible = ref(false) // 对话框是否显示 const dialogStatus = ref('') // 对话框类型:create,update const multipleTableRef = ref() const dataForm = ref<{ [key: string]: string }>({ name: '', // 名称 code: '', // 编号 type: '', // 摄像头类型 input_stream_url: '', // RTSP地址 image_save_interval: '60', // 识别间隔 alarm_interval: '60', // 告警间隔 mode: '', gas_ip: '', relation_scene_name: '', relation_model_names: '', ip: '', }) // 表单 const textMap: { [key: string]: string } = { update: '编辑', create: '新增', detail: '详情', } // 表头显示标题 const btnLoading = ref(false) // 保存按钮的加载中状态 const rules: FormRules = { name: [{ required: true, message: '名称不能为空', trigger: ['blur', 'change'] }], code: [{ required: true, message: '编号不能为空', trigger: ['blur', 'change'] }], type: [{ required: true, message: '摄像头类型必选', trigger: ['blur', 'change'] }], input_stream_url: [{ required: true, message: '摄像头类型不能为空', trigger: ['blur', 'change'] }], image_save_interval: [{ required: true, message: '识别间隔不能为空', trigger: ['blur', 'change'] }], alarm_interval: [{ required: true, message: '告警间隔不能为空', trigger: ['blur', 'change'] }], mode: [{ required: true, message: '场景模式必选', trigger: ['blur', 'change'] }], gas_ip: [{ required: true, message: '甲烷IP必填', trigger: ['blur', 'change'] }], ip: [{ required: true, message: '摄像头IP必填', trigger: ['blur', 'change'] }], relation_model_names: [{ required: true, message: '关联算法必选', trigger: ['blur', 'change'] }], relation_scene_name: [{ required: true, message: '业务场景必选', trigger: ['blur', 'change'] }], } // 前端校验规则 const sceneList = ref<any[]>([]) const modelList = ref<any[]>([]) const modelAllList = ref<any[]>([]) // 初始化对话框 const initDialog = (row: any, dialogStatusValue: string) => { dialogStatus.value = dialogStatusValue dialogFormVisible.value = true if (dialogStatusValue === 'create') { resetForm() // nextTick(() => { // multipleTableRef.value?.resetFields() // }) } else { dataForm.value = row for (const i in dataForm.value) { dataForm.value[i] = typeof dataForm.value[i] === 'number' ? String(dataForm.value[i]) : dataForm.value[i] } if (dataForm.value.mode === '1') { // 算法 getModelRelations(row.id).then((res) => { if (!res.data.length) { modelList.value = modelAllList.value return } modelList.value = res.data.map((item: any) => ({ ...item, name: item.algo_model_name, custom_threshold: item.threshold, id: item.algo_model_id, })) if (modelList.value.length !== modelAllList.value.length) { // 先判断是删除还是新建 if (modelList.value.length < modelAllList.value.length) { // 新建 const addArr = modelAllList.value.filter((item: any) => modelList.value.every((citem: any) => citem.name !== item.name)) addArr.forEach((element: any) => { modelList.value.push({ ...element, custom_threshold: '0.5', }) }) } } modelList.value = modelList.value.sort((a: any, b: any) => Number(a.id) - Number(b.id)) nextTick(() => { modelList.value.forEach((element: any) => { multipleTableRef.value!.toggleRowSelection( element, String(element.is_use) === '1', ) }) }) }) } } } defineExpose({ initDialog, }) const cancel = () => { dialogFormVisible.value = false resetForm() } // 选择的算法 const selectModelList = ref<any[]>([]) const selectModel = (value: any) => { selectModelList.value = value dataForm.value.relation_model_names = value.map((item: any) => item.name).join(',') } // 重置表单 function resetForm() { selectModelList.value = [] dataForm.value = { name: '', code: '', // 编号 type: '', // 摄像头类型 input_stream_url: '', // RTSP地址 image_save_interval: '60', // 识别间隔 alarm_interval: '60', // 告警间隔 mode: '', gas_ip: '', relation_scene_name: '', relation_model_names: '', ip: '', } nextTick(() => { dataFormRef.value.resetFields() }) } // 获取字典相关 const fetchDict = () => { getSceneAll({}).then((res) => { sceneList.value = res.data }) getModelAll({}).then((res) => { modelList.value = res.data.map((item: any) => ({ ...item, custom_threshold: 0.5 })).sort((a: any, b: any) => Number(a.id) - Number(b.id)) modelAllList.value = res.data.map((item: any) => ({ ...item, custom_threshold: 0.5 })).sort((a: any, b: any) => Number(a.id) - Number(b.id)) }) } fetchDict() // 新建 const add = () => { const confirm = () => { ElMessage.success(dialogStatus.value === 'update' ? '编辑成功' : '新建成功') emits('refresh') resetForm() cancel() } (dialogStatus.value === 'update' ? updateDevice : addDevice)(dataForm.value).then((res) => { // 算法 if (dataForm.value.mode === '1') { updateModelRelations({ id: dialogStatus.value === 'update' ? dataForm.value.id : res.data.id, list: [ ...selectModelList.value.map((item: any) => ({ algo_model_id: item.id, is_use: 1, threshold: item.custom_threshold, })), ...modelList.value.filter((item: any) => selectModelList.value.every((citem: any) => citem.id !== item.id)).map((item: any) => ({ algo_model_id: item.id, is_use: 0, threshold: item.custom_threshold, })), ], }).then((res) => { confirm() }) } else if (dataForm.value.mode === '2') { // 场景 updateSceneRelations({ id: dialogStatus.value === 'update' ? dataForm.value.id : res.data.id, scene_id: sceneList.value.filter((item: any) => item.name === dataForm.value.relation_scene_name)[0]?.id || '', }).then(() => { confirm() }) } else { confirm() } }) } const saveData = () => { // 验证 // 1.关联算法 if (dataForm.value.mode === '1' && selectModelList.value.some(item => !item.custom_threshold)) { ElMessage.warning('关联算法所有阈值应为有效值') return } // 2.关联场景 if (dataForm.value.mode === '2' && !dataForm.value.relation_scene_name) { ElMessage.warning('场景应为必选,若列表为空,应先添加场景') return } // 验证 dataFormRef.value.validate((valid: any) => { if (valid) { if (dialogStatus.value === 'create') { add() } else { add() } } }) } </script> <template> <el-dialog v-model="dialogFormVisible" :title="textMap[dialogStatus]" append-to-body> <el-form ref="dataFormRef" :rules="rules" :model="dataForm" label-position="right" label-width="120px" :disabled="dialogStatus === 'detail'" > <el-row :gutter="24"> <el-col :span="12"> <el-form-item label="摄像头名称" prop="name"> <el-input v-model.trim="dataForm.name" type="text" placeholder="必填" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="摄像头编号" prop="code"> <el-input v-model.trim="dataForm.code" type="text" placeholder="必填" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="摄像头IP" prop="ip"> <el-input v-model.trim="dataForm.ip" type="text" placeholder="必填" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="RTSP地址" prop="input_stream_url"> <el-input v-model.trim="dataForm.input_stream_url" type="text" placeholder="必填" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="识别间隔(s)" prop="image_save_interval"> <el-input v-model.trim="dataForm.image_save_interval" type="number" :min="1" placeholder="必填" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="告警间隔(s)" prop="alarm_interval"> <el-input v-model.trim="dataForm.alarm_interval" type="number" :min="1" placeholder="必填" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="摄像头类型" prop="type"> <el-select v-model="dataForm.type" placeholder="摄像头类型" clearable class="select" style="width: 100%;"> <el-option value="1" label="摄像头" /> <el-option value="2" label="安全树" /> </el-select> </el-form-item> </el-col> <el-col v-if="dataForm.type === '2'" :span="12"> <el-form-item label="甲烷IP" prop="gas_ip"> <el-input v-model.trim="dataForm.gas_ip" type="text" placeholder="必填" /> </el-form-item> </el-col> </el-row> <el-row :gutter="24"> <el-col :span="12"> <el-form-item label="场景模式" prop="mode"> <el-radio-group v-model="dataForm.mode"> <el-radio label="2"> 业务场景 </el-radio> <el-radio label="1"> 算法关联 </el-radio> <el-radio label="0"> 无 </el-radio> </el-radio-group> </el-form-item> </el-col> </el-row> <el-row v-if="dataForm.mode === '2'" :gutter="24"> <el-col :span="12"> <el-form-item label="业务场景" prop="relation_scene_name"> <el-select v-model="dataForm.relation_scene_name" placeholder="业务场景" clearable class="select" style="width: 100%;" > <el-option v-for="item in sceneList" :key="item.id" :value="item.name" :label="item.name" /> </el-select> </el-form-item> </el-col> </el-row> <el-row v-if="dataForm.mode === '1'" :gutter="24"> <el-col :span="18"> <el-form-item label="算法关联" prop="relation_model_names"> <el-table ref="multipleTableRef" :data="modelList" border stripe style="width: 100%;" @selection-change="selectModel" > <el-table-column label="算法名称" align="center" value="name"> <template #default="scope"> {{ scope.row.name }} </template> </el-table-column> <el-table-column label="阈值" align="center"> <template #default="scope"> <el-input-number v-model="scope.row.custom_threshold" :min="0" controls-position="right" /> </template> </el-table-column> <el-table-column type="selection" width="55" align="center" /> </el-table> </el-form-item> </el-col> </el-row> </el-form> <template #footer> <div class="dialog-footer"> <el-button :loading="btnLoading" type="primary" @click="saveData"> 保存 </el-button> <el-button @click="cancel"> 取消 </el-button> </div> </template> </el-dialog> </template> <style lang="scss" scoped> .el-dialog { width: 700px; } .el-select { width: 100%; } .draw { height: 300px; width: 533.333px; border: 2px solid #000; padding: 0; margin: 0; box-sizing: content-box; position: relative; } </style>