<script lang="ts" setup name="EditDev"> import type { Ref } from 'vue' import { nextTick, reactive, ref } from 'vue' import { ElMessage, ElMessageBox } from 'element-plus' import type { FormInstance, FormRules } from 'element-plus' import type { DevInfo, DevListInfo } from './dev-interface' import { addDev, getDevInfo, updateDev } from '@/api/ycjg/dev' import { getDictByCode } from '@/api/system/dict' import { isInRange, isInRangeNum, isIp, isPort } from '@/utils/validate' import AreaSelectTree from '@/views/system/area/areaSelectTree.vue' const emits = defineEmits(['closeRefresh']) // 对话框类型:create,update const dialogStatus = ref('create') const dialogVisible = ref(false) // 显示标题 const textMap: { [key: string]: string } = { detail: '详情', update: '编辑', create: '新增', } const areaSelectShow = ref(false) const areaSelect = ref() // 表单数据对象 const formData: Ref<DevInfo> = ref({ id: '', deptId: '', area: '', areaName: '', monitorName: '', deviceIp: '', devicePort: '', deviceUser: '', devicePassword: '', nvrIp: '', nvrPort: '', nvrUser: '', nvrPassword: '', nvrChannel: '', deviceType: '', deviceTypeName: '', longitude: '', latitude: '', description: '', location: '', // speed: 0, // stopTime: 0, // high: 0, // angle: 0, deviceStatus: '', deviceStatusName: '', secretLevel: '', // 密级 nvrIndexCode: '', // NVR国标号 cameraIndexCode: '', // 设备国标号 }) // 保存按钮加载状态 const btnLoading = ref(false) // ---------------表单提交-------------------------------- const isIpV = (rule: any, value: any, callback: any) => { if (value === '') { callback(new Error('Something went wrong!')) } else if (isIp(value)) { callback() } else { callback(new Error('IP不合法')) } callback() } const isPortV = (rule: any, value: any, callback: any) => { if (value === '') { callback(new Error('Something went wrong!')) } else if (isPort(value)) { callback() } else { callback(new Error('端口号不合法')) } callback() } const isInRangeV = function (min: number, max: number) { return function (rule: any, value: any, callback: any) { if (value === '') { callback(new Error(`请输入${min.toString()}-${max.toString()}整数`)) } else if (isInRange(Number(value), max, min)) { callback() } else { callback(new Error(`请输入${min.toString()}-${max.toString()}整数`)) } callback() } } const isInRangeNumV = function (min: number, max: number) { return function (rule: any, value: any, callback: any) { if (value === '') { callback(new Error(`请输入${min.toString()}-${max.toString()}小数`)) } else if (isNaN(value)) { callback(new Error(`请输入${min.toString()}-${max.toString()}小数`)) } else if (isInRangeNum(Number(value), min, max)) { callback() } else { callback(new Error(`请输入${min.toString()}-${max.toString()}小数`)) } callback() } } // 表单对象 const dataFormRef = ref<FormInstance>() // 校验规则 const rules = reactive<FormRules>({ monitorName: [{ required: true, message: '设备名称必填', trigger: ['blur', 'change'] }], area: [{ required: true, message: '所属区域必选', trigger: ['blur', 'change'] }], deptId: [{ required: true, message: '所属单位必填', trigger: ['blur', 'change'] }], deviceIp: [{ required: true, message: '请输入合法云台登录地址', trigger: ['blur', 'change'], validator: isIpV }], devicePort: [{ required: true, message: '请输入合法端口号', trigger: ['blur', 'change'], validator: isPortV }], deviceUser: [{ required: true, message: '云台登录账号必填', trigger: ['blur', 'change'] }], devicePassword: [{ required: true, message: '云台登录密码必填', trigger: ['blur', 'change'] }], nvrIp: [{ required: true, message: '请输入合法NVR登录地址', trigger: ['blur', 'change'], validator: isIpV }], nvrPort: [{ required: true, message: '请输入合法端口号', trigger: ['blur', 'change'], validator: isPortV }], nvrUser: [{ required: true, message: 'NVR登录账号必填', trigger: ['blur', 'change'] }], nvrPassword: [{ required: true, message: 'NVR登录密码必填', trigger: ['blur', 'change'] }], nvrChannel: [{ required: true, message: '请输入合法NVR通道号', trigger: ['blur', 'change'], validator: isInRangeV(0, 63) }], deviceType: [{ required: true, message: '设备型号必填', trigger: ['blur', 'change'] }], longitude: [{ required: true, message: '请输入合法经度', trigger: ['blur', 'change'], validator: isInRangeNumV(0, 200) }], latitude: [{ required: true, message: '请输入合法纬度', trigger: ['blur', 'change'], validator: isInRangeNumV(0, 200) }], cameraIndexCode: [{ required: true, message: '设备国际号必填', trigger: ['blur', 'change'] }], nvrIndexCode: [{ required: true, message: 'NVR国标号必填', trigger: ['blur', 'change'] }], secretLevel: [{ required: true, message: '密级必填', trigger: ['blur', 'change'] }], }) const deviceTypeList = ref<any[]>([]) // 字典值 const secretLevelList = ref<any[]>([]) // 密级字典值 function getDict() { getDictByCode('deviceType').then((response) => { deviceTypeList.value = response.data }) // 密级 getDictByCode('secretLevel').then((response) => { secretLevelList.value = response.data }) } // 表单提交 function submitForm() { if (dataFormRef.value) { dataFormRef.value?.validate((valid: boolean) => { if (valid) { if (dialogStatus.value === 'create') { createData() } else if (dialogStatus.value === 'update') { updateData() } } }) } } // 新增数据 function createData() { btnLoading.value = true addDev(formData.value).then((res) => { ElMessageBox.confirm( '新增成功,是否继续新增?', '提示', { confirmButtonText: '是', cancelButtonText: '否', type: 'info', }, ).then(() => { resetForm() }).catch(() => { closeRefresh() }) btnLoading.value = false }).catch((_) => { btnLoading.value = false }) } // 更新数据 function updateData() { updateDev(formData.value).then((res) => { ElMessage.success('修改成功') btnLoading.value = false closeRefresh() }).catch((_) => { // 异常情况,loading置为false btnLoading.value = false }) } // 重置表单 function resetForm() { formData.value = { id: '', deptId: '', area: '', areaName: '', monitorName: '', deviceIp: '', devicePort: '', deviceUser: '', devicePassword: '', nvrIp: '', nvrPort: '', nvrUser: '', nvrPassword: '', nvrChannel: '', deviceType: '', deviceTypeName: '', longitude: '', latitude: '', description: '', location: '', // speed: 0, // stopTime: 0, // high: 0, // angle: 0, deviceStatus: '', deviceStatusName: '', secretLevel: '', // 密级 nvrIndexCode: '', // NVR国标号 cameraIndexCode: '', // 设备国标号 } } // ----------初始化、关闭对话框相关----------------- function initDialog(dialogstatus: string, row: DevListInfo) { dialogStatus.value = dialogstatus dialogVisible.value = true btnLoading.value = false if (dialogstatus === 'create') { resetForm() nextTick(() => { dataFormRef.value?.clearValidate() }) } else if (dialogstatus === 'update' || dialogstatus === 'detail') { nextTick(() => { dataFormRef.value?.clearValidate() }) getDev(row.id) } } function getDev(id: string) { getDevInfo(id).then((response) => { formData.value.id = response.data.id formData.value.deptId = response.data.deptId formData.value.area = response.data.area formData.value.areaName = response.data.areaName formData.value.monitorName = response.data.monitorName formData.value.deviceIp = response.data.deviceIp formData.value.devicePort = response.data.devicePort formData.value.deviceUser = response.data.deviceUser formData.value.devicePassword = response.data.devicePassword formData.value.nvrIp = response.data.nvrIp formData.value.nvrPort = response.data.nvrPort formData.value.nvrUser = response.data.nvrUser formData.value.nvrPassword = response.data.nvrPassword formData.value.nvrChannel = response.data.nvrChannel formData.value.deviceType = response.data.deviceType formData.value.deviceTypeName = response.data.deviceTypeName formData.value.longitude = response.data.longitude formData.value.latitude = response.data.latitude formData.value.description = response.data.description formData.value.location = response.data.location formData.value.deviceStatus = response.data.deviceStatus formData.value.deviceStatusName = response.data.deviceStatusName if (deviceTypeList.value.filter(item => item.value === formData.value.deviceType) && deviceTypeList.value.filter(item => item.value === formData.value.deviceType).length) { formData.value.deviceTypeName = deviceTypeList.value.filter(item => item.value === formData.value.deviceType)[0].name } formData.value.secretLevel = `${response.data.secretLevel}` // 密级 formData.value.nvrIndexCode = response.data.nvrIndexCode // NVR国标号 formData.value.cameraIndexCode = response.data.cameraIndexCode // 设备国标号 }) } // 打开区域选择弹窗 const openAreaSelect = () => { areaSelectShow.value = true setTimeout(areaSelect.value.initDialog(), 200) } const changeParent = (node: any) => { formData.value.area = node.id formData.value.areaName = node.name areaSelectShow.value = false } function selectType() { formData.value.deviceTypeName = deviceTypeList.value.filter(item => item.value === formData.value.deviceType)[0].name } // 关闭并刷新 function closeRefresh() { dialogVisible.value = false resetForm() emits('closeRefresh') } // 关闭弹窗 function dialogClose() { dialogVisible.value = false resetForm() closeRefresh() } onMounted(() => { getDict() }) // ----------------------- 以下是暴露的方法内容 ---------------------------- defineExpose({ initDialog }) </script> <template> <el-dialog v-model="dialogVisible" :title="textMap[dialogStatus]" width="1000" :before-close="dialogClose" append-to-body :open-delay="0" :close-on-click-modal="false" > <el-form ref="dataFormRef" :model="formData" :rules="rules" label-position="left" label-width="80px" class="form-container" size="default" @submit.prevent > <el-row :gutter="10"> <el-col :span="12" class="grid-cell"> <el-form-item label="设备名称" prop="monitorName" class="required"> <el-input v-model.trim="formData.monitorName" :disabled="dialogStatus === 'detail'" type="text" placeholder="必填" clearable /> </el-form-item> </el-col> <el-col :span="12" class="grid-cell"> <el-form-item label="设备类型" prop="deviceType" class="required"> <el-select v-model="formData.deviceType" :disabled="dialogStatus === 'detail'" placeholder="必选" style="width: 100%;" @change="selectType" > <el-option v-for="item in deviceTypeList" :key="item.value" :label="item.name" :value="item.value" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="所属区域" prop="area"> <el-input :value="formData.areaName" :disabled="dialogStatus === 'detail'" type="text" placeholder="必选" @click="openAreaSelect" /> </el-form-item> </el-col> <el-col :span="12" class="grid-cell"> <el-form-item label="所属单位" prop="deptId" class="required"> <dept-select v-model="formData.deptId" :disabled="dialogStatus === 'detail'" :dept-show="true" /> </el-form-item> </el-col> <el-col :span="12" class="grid-cell"> <el-form-item label="密级" prop="secretLevel" class="required"> <el-select v-model="formData.secretLevel" :disabled="dialogStatus === 'detail'" placeholder="必选" style="width: 100%;" @change="selectType" > <el-option v-for="item in secretLevelList" :key="item.value" :label="item.name" :value="item.value" /> </el-select> </el-form-item> </el-col> </el-row> <!-- <el-row style="margin-bottom: 10px;margin-top: -10px;font-weight: bold;"> 摄像头信息: </el-row> --> <el-row :gutter="10"> <!-- <el-col :span="6" class="grid-cell"> <el-form-item label="ip地址" prop="deviceIp" class="required"> <el-input v-model.trim="formData.deviceIp" :disabled="dialogStatus === 'detail'" type="text" placeholder="必填" clearable /> </el-form-item> </el-col> <el-col :span="4" class="grid-cell"> <el-form-item label="端口" prop="devicePort" class="required" label-width="52px"> <el-input v-model.trim="formData.devicePort" :disabled="dialogStatus === 'detail'" type="text" placeholder="必填" clearable /> </el-form-item> </el-col> <el-col :span="5" class="grid-cell"> <el-form-item label="登录账号" prop="deviceUser" class="required"> <el-input v-model.trim="formData.deviceUser" :disabled="dialogStatus === 'detail'" type="text" placeholder="必填" clearable /> </el-form-item> </el-col> <el-col :span="5" class="grid-cell"> <el-form-item label="登录密码" prop="devicePassword" class="required"> <el-input v-model.trim="formData.devicePassword" :disabled="dialogStatus === 'detail'" type="text" placeholder="必填" clearable show-password /> </el-form-item> </el-col> --> <el-col :span="24" class="grid-cell"> <el-form-item label="设备国标号" prop="cameraIndexCode" class="required" label-width="100px"> <el-input v-model.trim="formData.cameraIndexCode" :disabled="dialogStatus === 'detail'" placeholder="必填" clearable type="textarea" maxlength="100" show-word-limit autosize /> </el-form-item> </el-col> </el-row> <!-- <el-row style="margin-bottom: 10px;margin-top: -10px;font-weight: bold;"> NVR信息: </el-row> --> <el-row :gutter="10"> <!-- <el-col :span="6" class="grid-cell"> <el-form-item label="ip地址" prop="nvrIp" class="required"> <el-input v-model.trim="formData.nvrIp" :disabled="dialogStatus === 'detail'" type="text" placeholder="必填" clearable /> </el-form-item> </el-col> <el-col :span="4" class="grid-cell"> <el-form-item label="端口" prop="nvrPort" class="required" label-width="52px"> <el-input v-model.trim="formData.nvrPort" :disabled="dialogStatus === 'detail'" type="text" placeholder="必填" clearable /> </el-form-item> </el-col> <el-col :span="5" class="grid-cell"> <el-form-item label="登录账号" prop="nvrUser" class="required"> <el-input v-model.trim="formData.nvrUser" :disabled="dialogStatus === 'detail'" type="text" placeholder="必填" clearable /> </el-form-item> </el-col> <el-col :span="5" class="grid-cell"> <el-form-item label="登录密码" prop="nvrPassword" class="required"> <el-input v-model.trim="formData.nvrPassword" :disabled="dialogStatus === 'detail'" type="text" placeholder="必填" clearable show-password /> </el-form-item> </el-col> <el-col :span="4" class="grid-cell"> <el-form-item label="通道号" prop="nvrChannel" class="required" label-width="70px"> <el-input v-model.trim="formData.nvrChannel" :disabled="dialogStatus === 'detail'" type="text" placeholder="必填" clearable /> </el-form-item> </el-col> --> <el-col :span="12" class="grid-cell"> <el-form-item label="经度" prop="longitude" class="required"> <el-input v-model.trim="formData.longitude" :disabled="dialogStatus === 'detail'" type="text" placeholder="必填" clearable /> </el-form-item> </el-col> <el-col :span="12" class="grid-cell"> <el-form-item label="纬度" prop="latitude" class="required"> <el-input v-model.trim="formData.latitude" :disabled="dialogStatus === 'detail'" type="text" placeholder="必填" clearable /> </el-form-item> </el-col> <!-- <el-col :span="24" class="grid-cell"> <el-form-item label="NVR国标号" prop="nvrIndexCode" class="required" label-width="100px"> <el-input v-model.trim="formData.nvrIndexCode" :disabled="dialogStatus === 'detail'" placeholder="必填" clearable type="textarea" maxlength="100" show-word-limit autosize /> </el-form-item> </el-col> --> <el-col :span="24" class="grid-cell"> <el-form-item label="详细位置" class="required"> <el-input v-model.trim="formData.location" :disabled="dialogStatus === 'detail'" type="text" placeholder="非必填" clearable /> </el-form-item> </el-col> <el-col :span="24" class="grid-cell"> <el-form-item label="备注" class="required"> <el-input v-model.trim="formData.description" :disabled="dialogStatus === 'detail'" type="text" placeholder="非必填" clearable maxlength="50" show-word-limit /> </el-form-item> </el-col> </el-row> </el-form> <template #footer> <div v-show="dialogStatus !== 'detail'" class="dialog-footer"> <el-button :loading="btnLoading" type="primary" @click="submitForm"> 保存 </el-button> <el-button @click="dialogClose"> 取消 </el-button> </div> </template> <area-select-tree v-show="areaSelectShow" ref="areaSelect" @select-done="changeParent" /> </el-dialog> </template> <style lang="scss" scoped> .form-container { width: 100%; .full-width-input { width: 100%; } .dict-detail { padding: 10px; .title { font-size: 16px; font-weight: bold; margin-bottom: 15px; } } } </style>