| | |
| | | <template> |
| | | <FormDialog |
| | | v-model="dialogVisible" |
| | | title="上传巡检记录" |
| | | <FormDialog v-model="dialogVisible" |
| | | :title="operationType === 'view' ? '巡检记录详情' : '上传巡检记录'" |
| | | width="980px" |
| | | :operation-type="operationType" |
| | | @close="handleClose" |
| | | @cancel="handleClose" |
| | | > |
| | | @cancel="handleClose"> |
| | | <main class="upload-content"> |
| | | <el-card v-if="taskInfo" class="section-card"> |
| | | <el-descriptions :column="1" border> |
| | | <el-card v-if="taskInfo" |
| | | class="section-card"> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="巡检任务名称"> |
| | | {{ taskInfo.taskName || "-" }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="巡检项目"> |
| | | {{ taskInfo.inspectionProject || "-" }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="执行巡检人"> |
| | | <template v-if="formattedInspector && formattedInspector.length"> |
| | | <el-tag v-for="tag in formattedInspector" |
| | | :key="tag" |
| | | size="small" |
| | | style="margin-right: 4px"> |
| | | {{ tag }} |
| | | </el-tag> |
| | | </template> |
| | | <span v-else>--</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="频次"> |
| | | {{ formatFrequencyType(taskInfo.frequencyType) || "-" }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="开始日期与时间"> |
| | | {{ formatFrequencyDetail(taskInfo.frequencyDetail) || "-" }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="登记人"> |
| | | {{ taskInfo.registrant || "-" }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="登记日期"> |
| | | {{ formatDateTime(taskInfo.createTime) || "-" }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="备注"> |
| | | {{ taskInfo.remarks || "-" }} |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | </el-card> |
| | | |
| | | <el-card class="section-card"> |
| | | <h3>巡检状态</h3> |
| | | <el-radio-group v-model="hasException"> |
| | | <el-radio-group v-model="hasException" |
| | | :disabled="operationType === 'view'"> |
| | | <el-radio-button :value="false">正常</el-radio-button> |
| | | <el-radio-button :value="true">存在异常</el-radio-button> |
| | | </el-radio-group> |
| | | </el-card> |
| | | |
| | | <el-card v-if="hasException === true" class="section-card"> |
| | | <el-card v-if="hasException === true" |
| | | class="section-card"> |
| | | <h3>异常描述</h3> |
| | | <el-input |
| | | v-model="abnormalDescription" |
| | | <el-input v-model="abnormalDescription" |
| | | type="textarea" |
| | | maxlength="500" |
| | | show-word-limit |
| | | :rows="4" |
| | | placeholder="请描述异常情况..." |
| | | /> |
| | | :disabled="operationType === 'view'" |
| | | placeholder="请描述异常情况..." /> |
| | | </el-card> |
| | | |
| | | <el-card v-if="hasException === true" class="section-card"> |
| | | <el-tabs v-model="currentUploadType"> |
| | | <el-tab-pane label="生产前" name="before" /> |
| | | <el-tab-pane label="生产中" name="after" /> |
| | | <el-tab-pane label="生产后" name="issue" /> |
| | | </el-tabs> |
| | | |
| | | <div class="upload-buttons"> |
| | | <el-upload |
| | | :show-file-list="false" |
| | | <el-card v-if="hasException === true" |
| | | class="section-card"> |
| | | <div class="upload-buttons" |
| | | v-if="operationType !== 'view'"> |
| | | <el-upload :show-file-list="false" |
| | | :http-request="uploadFile" |
| | | :disabled="getCurrentFiles().length >= uploadConfig.limit || uploading" |
| | | accept="image/*" |
| | | > |
| | | <el-button type="primary" :loading="uploading"> |
| | | <el-icon><Camera /></el-icon> |
| | | :disabled="beforeModelValue.length >= uploadConfig.limit || uploading" |
| | | accept="image/*"> |
| | | <el-button type="primary" |
| | | :loading="uploading"> |
| | | <el-icon> |
| | | <Camera /> |
| | | </el-icon> |
| | | 选择图片 |
| | | </el-button> |
| | | </el-upload> |
| | | |
| | | <el-upload |
| | | :show-file-list="false" |
| | | <el-upload :show-file-list="false" |
| | | :http-request="uploadFile" |
| | | :disabled="getCurrentFiles().length >= uploadConfig.limit || uploading" |
| | | accept="video/*" |
| | | > |
| | | <el-button type="success" :loading="uploading"> |
| | | <el-icon><VideoCamera /></el-icon> |
| | | :disabled="beforeModelValue.length >= uploadConfig.limit || uploading" |
| | | accept="video/*"> |
| | | <el-button type="success" |
| | | :loading="uploading"> |
| | | <el-icon> |
| | | <VideoCamera /> |
| | | </el-icon> |
| | | 选择视频 |
| | | </el-button> |
| | | </el-upload> |
| | | </div> |
| | | |
| | | <el-progress |
| | | v-if="uploading" |
| | | <el-progress v-if="uploading" |
| | | :percentage="uploadProgress" |
| | | class="upload-progress" |
| | | /> |
| | | |
| | | <div v-if="getCurrentFiles().length" class="file-list"> |
| | | <div |
| | | v-for="(file, index) in getCurrentFiles()" |
| | | class="upload-progress" /> |
| | | <div v-if="beforeModelValue.length" |
| | | class="file-list"> |
| | | <div v-for="(file, index) in beforeModelValue" |
| | | :key="file.uid || file.id || index" |
| | | class="file-item" |
| | | > |
| | | class="file-item"> |
| | | <div class="file-preview-container"> |
| | | <el-image |
| | | v-if="file.type === 'image' || !file.type" |
| | | <el-image v-if="file.type === 'image' || !file.type" |
| | | :src="file.url || file.tempFilePath || file.path || file.downloadUrl" |
| | | fit="cover" |
| | | class="file-preview" |
| | | :preview-src-list="[file.url || file.tempFilePath || file.path || file.downloadUrl]" |
| | | preview-teleported |
| | | /> |
| | | |
| | | <div v-else class="video-preview" @click="previewVideo(file)"> |
| | | <el-icon><VideoCamera /></el-icon> |
| | | preview-teleported /> |
| | | <div v-else |
| | | class="video-preview" |
| | | @click="previewVideo(file)"> |
| | | <el-icon> |
| | | <VideoCamera /> |
| | | </el-icon> |
| | | <span>视频</span> |
| | | </div> |
| | | |
| | | <el-button |
| | | class="delete-btn" |
| | | <el-button class="delete-btn" |
| | | type="danger" |
| | | circle |
| | | size="small" |
| | | @click="removeFile(index)" |
| | | > |
| | | <el-icon><Close /></el-icon> |
| | | v-if="operationType !== 'view'" |
| | | @click="removeFile(index)"> |
| | | <el-icon> |
| | | <Close /> |
| | | </el-icon> |
| | | </el-button> |
| | | </div> |
| | | |
| | | <div class="file-info"> |
| | | <div class="file-name"> |
| | | {{ file.bucketFilename || file.name || (file.type === "image" ? "图片" : "视频") }} |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <el-empty |
| | | v-else |
| | | :description="`请选择要上传的${getUploadTypeText()}图片或视频`" |
| | | /> |
| | | |
| | | <el-alert |
| | | class="upload-summary" |
| | | type="info" |
| | | :closable="false" |
| | | :title="`生产前:${beforeModelValue.length}个文件 | 生产中:${afterModelValue.length}个文件 | 生产后:${issueModelValue.length}个文件`" |
| | | /> |
| | | <el-empty v-else |
| | | :description="operationType === 'view' ? '暂无异常照片或视频' : '请选择要上传的巡检图片或视频'" /> |
| | | </el-card> |
| | | |
| | | <el-result |
| | | v-if="hasException === false" |
| | | <el-result v-if="hasException === false" |
| | | icon="success" |
| | | title="设备运行正常" |
| | | sub-title="无需上传照片" |
| | | /> |
| | | :sub-title="operationType === 'view' ? '' : '无需上传照片'" /> |
| | | </main> |
| | | |
| | | <template #footer> |
| | | <footer class="footer-buttons"> |
| | | <el-button type="primary" @click="submitUpload">提交</el-button> |
| | | <el-button v-if="hasException === true" type="warning" @click="goToRepair"> |
| | | <el-button type="primary" |
| | | v-if="operationType !== 'view'" |
| | | @click="submitUpload">提交</el-button> |
| | | <el-button v-if="hasException === true && operationType !== 'view'" |
| | | type="warning" |
| | | @click="goToRepair"> |
| | | 新增报修 |
| | | </el-button> |
| | | <el-button @click="handleClose">取消</el-button> |
| | | <el-button @click="handleClose">{{ operationType === 'view' ? '关闭' : '取消' }}</el-button> |
| | | </footer> |
| | | </template> |
| | | </FormDialog> |
| | | |
| | | <el-dialog |
| | | v-model="showVideoDialog" |
| | | <el-dialog v-model="showVideoDialog" |
| | | :title="currentVideoFile?.originalFilename || currentVideoFile?.name || '视频预览'" |
| | | width="720px" |
| | | > |
| | | <video |
| | | v-if="currentVideoFile" |
| | | width="720px"> |
| | | <video v-if="currentVideoFile" |
| | | :src="currentVideoFile.url || currentVideoFile.downloadUrl" |
| | | class="video-player" |
| | | controls |
| | | autoplay |
| | | /> |
| | | autoplay /> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | |
| | | import { ElLoading, ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Camera, Close, VideoCamera } from "@element-plus/icons-vue"; |
| | | import axios from "axios"; |
| | | import dayjs from "dayjs"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { uploadInspectionTask } from "@/api/inspectionManagement/index.js"; |
| | | import { getToken } from "@/utils/auth"; |
| | |
| | | const taskInfo = ref(null); |
| | | const uploading = ref(false); |
| | | const uploadProgress = ref(0); |
| | | const operationType = ref("add"); // add, view |
| | | |
| | | const beforeModelValue = ref([]); |
| | | const afterModelValue = ref([]); |
| | | const issueModelValue = ref([]); |
| | | |
| | | const currentUploadType = ref("before"); |
| | | const formattedInspector = computed(() => { |
| | | if (!taskInfo.value?.inspector) return []; |
| | | if (Array.isArray(taskInfo.value.inspector)) return taskInfo.value.inspector; |
| | | if (typeof taskInfo.value.inspector === "string") { |
| | | return taskInfo.value.inspector |
| | | .split(",") |
| | | .map(s => s.trim()) |
| | | .filter(s => s); |
| | | } |
| | | return [taskInfo.value.inspector]; |
| | | }); |
| | | |
| | | const formatFrequencyType = type => { |
| | | const mapping = { |
| | | DAILY: "每日", |
| | | WEEKLY: "每周", |
| | | MONTHLY: "每月", |
| | | QUARTERLY: "季度", |
| | | }; |
| | | return mapping[type] || type; |
| | | }; |
| | | |
| | | const formatFrequencyDetail = detail => { |
| | | if (typeof detail !== "string") return detail; |
| | | const replacements = { |
| | | MON: "周一", |
| | | TUE: "周二", |
| | | WED: "周三", |
| | | THU: "周四", |
| | | FRI: "周五", |
| | | SAT: "周六", |
| | | SUN: "周日", |
| | | }; |
| | | return detail.replace( |
| | | /MON|TUE|WED|THU|FRI|SAT|SUN/g, |
| | | match => replacements[match] |
| | | ); |
| | | }; |
| | | |
| | | const formatDateTime = date => { |
| | | if (!date) return "-"; |
| | | return dayjs(date).format("YYYY-MM-DD HH:mm:ss"); |
| | | }; |
| | | |
| | | const hasException = ref(null); |
| | | const abnormalDescription = ref(""); |
| | | |
| | |
| | | |
| | | return { |
| | | ...item, |
| | | url: processFileUrl(item.url || item.previewURL || item.downloadUrl || item.path || ""), |
| | | url: processFileUrl( |
| | | item.url || item.previewURL || item.downloadUrl || item.path || "" |
| | | ), |
| | | downloadUrl: processFileUrl( |
| | | item.downloadUrl || item.url || item.previewURL || item.path || "" |
| | | ), |
| | |
| | | const resetState = () => { |
| | | taskInfo.value = null; |
| | | beforeModelValue.value = []; |
| | | afterModelValue.value = []; |
| | | issueModelValue.value = []; |
| | | currentUploadType.value = "before"; |
| | | hasException.value = null; |
| | | abnormalDescription.value = ""; |
| | | uploading.value = false; |
| | | uploadProgress.value = 0; |
| | | showVideoDialog.value = false; |
| | | currentVideoFile.value = null; |
| | | operationType.value = "add"; |
| | | }; |
| | | |
| | | const openDialog = row => { |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type || "add"; |
| | | const raw = JSON.parse(JSON.stringify(row?.__raw || row || {})); |
| | | taskInfo.value = raw; |
| | | |
| | |
| | | raw.commonFileListBeforeVO || raw.commonFileListBefore || [], |
| | | "image" |
| | | ); |
| | | afterModelValue.value = normalizeList( |
| | | raw.commonFileListVO || raw.commonFileList || [], |
| | | "image" |
| | | ); |
| | | issueModelValue.value = normalizeList( |
| | | raw.commonFileListAfterVO || raw.commonFileListAfter || [], |
| | | "image" |
| | | ); |
| | | |
| | | abnormalDescription.value = raw.abnormalDescription || ""; |
| | | |
| | | if (raw.hasException !== undefined && raw.hasException !== null) { |
| | | hasException.value = raw.hasException; |
| | | } else if (raw.inspectionResult !== undefined && raw.inspectionResult !== null) { |
| | | } else if ( |
| | | raw.inspectionResult !== undefined && |
| | | raw.inspectionResult !== null |
| | | ) { |
| | | hasException.value = String(raw.inspectionResult) === "0"; |
| | | } else { |
| | | hasException.value = null; |
| | | } |
| | | |
| | | if ( |
| | | hasException.value !== true && |
| | | (beforeModelValue.value.length || afterModelValue.value.length || issueModelValue.value.length) |
| | | ) { |
| | | if (hasException.value !== true && beforeModelValue.value.length) { |
| | | hasException.value = true; |
| | | } |
| | | |
| | |
| | | emit("closeDia"); |
| | | }; |
| | | |
| | | const getCurrentFiles = () => { |
| | | if (currentUploadType.value === "before") return beforeModelValue.value; |
| | | if (currentUploadType.value === "after") return afterModelValue.value; |
| | | if (currentUploadType.value === "issue") return issueModelValue.value; |
| | | return []; |
| | | }; |
| | | |
| | | const getUploadTypeText = () => { |
| | | if (currentUploadType.value === "before") return "生产前"; |
| | | if (currentUploadType.value === "after") return "生产中"; |
| | | if (currentUploadType.value === "issue") return "生产后"; |
| | | return ""; |
| | | }; |
| | | |
| | | const getTabType = () => { |
| | | if (currentUploadType.value === "before") return 10; |
| | | if (currentUploadType.value === "after") return 11; |
| | | if (currentUploadType.value === "issue") return 12; |
| | | return 10; |
| | | }; |
| | | |
| | | const previewVideo = file => { |
| | | currentVideoFile.value = file; |
| | | showVideoDialog.value = true; |
| | |
| | | const uploadFile = async uploadRequest => { |
| | | const rawFile = uploadRequest.file; |
| | | |
| | | if (getCurrentFiles().length >= uploadConfig.limit) { |
| | | if (beforeModelValue.value.length >= uploadConfig.limit) { |
| | | ElMessage.warning(`最多只能选择${uploadConfig.limit}个文件`); |
| | | return; |
| | | } |
| | | |
| | | const ext = rawFile.name.split(".").pop()?.toLowerCase(); |
| | | if (!uploadConfig.fileType.includes(ext)) { |
| | | ElMessage.warning(`文件格式不支持,请上传 ${uploadConfig.fileType.join("/")} 格式`); |
| | | ElMessage.warning( |
| | | `文件格式不支持,请上传 ${uploadConfig.fileType.join("/")} 格式` |
| | | ); |
| | | return; |
| | | } |
| | | |
| | |
| | | |
| | | const formData = new FormData(); |
| | | formData.append("files", rawFile); |
| | | formData.append("type", getTabType()); |
| | | formData.append("type", 10); // 生产前固定为10 |
| | | |
| | | uploading.value = true; |
| | | uploadProgress.value = 0; |
| | |
| | | const finalUrl = processFileUrl( |
| | | resultData.url || resultData.previewURL || resultData.downloadUrl || "" |
| | | ); |
| | | const finalName = resultData.name || resultData.originalFilename || resultData.bucketFilename; |
| | | const finalName = |
| | | resultData.name || |
| | | resultData.originalFilename || |
| | | resultData.bucketFilename; |
| | | const finalId = resultData.tempId || resultData.id || resultData.tempFileId; |
| | | |
| | | const uploadedFile = { |
| | |
| | | uid: `${Date.now()}-${Math.random()}`, |
| | | }; |
| | | |
| | | getCurrentFiles().push(uploadedFile); |
| | | beforeModelValue.value.push(uploadedFile); |
| | | ElMessage.success("上传成功"); |
| | | } catch (error) { |
| | | ElMessage.error(error?.message || "上传失败"); |
| | |
| | | } |
| | | |
| | | if (hasException.value === true) { |
| | | const totalFiles = |
| | | beforeModelValue.value.length + |
| | | afterModelValue.value.length + |
| | | issueModelValue.value.length; |
| | | const totalFiles = beforeModelValue.value.length; |
| | | |
| | | if (!totalFiles) { |
| | | ElMessage.warning("请上传异常照片或视频"); |
| | |
| | | }); |
| | | |
| | | try { |
| | | const allFiles = [ |
| | | ...beforeModelValue.value, |
| | | ...afterModelValue.value, |
| | | ...issueModelValue.value, |
| | | ]; |
| | | const allFiles = [...beforeModelValue.value]; |
| | | |
| | | const tempFileIds = allFiles |
| | | .map(item => item?.tempId ?? item?.tempFileId ?? item?.id) |
| | |
| | | const submitData = { |
| | | ...baseTaskInfo, |
| | | commonFileListBeforeDTO: beforeModelValue.value.map(buildFileItem), |
| | | commonFileListDTO: afterModelValue.value.map(buildFileItem), |
| | | commonFileListAfterDTO: issueModelValue.value.map(buildFileItem), |
| | | commonFileListDTO: [], |
| | | commonFileListAfterDTO: [], |
| | | hasException: hasException.value, |
| | | inspectionResult: hasException.value ? 0 : 1, |
| | | abnormalDescription: abnormalDescription.value, |
| | |
| | | await ElMessageBox.confirm("确定要删除这个文件吗?", "确认删除", { |
| | | type: "warning", |
| | | }); |
| | | getCurrentFiles().splice(index, 1); |
| | | beforeModelValue.value.splice(index, 1); |
| | | } catch {} |
| | | }; |
| | | |
| | |
| | | hasException: hasException.value, |
| | | inspectionResult: hasException.value ? 0 : 1, |
| | | commonFileListBeforeDTO: beforeModelValue.value.map(buildFileItem), |
| | | commonFileListDTO: afterModelValue.value.map(buildFileItem), |
| | | commonFileListAfterDTO: issueModelValue.value.map(buildFileItem), |
| | | commonFileListDTO: [], |
| | | commonFileListAfterDTO: [], |
| | | uploadedFiles: { |
| | | before: beforeModelValue.value, |
| | | after: afterModelValue.value, |
| | | issue: issueModelValue.value, |
| | | after: [], |
| | | issue: [], |
| | | }, |
| | | }; |
| | | |