| | |
| | | count |
| | | maxlength="200" /> |
| | | </u-form-item> |
| | | <view class="simple-upload-area"> |
| | | <view class="upload-buttons"> |
| | | <u-button type="primary" |
| | | @click="chooseRepairMedia" |
| | | :loading="uploading" |
| | | :disabled="repairFileList.length >= uploadConfig.limit" |
| | | :customStyle="{ marginRight: '10px', flex: 1 }"> |
| | | <u-icon name="camera" |
| | | size="18" |
| | | color="#fff" |
| | | style="margin-right: 5px;"></u-icon> |
| | | {{ uploading ? "上传中..." : "拍照" }} |
| | | </u-button> |
| | | </view> |
| | | <view v-if="uploading" |
| | | class="upload-progress"> |
| | | <u-line-progress :percentage="uploadProgress" |
| | | :showText="true" |
| | | activeColor="#409eff"></u-line-progress> |
| | | </view> |
| | | <view v-if="repairFileList.length > 0" |
| | | class="file-list"> |
| | | <view v-for="(file, index) in repairFileList" |
| | | :key="index" |
| | | class="file-item"> |
| | | <view class="file-preview-container"> |
| | | <view class="delete-btn" |
| | | @click="removeRepairFile(index)"> |
| | | <u-icon name="close" |
| | | size="12" |
| | | color="#fff"></u-icon> |
| | | </view> |
| | | <image :src="file.url || file.tempFilePath || file.path || file.downloadUrl" |
| | | class="file-preview" |
| | | mode="aspectFill" /> |
| | | </view> |
| | | <view class="file-info"> |
| | | <text class="file-name">{{ file.bucketFilename || file.name || "图片" }}</text> |
| | | <text class="file-size">{{ formatFileSize(file.size) }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-if="repairFileList.length === 0" |
| | | class="empty-state"> |
| | | <text>请选择要上传的现场照片</text> |
| | | </view> |
| | | </view> |
| | | </u-cell-group> |
| | | <!-- 提交按钮 --> |
| | | <view class="footer-btns"> |
| | |
| | | editRepair, |
| | | getRepairById, |
| | | } from "@/api/equipmentManagement/repair"; |
| | | import config from "@/config"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import dayjs from "dayjs"; |
| | | import { formatDateToYMD } from "@/utils/ruoyi"; |
| | | const showToast = message => { |
| | |
| | | const loading = ref(false); |
| | | const showDate = ref(false); |
| | | const pickerDateValue = ref(Date.now()); |
| | | |
| | | // 上传配置(与巡检一致) |
| | | const uploadConfig = { |
| | | action: "/file/upload", |
| | | limit: 10, |
| | | }; |
| | | const uploadFileUrl = computed(() => config.baseUrl + uploadConfig.action); |
| | | const repairFileList = ref([]); |
| | | const uploading = ref(false); |
| | | const uploadProgress = ref(0); |
| | | |
| | | // 设备选项 |
| | | const deviceOptions = ref([]); |
| | |
| | | if (device) { |
| | | deviceNameText.value = device.deviceName; |
| | | } |
| | | // 回显附件列表(后端返回 fileList 时) |
| | | const list = data.fileList || data.commonFileList || []; |
| | | repairFileList.value = (Array.isArray(list) ? list : []).map(f => ({ |
| | | url: f.url || f.downloadUrl, |
| | | name: f.bucketFilename || f.originalFilename || f.name, |
| | | bucketFilename: f.bucketFilename || f.originalFilename || f.name, |
| | | size: f.size || f.byteSize, |
| | | uploadResponse: f, |
| | | })); |
| | | } |
| | | } catch (e) { |
| | | showToast("获取详情失败"); |
| | |
| | | form.value.deviceLedgerId = item.id; |
| | | setDeviceModel(item.id); |
| | | } |
| | | }; |
| | | |
| | | // 格式化文件大小 |
| | | 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 chooseRepairMedia = () => { |
| | | if (repairFileList.value.length >= uploadConfig.limit) { |
| | | showToast(`最多只能选择${uploadConfig.limit}个文件`); |
| | | return; |
| | | } |
| | | const remaining = uploadConfig.limit - repairFileList.value.length; |
| | | if (typeof uni.chooseMedia === "function") { |
| | | uni.chooseMedia({ |
| | | count: Math.min(remaining, 9), |
| | | mediaType: ["image"], |
| | | sizeType: ["compressed", "original"], |
| | | sourceType: ["camera", "album"], |
| | | success: res => { |
| | | const files = res?.tempFiles || []; |
| | | files.forEach((tf, idx) => { |
| | | const filePath = tf.tempFilePath || tf.path || ""; |
| | | const file = { |
| | | tempFilePath: filePath, |
| | | path: filePath, |
| | | type: "image", |
| | | name: `repair_${Date.now()}_${idx}.jpg`, |
| | | size: tf.size || 0, |
| | | uid: Date.now() + Math.random() + idx, |
| | | }; |
| | | uploadRepairFile(file); |
| | | }); |
| | | }, |
| | | fail: () => showToast("选择图片失败"), |
| | | }); |
| | | } else { |
| | | uni.chooseImage({ |
| | | count: remaining, |
| | | sizeType: ["compressed", "original"], |
| | | sourceType: ["camera", "album"], |
| | | success: res => { |
| | | (res.tempFilePaths || []).forEach((path, idx) => { |
| | | uploadRepairFile({ |
| | | tempFilePath: path, |
| | | path: path, |
| | | type: "image", |
| | | name: `repair_${Date.now()}_${idx}.jpg`, |
| | | size: 0, |
| | | uid: Date.now() + Math.random(), |
| | | }); |
| | | }); |
| | | }, |
| | | fail: () => showToast("选择图片失败"), |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const uploadRepairFile = file => { |
| | | const filePath = file.tempFilePath || file.path; |
| | | if (!filePath) return; |
| | | uploading.value = true; |
| | | uploadProgress.value = 0; |
| | | const token = getToken(); |
| | | if (!token) { |
| | | showToast("请先登录"); |
| | | uploading.value = false; |
| | | return; |
| | | } |
| | | const uploadTask = uni.uploadFile({ |
| | | url: uploadFileUrl.value, |
| | | filePath, |
| | | name: "file", |
| | | header: { Authorization: "Bearer " + token }, |
| | | success: res => { |
| | | try { |
| | | if (res.statusCode === 200) { |
| | | const response = typeof res.data === "string" ? JSON.parse(res.data) : res.data; |
| | | const d = response?.data !== undefined ? response.data : response; |
| | | if (response && (response.code === 200 || response.code === 0) && d) { |
| | | const fileData = { |
| | | ...file, |
| | | id: d.id, |
| | | tempId: d.tempId ?? d.tempFileId ?? d.id, |
| | | url: d.url || d.downloadUrl || filePath, |
| | | bucketFilename: d.bucketFilename || d.originalFilename || file.name, |
| | | downloadUrl: d.downloadUrl || d.url, |
| | | size: d.size ?? d.byteSize ?? file.size, |
| | | uploadResponse: response, |
| | | }; |
| | | repairFileList.value.push(fileData); |
| | | } else { |
| | | showToast(response?.msg || "上传失败"); |
| | | } |
| | | } else { |
| | | showToast("上传失败"); |
| | | } |
| | | } catch (e) { |
| | | showToast("上传失败"); |
| | | } |
| | | uploading.value = false; |
| | | uploadProgress.value = 0; |
| | | }, |
| | | fail: () => { |
| | | showToast("上传失败"); |
| | | uploading.value = false; |
| | | uploadProgress.value = 0; |
| | | }, |
| | | }); |
| | | if (uploadTask && uploadTask.onProgressUpdate) { |
| | | uploadTask.onProgressUpdate(e => { |
| | | uploadProgress.value = e.progress; |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const removeRepairFile = index => { |
| | | repairFileList.value.splice(index, 1); |
| | | }; |
| | | |
| | | // 显示日期选择器 |
| | |
| | | loading.value = true; |
| | | const id = getPageId(); |
| | | |
| | | // 准备提交数据 |
| | | // 附件数组:上传接口返回的附件信息(与巡检一致传 fileList) |
| | | const fileList = repairFileList.value.map(f => { |
| | | const d = f.uploadResponse?.data !== undefined ? f.uploadResponse.data : f.uploadResponse; |
| | | return d ? { ...d } : null; |
| | | }).filter(Boolean); |
| | | const submitData = { ...form.value }; |
| | | if (fileList.length) submitData.fileList = fileList; |
| | | |
| | | const { code } = id |
| | | ? await editRepair({ id: id, ...submitData }) |
| | |
| | | .picker-input-text.placeholder { |
| | | color: #c0c4cc; |
| | | } |
| | | |
| | | .simple-upload-area { |
| | | padding: 15px 20px; |
| | | } |
| | | |
| | | .upload-buttons { |
| | | display: flex; |
| | | gap: 10px; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .upload-progress { |
| | | margin: 15px 0; |
| | | padding: 0 10px; |
| | | } |
| | | |
| | | .file-list { |
| | | margin-top: 15px; |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .file-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 8px; |
| | | border: 1px solid #e9ecef; |
| | | width: calc(50% - 6px); |
| | | min-width: 120px; |
| | | } |
| | | |
| | | .file-preview-container { |
| | | position: relative; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .file-preview { |
| | | width: 80px; |
| | | height: 80px; |
| | | border-radius: 8px; |
| | | object-fit: cover; |
| | | border: 2px solid #f0f0f0; |
| | | } |
| | | |
| | | .delete-btn { |
| | | position: absolute; |
| | | top: -6px; |
| | | right: -6px; |
| | | width: 20px; |
| | | height: 20px; |
| | | background: #ff4757; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | z-index: 1; |
| | | } |
| | | |
| | | .file-info { |
| | | text-align: center; |
| | | width: 100%; |
| | | } |
| | | |
| | | .file-name { |
| | | font-size: 12px; |
| | | color: #333; |
| | | display: block; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | max-width: 100px; |
| | | } |
| | | |
| | | .file-size { |
| | | font-size: 10px; |
| | | color: #999; |
| | | margin-top: 2px; |
| | | display: block; |
| | | } |
| | | |
| | | .empty-state { |
| | | text-align: center; |
| | | padding: 24px 16px; |
| | | color: #999; |
| | | font-size: 14px; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | border: 2px dashed #ddd; |
| | | } |
| | | </style> |