<!-- Description: 设备运维 Author: 李亚光 Date: 2024-01-03 --> <script lang="ts" setup name="H5DeviceOperation"> import { keepSearchParams } from '@/utils/keepQuery' import dayjs from 'dayjs' import { showToast, showImagePreview, showLoadingToast, closeToast } from 'vant' import { getDictByCode } from '@/api/system/dict' import { addDeviceInstall, getDeviceInstall, uploadApi } from '@/api/mobile/operation' import useUserStore from '@/store/modules/user' const $router = useRouter() const userInfo = useUserStore() const baseInfo = ref({ repairType: '', repairTypeName: '', devcode: '', ledgerNumber: '', position: '', repairTime: '', repairPerson: '', repairContent: '', deptid: '', deviceType: '', ledgerName: '', deviceId: '', photo: [] as string[] }) baseInfo.value.repairPerson = userInfo.name const disabledPosition = ref(true) // 是否禁用安装位置 const isRequiredPhoto = ref(true) // 是否必传照片 watch(() => baseInfo.value.repairTypeName, (newVal) => { disabledPosition.value = true isRequiredPhoto.value = true // 设备回装支持更改安装位置 // 拆下返厂可不传现场照片 if (newVal) { if (newVal.includes('回装')) { disabledPosition.value = false } else if (newVal.includes('返厂')) { isRequiredPhoto.value = false } } }, { deep: true }) // 运维类型 const showRepairType = ref(false) const repairTypeValue = ref([]) const onConfirmRepairType = ({ selectedValues, selectedOptions }) => { baseInfo.value.repairType = selectedOptions[0]?.value baseInfo.value.repairTypeName = selectedOptions[0]?.text repairTypeValue.value = selectedValues showRepairType.value = false } // 根据设备编号查询设备信息 const searchDeviceInfo = () => { if (!baseInfo.value.devcode) { return } getDeviceInstall(baseInfo.value.devcode).then(res => { if (res.data) { if (!res.data.manufacturerId.includes(userInfo.avatar)) { showToast('该设备厂商不属于当前账号厂商,请检查') baseInfo.value.ledgerNumber = '' baseInfo.value.position = '' baseInfo.value.deptid = '' baseInfo.value.deviceType = '' baseInfo.value.ledgerName = '' baseInfo.value.devcode = '' return } baseInfo.value.deviceId = res.data.deviceId baseInfo.value.ledgerNumber = res.data.tagNumber baseInfo.value.position = res.data.position baseInfo.value.deptid = res.data.deptid baseInfo.value.deviceType = res.data.devTypeId baseInfo.value.ledgerName = res.data.ledgerName if (!res.data.tagNumber || !res.data.position) { showToast('该设备缺少关键位置信息') } } else { baseInfo.value.ledgerNumber = '' baseInfo.value.position = '' showToast('未查询到设备信息,请检查设备编号是否正确') } }) } // 运维日期 const showDatePicker = ref(false) const pickerValueRepairDate = ref<string[]>([]) pickerValueRepairDate.value = dayjs().format('YYYY-MM-DD').split('-') baseInfo.value.repairTime = pickerValueRepairDate.value.join('-') const onConfirmRepairDate = ({ selectedValues }) => { baseInfo.value.repairTime = selectedValues.join('-') pickerValueRepairDate.value = selectedValues showDatePicker.value = false } // 现场照片 // const showCamera = ref(false) // 拍照 const takePictures = () => { if (baseInfo.value.photo.length >= 3) { showToast('最多上传三张照片') return } // 调用摄像头 const camera = document.getElementById('camera-photo') camera?.click() } const fileRef = ref() // 文件上传input,获取input的引用 const onFileChange = (event: any) => { // 原生上传 if (event.target.files?.length !== 0) { const file = event.target.files[0] const fd = new FormData() showLoadingToast({ duration: 0, message: '加载中...', forbidClick: true, loadingType: 'spinner', }) fd.append('file', file) uploadApi(fd).then(res => { baseInfo.value.photo.push(res.data as string) fileRef.value.value = '' closeToast() }).catch(() => { fileRef.value.value = '' closeToast() }) // const reader = new FileReader() // 实例化FileReader // reader.readAsDataURL(file) // 读取成功以后执行的方法 // reader.onload = (e) => { // formRef.value.resetValidation('photo') // let img = new Image() // // base64 // img.src = e.target.result // console.log(img.src, 'img.src') // img.onload = () => { // imagetoCanvas(img) //Image 对象转变为一个 Canvas 类型对象,i为遍历的下标 // } // } } } // 删除图片 const deletePhoto = (index: number) => { baseInfo.value.photo.splice(index, 1) } // 图片地址 const getPhotoUrl = computed(() => { return (url: string) => { return `${window.localStorage.getItem('url-bj-well')}/static/${url}` } }) // 预览图片 const previewPhoto = (index: number) => { showImagePreview({ images: baseInfo.value.photo.map((item) => getPhotoUrl.value(item)), startPosition: index }) } // 获取字典 const repairTypeColumns = ref<{ text: string; value: string }[]>([]) // 运维类型 const fetchDict = () => { getDictByCode('repairType').then(res => { repairTypeColumns.value = res.data.filter((item: { id: string; name: string; value: string }) => !item.name.includes('新装')).map((item: { id: string; name: string; value: string }) => ({ text: item.name, value: item.value })) }) } fetchDict() // 查看设备数据 const deviceData = () => { if (!baseInfo.value.devcode) { showToast('设备编号不能为空') return } $router.push({ name: 'OperationData', query: { devcode: baseInfo.value.devcode } }) } // 保存 const formRef = ref() const loading = ref(false) const save = () => { // 表单验证 formRef.value.validate(['repairType', 'devcode', 'tagNumber', 'position', 'repairTime', 'repairPerson', 'repairContent']).then(() => { // 验证现场照片 if (isRequiredPhoto.value && !baseInfo.value.photo.length) { showToast('请上传现场照片') return } loading.value = true const data = JSON.parse(JSON.stringify(baseInfo.value)) // 将图片放在单个属性中 if (data.photo.length) { data.photo.forEach((element: string, index: number) => { data[`photo${index + 1}`] = element }) } // 清空图片数组 data.photo = undefined addDeviceInstall(data).then(() => { loading.value = false $router.push({ name: 'OperationDataSuccess', query: { row: JSON.stringify(baseInfo.value) } }) }).catch(() => { loading.value = false }) }) } // 页面缓存 onBeforeRouteLeave((to: any) => { keepSearchParams(to.path, 'H5DeviceOperation') }) onActivated(() => { if (!($router.options.history.state.forward as string || '').includes('operation/data')) { baseInfo.value = { repairType: '', repairTypeName: '', devcode: '', ledgerNumber: '', position: '', repairTime: '', repairPerson: '', repairContent: '', photo: [] as string[], deptid: '', deviceType: '', ledgerName: '', deviceId: '', } } }) </script> <template> <div v-loading="loading" class="device-container"> <!-- 设备信息 --> <div class="device-info"> <van-form ref="formRef"> <van-cell-group> <!-- 运维类型 --> <van-field v-model="baseInfo.repairTypeName" label="运维类型" is-link readonly name="repairType" placeholder="选择运维类型" required @click="showRepairType = true" input-align="right" :rules="[{ required: true, message: '请选择运维类型' }]" /> <van-popup v-model:show="showRepairType" destroy-on-close position="bottom"> <van-picker :columns="repairTypeColumns" title="运维类型" :model-value="repairTypeValue" @confirm="onConfirmRepairType" @cancel="showRepairType = false" /> </van-popup> <!-- 设备编号 --> <van-field v-model="baseInfo.devcode" label="设备编号" name="devcode" placeholder="输入设备编号" required input-align="right" @blur="searchDeviceInfo" clearable :rules="[{ required: true, message: '请输入设备编号' }]" /> <!-- 安装位置 --> <van-field v-model="baseInfo.ledgerNumber" :readonly="disabledPosition" label="安装位置" name="tagNumber" placeholder="安装位置" required input-align="right" :rules="[{ required: true, message: '请确认设备编号是否正确' }]" /> <!-- 详细位置 --> <van-field v-model="baseInfo.position" :readonly="disabledPosition" label="详细位置" name="position" placeholder="详细位置" required input-align="right" :rules="[{ required: true, message: '请确认设备编号是否正确' }]" /> <!-- 运维日期 --> <van-field v-model="baseInfo.repairTime" is-link label="运维日期" name="repairTime" required placeholder="选择运维日期" @click="showDatePicker = true" input-align="right" :rules="[{ required: true, message: '请选择运维日期' }]" /> <van-popup v-model:show="showDatePicker" destroy-on-close position="bottom"> <van-date-picker :model-value="pickerValueRepairDate" @confirm="onConfirmRepairDate" @cancel="showDatePicker = false" /> </van-popup> <!-- 运维人员 --> <van-field v-model="baseInfo.repairPerson" label="运维人员" name="repairPerson" placeholder="输入运维人员" required input-align="right" clearable :rules="[{ required: true, message: '请输入运维人员' }]" /> <!-- 运维内容 --> <van-field v-model="baseInfo.repairContent" label="运维内容" name="repairContent" placeholder="输入运维内容" required input-align="right" clearable :rules="[{ required: true, message: '请输入运维内容' }]" /> <!-- 现场照片 --> <van-field readonly name="photo" label="现场照片" :required="isRequiredPhoto" :rules="[{ required: isRequiredPhoto, message: '请上传现场照片' }]" input-align="center"> <template #input> <div v-for="(item, index) in baseInfo.photo" :key="index" class="show-photo"> <div class="del_d"> <div class="del" @click="deletePhoto(index)"></div> </div> <img :src="getPhotoUrl(item)" alt="" srcset="" width="30px" height="30px" @click="previewPhoto(index)"> </div> </template> <template #right-icon> <div class="camera" @click="takePictures" /> </template> </van-field> <!-- 照相组件 --> <input ref="fileRef" style="display: none;opacity: 0;" multiple id="camera-photo" type="file" accept="image/*" @change="onFileChange"> </van-cell-group> </van-form> </div> </div> <!-- 按钮 --> <div class="btn-container"> <el-button type="primary" :disabled="loading" style="width: 48%;" @click="save">保存</el-button> <el-button type="primary" :disabled="loading" plain style="width: 48%;" @click="deviceData">查看设备数据</el-button> </div> </template> <style lang="scss" scoped> .show-photo { position: relative; margin-left: 10px; } .del_d { position: absolute; width: 16px; height: 16px; background: red; border-radius: 50%; top: -10px; right: -10px; display: flex; align-items: center; justify-content: center; cursor: pointer; .del { width: 10px; height: 3px; background: white; } } $primary: #0D76D4; $--van-primary-color: #0D76D4; ::v-deep(.van-picker-column__item--selected) { color: $primary !important; } .device-container { width: 100%; height: calc(100vh - 40px); overflow: hidden; position: relative; .device-info { background-color: #fff; width: 96%; margin: 0 auto; margin-top: 1vh; border-radius: 6px; padding: 4px; .title { font-weight: 700; font-size: 1.1rem; padding-left: 0.5rem; margin-top: 0.5rem; } ::v-deep(.van-cell__title) { font-weight: 700; font-size: 1rem; } ::v-deep(.van-cell__value) { // font-weight: 700; font-size: 1rem; } } } .camera { width: 24px; height: 24px; background: url('@/assets/icons/icon-camera.svg') no-repeat center center / cover; } .btn-container { position: absolute; width: 96%; bottom: 1vh; display: flex; justify-content: space-around; // margin: 0 auto; left: 50%; transform: translateX(-50%); ::v-deep(.el-button) { font-size: 18px; } } </style>