| src/pages/inspectionUpload/attachment.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/inspectionUpload/components/formDia.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/inspectionUpload/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/inspectionUpload/upload.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/pages/inspectionUpload/attachment.vue
@@ -1,53 +1,28 @@ <template> <view class="attachment-page"> <!-- 页面头部 --> <PageHeader :title="`查看附件 - ${taskInfo?.taskName || ''}`" @back="goBack" /> <PageHeader :title="`查看附件 - ${taskInfo?.taskName || ''}`" @back="goBack" /> <!-- 页面内容 --> <view class="attachment-content"> <!-- 分类标签页 --> <view class="attachment-tabs"> <view class="tab-item" :class="{ active: currentViewType === 'before' }" @click="switchViewType('before')" > 生产前 ({{ getAttachmentsByType(0).length }}) </view> <view class="tab-item" :class="{ active: currentViewType === 'after' }" @click="switchViewType('after')" > 生产中 ({{ getAttachmentsByType(1).length }}) </view> <view class="tab-item" :class="{ active: currentViewType === 'issue' }" @click="switchViewType('issue')" > 生产后 ({{ getAttachmentsByType(2).length }}) </view> </view> <!-- 当前分类的附件列表 --> <view class="attachment-list-container"> <view v-if="getCurrentViewAttachments().length > 0" class="attachment-list"> <view v-for="(file, index) in getCurrentViewAttachments()" <view v-if="attachmentList.length > 0" class="attachment-list"> <view v-for="(file, index) in attachmentList" :key="index" class="attachment-item" @click="previewAttachment(file)" > @click="previewAttachment(file)"> <view class="attachment-preview-container"> <image v-if="isImageFile(file)" <image v-if="isImageFile(file)" :src="file.url || file.downloadUrl" class="attachment-preview" mode="aspectFill" /> <view v-else class="attachment-video-preview"> <u-icon name="video" size="40" color="#409eff"></u-icon> mode="aspectFill" /> <view v-else class="attachment-video-preview"> <u-icon name="video" size="40" color="#409eff"></u-icon> <text class="video-text">视频</text> </view> </view> @@ -57,31 +32,37 @@ </view> </view> </view> <view v-else class="attachment-empty"> <u-icon name="folder-open" size="60" color="#ccc"></u-icon> <view v-else class="attachment-empty"> <u-icon name="folder-open" size="60" color="#ccc"></u-icon> <text class="empty-text">该分类暂无附件</text> </view> </view> </view> <!-- 视频预览弹窗 --> <view v-if="showVideoDialog" class="video-modal-overlay" @click="closeVideoPreview"> <view class="video-modal-container" @click.stop> <view v-if="showVideoDialog" class="video-modal-overlay" @click="closeVideoPreview"> <view class="video-modal-container" @click.stop> <view class="video-modal-header"> <text class="video-modal-title">{{ currentVideoFile?.originalFilename || '视频预览' }}</text> <view class="close-btn-video" @click="closeVideoPreview"> <u-icon name="close" size="20" color="#fff"></u-icon> <view class="close-btn-video" @click="closeVideoPreview"> <u-icon name="close" size="20" color="#fff"></u-icon> </view> </view> <view class="video-modal-body"> <video v-if="currentVideoFile" <video v-if="currentVideoFile" :src="currentVideoFile.url || currentVideoFile.downloadUrl" class="video-player" controls autoplay @error="handleVideoError" ></video> @error="handleVideoError"></video> </view> </view> </view> @@ -89,19 +70,16 @@ </template> <script setup> import { ref } from 'vue'; import { onLoad } from '@dcloudio/uni-app'; import PageHeader from '@/components/PageHeader.vue'; import config from '@/config'; import { ref } from "vue"; import { onLoad } from "@dcloudio/uni-app"; import PageHeader from "@/components/PageHeader.vue"; import config from "@/config"; // 任务信息 const taskInfo = ref(null); // 附件列表 const attachmentList = ref([]); // 当前查看类型 const currentViewType = ref('before'); // 'before', 'after', 'issue' // 视频预览相关状态 const showVideoDialog = ref(false); @@ -111,16 +89,16 @@ const filePreviewBase = config.fileUrl; // 页面加载 onLoad((options) => { onLoad(options => { if (options.taskInfo) { try { taskInfo.value = JSON.parse(decodeURIComponent(options.taskInfo)); loadAttachments(); } catch (e) { console.error('解析任务信息失败:', e); console.error("解析任务信息失败:", e); uni.showToast({ title: '加载失败', icon: 'error' title: "加载失败", icon: "error", }); } } @@ -133,60 +111,66 @@ attachmentList.value = []; // 后端反显字段 const allList = Array.isArray(task?.commonFileList) ? task.commonFileList : []; const beforeList = Array.isArray(task?.commonFileListBefore) ? task.commonFileListBefore : allList.filter((f) => f?.type === 10); const afterList = Array.isArray(task?.commonFileListAfter) ? task.commonFileListAfter : allList.filter((f) => f?.type === 11); const issueList = Array.isArray(task?.commonFileListIssue) ? task.commonFileListIssue : allList.filter((f) => f?.type === 12); // 获取附件列表,优先从 commonFileListBeforeVO 获取 let rawList = []; if (Array.isArray(task.commonFileListBeforeVO)) { rawList = task.commonFileListBeforeVO; } else if (Array.isArray(task.commonFileListBefore)) { rawList = task.commonFileListBefore; } else if (Array.isArray(task.commonFileList)) { // 降级:从通用列表过滤 type 为 10 的 rawList = task.commonFileList.filter(f => f?.type === 10); } const mapToViewFile = (file, viewType) => { const u = normalizeFileUrl(file?.url || file?.downloadUrl || ''); const mapToViewFile = file => { // 优先使用 previewURL 或 url const rawUrl = file?.previewURL || file?.url || file?.downloadUrl || file?.downloadURL || ""; const u = normalizeFileUrl(rawUrl); return { ...file, type: viewType, name: file?.name || file?.originalFilename || file?.bucketFilename, bucketFilename: file?.bucketFilename || file?.name, name: file?.name || file?.originalFilename || file?.bucketFilename || "附件", bucketFilename: file?.bucketFilename || file?.name || file?.originalFilename, originalFilename: file?.originalFilename || file?.name, url: u, downloadUrl: u, size: file?.size || file?.byteSize, size: file?.size || file?.byteSize || 0, }; }; attachmentList.value.push(...beforeList.map((f) => mapToViewFile(f, 0))); attachmentList.value.push(...afterList.map((f) => mapToViewFile(f, 1))); attachmentList.value.push(...issueList.map((f) => mapToViewFile(f, 2))); attachmentList.value = rawList.map(f => mapToViewFile(f)); }; // 将后端返回的文件地址规范成可访问URL const normalizeFileUrl = (rawUrl) => { const normalizeFileUrl = rawUrl => { try { if (!rawUrl || typeof rawUrl !== 'string') return ''; if (!rawUrl || typeof rawUrl !== "string") return ""; const url = rawUrl.trim(); if (!url) return ''; if (!url) return ""; if (/^https?:\/\//i.test(url)) return url; if (url.startsWith('/')) return `${filePreviewBase}${url}`; if (url.startsWith("/")) return `${filePreviewBase}${url}`; // Windows path -> web path if (/^[a-zA-Z]:\\/.test(url)) { const normalized = url.replace(/\\/g, '/'); const idx = normalized.indexOf('/prod/'); const normalized = url.replace(/\\/g, "/"); const idx = normalized.indexOf("/prod/"); if (idx >= 0) { const relative = normalized.slice(idx + '/prod/'.length); const relative = normalized.slice(idx + "/prod/".length); return `${filePreviewBase}/${relative}`; } return normalized; } return `${filePreviewBase}/${url.replace(/^\//, '')}`; return `${filePreviewBase}/${url.replace(/^\//, "")}`; } catch (e) { return rawUrl || ''; return rawUrl || ""; } }; @@ -195,48 +179,24 @@ uni.navigateBack(); }; // 切换查看类型 const switchViewType = (type) => { currentViewType.value = type; }; // 根据type获取对应分类的附件 const getAttachmentsByType = (typeValue) => { return attachmentList.value.filter((file) => file.type === typeValue) || []; }; // 获取当前查看类型的附件 const getCurrentViewAttachments = () => { switch (currentViewType.value) { case 'before': return getAttachmentsByType(0); case 'after': return getAttachmentsByType(1); case 'issue': return getAttachmentsByType(2); default: return []; } }; // 判断是否为图片文件 const isImageFile = (file) => { if (file.contentType && file.contentType.startsWith('image/')) { const isImageFile = file => { if (file.contentType && file.contentType.startsWith("image/")) { return true; } if (file.type === 'image') return true; if (file.type === "image") return true; const name = file.bucketFilename || file.originalFilename || file.name || ''; const ext = name.split('.').pop()?.toLowerCase(); return ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext); const name = file.bucketFilename || file.originalFilename || file.name || ""; const ext = name.split(".").pop()?.toLowerCase(); return ["jpg", "jpeg", "png", "gif", "webp"].includes(ext); }; // 预览附件 const previewAttachment = (file) => { const previewAttachment = file => { if (isImageFile(file)) { const imageUrls = getCurrentViewAttachments() .filter((f) => isImageFile(f)) .map((f) => f.url || f.downloadUrl); const imageUrls = attachmentList.value .filter(f => isImageFile(f)) .map(f => f.url || f.downloadUrl); uni.previewImage({ urls: imageUrls, @@ -248,7 +208,7 @@ }; // 显示视频预览 const showVideoPreview = (file) => { const showVideoPreview = file => { currentVideoFile.value = file; showVideoDialog.value = true; }; @@ -262,17 +222,17 @@ // 视频播放错误处理 const handleVideoError = () => { uni.showToast({ title: '视频播放失败', icon: 'error', title: "视频播放失败", icon: "error", }); }; // 格式化文件大小 const formatFileSize = (size) => { if (!size) return ''; if (size < 1024) return size + 'B'; if (size < 1024 * 1024) return (size / 1024).toFixed(1) + 'KB'; return (size / (1024 * 1024)).toFixed(1) + 'MB'; const formatFileSize = size => { if (!size) return ""; if (size < 1024) return size + "B"; if (size < 1024 * 1024) return (size / 1024).toFixed(1) + "KB"; return (size / (1024 * 1024)).toFixed(1) + "MB"; }; </script> src/pages/inspectionUpload/components/formDia.vue
@@ -1,60 +1,53 @@ <template> <u-popup v-model="dialogVisitable" <u-popup v-model="dialogVisitable" mode="center" :round="10" :closeable="true" @close="cancel" > @close="cancel"> <view class="popup-content"> <view class="popup-header"> <text class="popup-title">巡检记录上传</text> </view> <view class="upload-container"> <!-- 异常状态选择 --> <view class="form-container"> <view class="title">巡检状态</view> <view class="exception-section"> <view class="exception-options"> <view class="exception-option" <view class="exception-option" :class="{ active: hasException === false }" @click="setExceptionStatus(false)" > <u-icon name="checkmark-circle" size="20" color="#52c41a"></u-icon> @click="setExceptionStatus(false)"> <u-icon name="checkmark-circle" size="20" color="#52c41a"></u-icon> <text class="option-text">正常</text> </view> <view class="exception-option" <view class="exception-option" :class="{ active: hasException === true }" @click="setExceptionStatus(true)" > <u-icon name="close-circle" size="20" color="#ff4d4f"></u-icon> @click="setExceptionStatus(true)"> <u-icon name="close-circle" size="20" color="#ff4d4f"></u-icon> <text class="option-text">存在异常</text> </view> </view> </view> </view> <!-- 异常描述(仅在异常时显示) --> <view class="form-container" v-if="hasException === true"> <view class="form-container" v-if="hasException === true"> <view class="title">异常描述</view> <u-input v-model="exceptionDescription" <u-input v-model="exceptionDescription" type="textarea" :maxlength="500" placeholder="请描述异常情况..." :customStyle="{ padding: '10px', backgroundColor: '#f5f5f5' }" /> :customStyle="{ padding: '10px', backgroundColor: '#f5f5f5' }" /> </view> <!-- 上传区域(仅在异常时显示) --> <template v-if="hasException === true"> <view class="form-container"> <view class="title">生产前</view> <u-upload :fileList="beforeModelValue" <view class="title">巡检照片</view> <u-upload :fileList="beforeModelValue" @afterRead="afterRead" @delete="deleteFile" name="before" @@ -62,78 +55,48 @@ :maxCount="10" :maxSize="5 * 1024 * 1024" accept="image/*" :previewFullImage="true" ></u-upload> </view> <view class="form-container"> <view class="title">生产后</view> <u-upload :fileList="afterModelValue" @afterRead="afterRead" @delete="deleteFile" name="after" multiple :maxCount="10" :maxSize="5 * 1024 * 1024" accept="image/*" :previewFullImage="true" ></u-upload> </view> <view class="form-container"> <view class="title">生产问题</view> <u-upload :fileList="issueModelValue" @afterRead="afterRead" @delete="deleteFile" name="issue" multiple :maxCount="10" :maxSize="5 * 1024 * 1024" accept="image/*" :previewFullImage="true" ></u-upload> :previewFullImage="true"></u-upload> </view> </template> <!-- 正常状态提示 --> <view class="form-container normal-tip" v-if="hasException === false"> <u-icon name="info-circle" size="40" color="#52c41a"></u-icon> <view class="form-container normal-tip" v-if="hasException === false"> <u-icon name="info-circle" size="40" color="#52c41a"></u-icon> <text class="tip-text">设备运行正常,无需上传照片</text> </view> </view> <view class="popup-footer"> <u-button @click="cancel" :customStyle="{ marginRight: '10px' }">取消</u-button> <u-button type="primary" @click="submitForm">保存</u-button> <u-button @click="cancel" :customStyle="{ marginRight: '10px' }">取消</u-button> <u-button type="primary" @click="submitForm">保存</u-button> </view> </view> </u-popup> </template> <script setup> import { ref, computed } from 'vue' import { submitInspectionRecord } from '@/api/equipmentManagement/inspection.js' import { getToken } from '@/utils/auth' import config from '@/config' import { ref, computed } from "vue"; import { submitInspectionRecord } from "@/api/equipmentManagement/inspection.js"; import { getToken } from "@/utils/auth"; import config from "@/config"; const emit = defineEmits(['closeDia']) const emit = defineEmits(["closeDia"]); const dialogVisitable = ref(false) const beforeModelValue = ref([]) const afterModelValue = ref([]) const issueModelValue = ref([]) const infoData = ref(null) const dialogVisitable = ref(false); const beforeModelValue = ref([]); const infoData = ref(null); // 异常状态:null=未选择, false=正常, true=异常 const hasException = ref(null) const hasException = ref(null); // 异常描述 const exceptionDescription = ref('') const exceptionDescription = ref(""); // 计算上传URL const uploadFileUrl = computed(() => { let baseUrl = ''; let baseUrl = ""; if (process.env.VUE_APP_BASE_API) { baseUrl = process.env.VUE_APP_BASE_API; @@ -141,31 +104,31 @@ baseUrl = config.baseUrl; } return baseUrl + '/file/upload'; }) return baseUrl + "/file/upload"; }); const uploadSingleFile = async (fileItem, typeValue) => { const token = getToken() if (!token) throw new Error('用户未登录') const token = getToken(); if (!token) throw new Error("用户未登录"); // H5: u-upload 可能给原生 File(fileItem.file) const rawFile = fileItem?.file const rawFile = fileItem?.file; if (rawFile) { const formData = new FormData() formData.append('file', rawFile, rawFile.name || 'image.jpg') formData.append('type', String(typeValue)) const formData = new FormData(); formData.append("file", rawFile, rawFile.name || "image.jpg"); formData.append("type", String(typeValue)); const res = await fetch(uploadFileUrl.value, { method: 'POST', headers: { Authorization: 'Bearer ' + token }, body: formData }) const data = await res.json() if (data?.code !== 200) throw new Error(data?.msg || '上传失败') method: "POST", headers: { Authorization: "Bearer " + token }, body: formData, }); const data = await res.json(); if (data?.code !== 200) throw new Error(data?.msg || "上传失败"); return { url: data?.data?.url, name: rawFile.name || 'image.jpg', status: 'success' } name: rawFile.name || "image.jpg", status: "success", }; } // 非 H5 / 兼容:走 uni.uploadFile @@ -173,84 +136,66 @@ uni.uploadFile({ url: uploadFileUrl.value, filePath: fileItem.url, name: 'file', name: "file", header: { 'Authorization': `Bearer ${token}` Authorization: `Bearer ${token}`, }, formData: { type: typeValue type: typeValue, }, success: (res) => { success: res => { try { const data = JSON.parse(res.data) const data = JSON.parse(res.data); if (data.code === 200) { resolve({ url: data.data.url, name: fileItem.name, status: 'success' }) status: "success", }); } else { reject(new Error(data.msg || '上传失败')) reject(new Error(data.msg || "上传失败")); } } catch (e) { reject(e) reject(e); } }, fail: (err) => reject(err) }) }) } fail: err => reject(err), }); }); }; // 文件上传处理 const afterRead = (event) => { const { name, file } = event const afterRead = event => { const { file } = event; // 根据上传类型设置不同的type值 let typeValue = 10 // 默认值 if (name === 'before') { typeValue = 10 // 生产前 } else if (name === 'after') { typeValue = 11 // 生产中 } else if (name === 'issue') { typeValue = 12 // 生产后 } // 仅保留生产前(typeValue=10) let typeValue = 10; const files = Array.isArray(file) ? file : [file] Promise.resolve().then(async () => { const files = Array.isArray(file) ? file : [file]; Promise.resolve() .then(async () => { for (const f of files) { const uploaded = await uploadSingleFile(f, typeValue) if (name === 'before') { beforeModelValue.value.push(uploaded) } else if (name === 'after') { afterModelValue.value.push(uploaded) } else if (name === 'issue') { issueModelValue.value.push(uploaded) const uploaded = await uploadSingleFile(f, typeValue); beforeModelValue.value.push(uploaded); } } uni.showToast({ title: '上传成功', icon: 'success' }) }).catch((err) => { console.error('上传失败:', err) uni.showToast({ title: '上传失败', icon: 'error' }) uni.showToast({ title: "上传成功", icon: "success" }); }) } .catch(err => { console.error("上传失败:", err); uni.showToast({ title: "上传失败", icon: "error" }); }); }; // 删除文件 const deleteFile = (event) => { const { name, index } = event if (name === 'before') { beforeModelValue.value.splice(index, 1) } else if (name === 'after') { afterModelValue.value.splice(index, 1) } else if (name === 'issue') { issueModelValue.value.splice(index, 1) } } const deleteFile = event => { const { index } = event; beforeModelValue.value.splice(index, 1); }; // 设置异常状态 const setExceptionStatus = (status) => { hasException.value = status } const setExceptionStatus = status => { hasException.value = status; }; // 提交表单 const submitForm = async () => { @@ -258,84 +203,77 @@ // 检查是否选择了巡检状态 if (hasException.value === null) { uni.showToast({ title: '请选择巡检状态', icon: 'none' }) return title: "请选择巡检状态", icon: "none", }); return; } // 如果是异常状态,检查是否有上传文件 if (hasException.value === true) { const totalFiles = beforeModelValue.value.length + afterModelValue.value.length + issueModelValue.value.length if (totalFiles === 0) { if (beforeModelValue.value.length === 0) { uni.showToast({ title: '请上传异常照片', icon: 'none' }) return title: "请上传异常照片", icon: "none", }); return; } // 检查是否填写了异常描述 if (!exceptionDescription.value.trim()) { uni.showToast({ title: '请填写异常描述', icon: 'none' }) return title: "请填写异常描述", icon: "none", }); return; } } let arr = [] let arr = []; if (beforeModelValue.value.length > 0) { arr.push(...beforeModelValue.value.map(item => ({ ...item, statusType: 0 }))) } if (afterModelValue.value.length > 0) { arr.push(...afterModelValue.value.map(item => ({ ...item, statusType: 1 }))) } if (issueModelValue.value.length > 0) { arr.push(...issueModelValue.value.map(item => ({ ...item, statusType: 2 }))) arr.push( ...beforeModelValue.value.map(item => ({ ...item, statusType: 0 })) ); } // 提交数据 infoData.value.storageBlobDTO = arr infoData.value.hasException = hasException.value infoData.value.exceptionDescription = exceptionDescription.value await submitInspectionRecord({ ...infoData.value }) infoData.value.storageBlobDTO = arr; infoData.value.hasException = hasException.value; infoData.value.exceptionDescription = exceptionDescription.value; await submitInspectionRecord({ ...infoData.value }); uni.showToast({ title: '提交成功', icon: 'success' }) title: "提交成功", icon: "success", }); cancel() cancel(); } catch (error) { console.error('提交失败:', error) console.error("提交失败:", error); uni.showToast({ title: '提交失败', icon: 'error' }) title: "提交失败", icon: "error", }); } } }; // 打开弹框 const openDialog = async (row) => { infoData.value = row dialogVisitable.value = true const openDialog = async row => { infoData.value = row; dialogVisitable.value = true; // 清空之前的数据 beforeModelValue.value = [] afterModelValue.value = [] issueModelValue.value = [] hasException.value = null exceptionDescription.value = '' } beforeModelValue.value = []; hasException.value = null; exceptionDescription.value = ""; }; // 关闭弹框 const cancel = () => { dialogVisitable.value = false emit('closeDia') } dialogVisitable.value = false; emit("closeDia"); }; defineExpose({ openDialog }) defineExpose({ openDialog }); </script> <style scoped lang="scss"> src/pages/inspectionUpload/index.vue
@@ -296,18 +296,22 @@ }; const getFileStatus = record => { let _beforeProduction = record.beforeProduction && record.beforeProduction.length; let _afterProduction = record.afterProduction && record.afterProduction.length; let _productionIssues = record.productionIssues && record.productionIssues.length; if (_beforeProduction && _afterProduction && _productionIssues) { return 2; } else if (_beforeProduction || _afterProduction || _productionIssues) { return 1; // 检查是否有巡检照片 (commonFileListBeforeVO) const hasFiles = (record.commonFileListBeforeVO && record.commonFileListBeforeVO.length > 0) || (record.commonFileListAfterVO && record.commonFileListAfterVO.length > 0) || (record.commonFileListVO && record.commonFileListVO.length > 0); if (hasFiles) { return 2; // 已完成 } else if ( record.inspectionResult !== undefined && record.inspectionResult !== null ) { return 1; // 巡检中 (已有结果但没照片,或者根据业务逻辑定义) } else { return 0; return 0; // 未巡检 } }; @@ -400,7 +404,12 @@ // 查看附件 - 跳转到附件页面 const viewAttachments = async task => { const taskData = encodeURIComponent(JSON.stringify(task)); // 仅传递必要的任务信息和 commonFileListBeforeVO 附件列表 const taskInfoToPass = { taskName: task.taskName, commonFileListBeforeVO: task.commonFileListBeforeVO || [], }; const taskData = encodeURIComponent(JSON.stringify(taskInfoToPass)); uni.navigateTo({ url: `/pages/inspectionUpload/attachment?taskInfo=${taskData}`, }); src/pages/inspectionUpload/upload.vue
@@ -57,33 +57,17 @@ maxlength="500" placeholder="请描述异常情况..." /> </view> <!-- 分类标签页(仅在异常时显示) --> <!-- 上传区域(仅在异常时显示) --> <view class="section-card" v-if="hasException === true"> <view class="upload-tabs"> <view class="tab-item" :class="{ active: currentUploadType === 'before' }" @click="switchUploadType('before')"> 生产前 </view> <view class="tab-item" :class="{ active: currentUploadType === 'after' }" @click="switchUploadType('after')"> 生产中 </view> <view class="tab-item" :class="{ active: currentUploadType === 'issue' }" @click="switchUploadType('issue')"> 生产后 </view> </view> <view class="section-title">巡检照片/视频</view> <!-- 当前分类的上传区域 --> <view class="upload-area"> <view class="upload-buttons"> <u-button type="primary" @click="chooseMedia('image')" :loading="uploading" :disabled="getCurrentFiles().length >= uploadConfig.limit" :disabled="beforeModelValue.length >= uploadConfig.limit" :customStyle="{ marginRight: '10px', flex: 1 }"> <u-icon name="camera" size="18" @@ -94,7 +78,7 @@ <u-button type="success" @click="chooseMedia('video')" :loading="uploading" :disabled="getCurrentFiles().length >= uploadConfig.limit" :disabled="beforeModelValue.length >= uploadConfig.limit" :customStyle="{ flex: 1 }"> <uni-icons type="videocam" size="18" @@ -111,9 +95,9 @@ activeColor="#409eff"></u-line-progress> </view> <!-- 当前分类的文件列表 --> <view v-if="getCurrentFiles().length > 0" <view v-if="beforeModelValue.length > 0" class="file-list"> <view v-for="(file, index) in getCurrentFiles()" <view v-for="(file, index) in beforeModelValue" :key="index" class="file-item"> <view class="file-preview-container"> @@ -143,17 +127,15 @@ </view> </view> </view> <view v-if="getCurrentFiles().length === 0" <view v-if="beforeModelValue.length === 0" class="empty-state"> <text>请选择要上传的{{ getUploadTypeText() }}图片或视频</text> <text>请选择要上传的图片或视频</text> </view> </view> <!-- 统计信息 --> <view class="upload-summary"> <text class="summary-text"> 生产前: {{ beforeModelValue.length }}个文件 | 生产中: {{ afterModelValue.length }}个文件 | 生产后: {{ issueModelValue.length }}个文件 已上传: {{ beforeModelValue.length }}个文件 </text> </view> </view> @@ -198,12 +180,7 @@ const uploadProgress = ref(0); // 三个分类的上传状态 const beforeModelValue = ref([]); // 生产前 const afterModelValue = ref([]); // 生产中 const issueModelValue = ref([]); // 生产后 // 当前激活的上传类型 const currentUploadType = ref("before"); // 'before', 'after', 'issue' const beforeModelValue = ref([]); // 巡检照片 // 异常状态 const hasException = ref(null); // null: 未选择, true: 存在异常, false: 正常 @@ -258,24 +235,25 @@ }); }; // 根据用户要求映射:AfterDTO(生产前), DTO(生产中), BeforeDTO(生产后) // 根据用户要求映射:仅保留生产前 if ( info.commonFileListAfterVO && Array.isArray(info.commonFileListAfterVO) ) { beforeModelValue.value = mapFiles(info.commonFileListAfterVO); } console.log(beforeModelValue.value, "beforeModelValue"); if (info.commonFileListVO && Array.isArray(info.commonFileListVO)) { afterModelValue.value = mapFiles(info.commonFileListVO); } if ( } else if ( info.commonFileListVO && Array.isArray(info.commonFileListVO) ) { beforeModelValue.value = mapFiles(info.commonFileListVO); } else if ( info.commonFileListBeforeVO && Array.isArray(info.commonFileListBeforeVO) ) { issueModelValue.value = mapFiles(info.commonFileListBeforeVO); beforeModelValue.value = mapFiles(info.commonFileListBeforeVO); } console.log(beforeModelValue.value, "beforeModelValue"); // 如果有异常描述,也恢复 if (info.abnormalDescription) { @@ -293,12 +271,7 @@ } // 自动兜底:如果存在已上传文件,则必然是异常状态,确保 UI 正常显示 if ( !hasException.value && (beforeModelValue.value.length > 0 || afterModelValue.value.length > 0 || issueModelValue.value.length > 0) ) { if (!hasException.value && beforeModelValue.value.length > 0) { hasException.value = true; } } catch (e) { @@ -310,39 +283,6 @@ // 返回上一页 const goBack = () => { uni.navigateBack(); }; // 切换上传类型 const switchUploadType = type => { currentUploadType.value = type; }; // 获取当前分类的文件列表 const getCurrentFiles = () => { switch (currentUploadType.value) { case "before": return beforeModelValue.value || []; case "after": return afterModelValue.value || []; case "issue": return issueModelValue.value || []; default: return []; } }; // 获取上传类型文本 const getUploadTypeText = () => { switch (currentUploadType.value) { case "before": return "生产前"; case "after": return "生产中"; case "issue": return "生产后"; default: return ""; } }; // 设置异常状态 @@ -361,12 +301,8 @@ hasException: hasException.value, inspectionResult: hasException.value ? 0 : 1, // 0-异常,1-正常 commonFileListAfterDTO: beforeModelValue.value, commonFileListDTO: afterModelValue.value, commonFileListBeforeDTO: issueModelValue.value, uploadedFiles: { before: beforeModelValue.value, after: afterModelValue.value, issue: issueModelValue.value, }, }; @@ -398,11 +334,7 @@ // 如果是异常状态,检查是否有上传文件和描述 if (hasException.value === true) { const totalFiles = beforeModelValue.value.length + afterModelValue.value.length + issueModelValue.value.length; if (totalFiles === 0) { if (beforeModelValue.value.length === 0) { uni.showToast({ title: "请上传异常照片", icon: "none", @@ -426,11 +358,7 @@ }); // 按照逻辑合并所有分类的文件用于提取ID const allFiles = [ ...beforeModelValue.value, ...afterModelValue.value, ...issueModelValue.value, ]; const allFiles = [...beforeModelValue.value]; // 传给后端的临时文件ID列表 let tempFileIds = []; @@ -444,8 +372,6 @@ const submitData = { ...taskInfo.value, commonFileListAfterDTO: beforeModelValue.value, // 生产前 commonFileListDTO: afterModelValue.value, // 生产中 commonFileListBeforeDTO: issueModelValue.value, // 生产后 hasException: hasException.value, inspectionResult: hasException.value ? 0 : 1, // 0-异常,1-正常 abnormalDescription: abnormalDescription.value, @@ -498,7 +424,7 @@ // 拍照/拍视频 const chooseMedia = type => { if (getCurrentFiles().length >= uploadConfig.limit) { if (beforeModelValue.value.length >= uploadConfig.limit) { uni.showToast({ title: `最多只能选择${uploadConfig.limit}个文件`, icon: "none", @@ -506,7 +432,7 @@ return; } const remaining = uploadConfig.limit - getCurrentFiles().length; const remaining = uploadConfig.limit - beforeModelValue.value.length; // 优先使用 chooseMedia if (typeof uni.chooseMedia === "function") { @@ -612,7 +538,7 @@ Authorization: `Bearer ${token}`, }, formData: { type: getTabType(), type: 10, }, success: res => { try { @@ -638,14 +564,8 @@ status: "success", }; // 根据当前类型添加到对应数组 if (currentUploadType.value === "before") { // 仅添加到 beforeModelValue beforeModelValue.value.push(uploadedFile); } else if (currentUploadType.value === "after") { afterModelValue.value.push(uploadedFile); } else if (currentUploadType.value === "issue") { issueModelValue.value.push(uploadedFile); } uni.showToast({ title: "上传成功", icon: "success" }); } else { @@ -670,24 +590,9 @@ }); }; // 获取type值 const getTabType = () => { switch (currentUploadType.value) { case "before": return 10; case "after": return 11; case "issue": return 12; default: return 10; } }; // 删除文件 const removeFile = index => { const files = getCurrentFiles(); files.splice(index, 1); beforeModelValue.value.splice(index, 1); }; </script>