| src/pages/inspectionUpload/attachment.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/inspectionUpload/components/formDia.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/inspectionUpload/components/qrCodeFormDia.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/inspectionUpload/upload.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/pages/inspectionUpload/attachment.vue
@@ -5,29 +5,11 @@ @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" <view v-if="attachmentList.length > 0" class="attachment-list"> <view v-for="(file, index) in getCurrentViewAttachments()" <view v-for="(file, index) in attachmentList" :key="index" class="attachment-item" @click="previewAttachment(file)"> @@ -52,7 +34,7 @@ </view> <view v-else class="attachment-empty"> <text class="empty-text">该分类暂无附件</text> <text class="empty-text">暂无附件</text> </view> </view> </view> @@ -96,9 +78,6 @@ // 附件列表 const attachmentList = ref([]); // 当前查看类型 const currentViewType = ref("before"); // 'before', 'after', 'issue' // 视频预览相关状态 const showVideoDialog = ref(false); const currentVideoFile = ref(null); @@ -122,46 +101,18 @@ } }); // 加载附件数据 // 加载附件数据(统一使用 commonFileListVO) const loadAttachments = () => { const task = taskInfo.value; if (!task) return; attachmentList.value = []; // 后端反显字段 (VO优先) const beforeList = Array.isArray(task?.commonFileListBeforeVO) ? task.commonFileListBeforeVO : Array.isArray(task?.commonFileListBefore) ? task.commonFileListBefore : []; const duringList = Array.isArray(task?.commonFileListVO) const fileList = Array.isArray(task?.commonFileListVO) ? task.commonFileListVO : Array.isArray(task?.commonFileListAfter) ? task.commonFileListAfter : []; // 兼容旧逻辑或命名不一致 const afterList = Array.isArray(task?.commonFileListAfterVO) ? task.commonFileListAfterVO : Array.isArray(task?.commonFileListIssue) ? task.commonFileListIssue : []; // 如果 VO 都没有,尝试从 commonFileList 过滤 const allList = Array.isArray(task?.commonFileList) ? task.commonFileList : []; const finalBefore = beforeList.length > 0 ? beforeList : allList.filter(f => f?.type === 10); const finalDuring = duringList.length > 0 ? duringList : allList.filter(f => f?.type === 11); const finalAfter = afterList.length > 0 ? afterList : allList.filter(f => f?.type === 12); const mapToViewFile = (file, viewType) => { // 兼容 previewURL, previewUrl, url, downloadURL, downloadUrl attachmentList.value = fileList.map(file => { const rawUrl = file?.previewURL || file?.previewUrl || @@ -173,20 +124,14 @@ return { ...file, type: viewType, name: file?.originalFilename || file?.bucketFilename || file?.name || "附件", name: file?.originalFilename || file?.bucketFilename || file?.name || "附件", bucketFilename: file?.bucketFilename || file?.name, originalFilename: file?.originalFilename || file?.name, url: u, downloadUrl: u, size: file?.byteSize || file?.size || 0, }; }; attachmentList.value.push(...finalBefore.map(f => mapToViewFile(f, 0))); attachmentList.value.push(...finalDuring.map(f => mapToViewFile(f, 1))); attachmentList.value.push(...finalAfter.map(f => mapToViewFile(f, 2))); }); }; // 将后端返回的文件地址规范成可访问URL @@ -220,30 +165,6 @@ 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/")) { @@ -259,7 +180,7 @@ // 预览附件 const previewAttachment = file => { if (isImageFile(file)) { const imageUrls = getCurrentViewAttachments() const imageUrls = attachmentList.value .filter(f => isImageFile(f)) .map(f => f.url || f.downloadUrl); @@ -309,32 +230,6 @@ .attachment-content { padding: 15px; } /* 标签页样式 */ .attachment-tabs { display: flex; background: #fff; border-radius: 12px; margin-bottom: 15px; padding: 4px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); } .tab-item { flex: 1; text-align: center; padding: 12px 8px; font-size: 14px; color: #666; border-radius: 8px; transition: all 0.3s ease; } .tab-item.active { background: #409eff; color: #fff; font-weight: 500; } /* 附件列表样式 */ src/pages/inspectionUpload/components/formDia.vue
@@ -1,7 +1,7 @@ <template> <u-popup v-model="dialogVisitable" mode="center" <u-popup v-model="dialogVisitable" mode="center" :round="10" :closeable="true" @close="cancel" @@ -10,23 +10,23 @@ <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> <text class="option-text">正常</text> </view> <view class="exception-option" <view class="exception-option" :class="{ active: hasException === true }" @click="setExceptionStatus(true)" > @@ -48,54 +48,23 @@ :customStyle="{ padding: '10px', backgroundColor: '#f5f5f5' }" /> </view> <!-- 上传区域(仅在异常时显示) --> <template v-if="hasException === true"> <view class="form-container"> <view class="title">生产前</view> <u-upload :fileList="beforeModelValue" @afterRead="afterRead" @delete="deleteFile" name="before" 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="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> </view> </template> <view class="form-container" v-if="hasException === true"> <view class="title">上传附件</view> <text class="upload-count">已上传: {{ fileList.length }}个文件</text> <u-upload :fileList="fileList" @afterRead="afterRead" @delete="deleteFile" name="attachment" multiple :maxCount="30" :maxSize="5 * 1024 * 1024" accept="image/*" :previewFullImage="true" ></u-upload> </view> <!-- 正常状态提示 --> <view class="form-container normal-tip" v-if="hasException === false"> @@ -103,7 +72,7 @@ <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> @@ -121,9 +90,7 @@ const emit = defineEmits(['closeDia']) const dialogVisitable = ref(false) const beforeModelValue = ref([]) const afterModelValue = ref([]) const issueModelValue = ref([]) const fileList = ref([]) const infoData = ref(null) // 异常状态:null=未选择, false=正常, true=异常 @@ -134,13 +101,13 @@ // 计算上传URL const uploadFileUrl = computed(() => { let baseUrl = ''; if (process.env.VUE_APP_BASE_API) { baseUrl = process.env.VUE_APP_BASE_API; } else { baseUrl = config.baseUrl; } return baseUrl + '/file/upload'; }) @@ -203,29 +170,12 @@ // 文件上传处理 const afterRead = (event) => { const { name, file } = event // 根据上传类型设置不同的type值 let typeValue = 10 // 默认值 if (name === 'before') { typeValue = 10 // 生产前 } else if (name === 'after') { typeValue = 11 // 生产中 } else if (name === 'issue') { typeValue = 12 // 生产后 } const { file } = event 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, 10) fileList.value.push(uploaded) } uni.showToast({ title: '上传成功', icon: 'success' }) }).catch((err) => { @@ -236,15 +186,8 @@ // 删除文件 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 { index } = event fileList.value.splice(index, 1) } // 设置异常状态 @@ -266,8 +209,7 @@ // 如果是异常状态,检查是否有上传文件 if (hasException.value === true) { const totalFiles = beforeModelValue.value.length + afterModelValue.value.length + issueModelValue.value.length if (totalFiles === 0) { if (fileList.value.length === 0) { uni.showToast({ title: '请上传异常照片', icon: 'none' @@ -284,28 +226,17 @@ } } 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 }))) } // 提交数据 infoData.value.storageBlobDTO = arr infoData.value.commonFileListDTO = fileList.value infoData.value.hasException = hasException.value infoData.value.exceptionDescription = exceptionDescription.value await submitInspectionRecord({ ...infoData.value }) uni.showToast({ title: '提交成功', icon: 'success' }) cancel() } catch (error) { console.error('提交失败:', error) @@ -320,11 +251,9 @@ const openDialog = async (row) => { infoData.value = row dialogVisitable.value = true // 清空之前的数据 beforeModelValue.value = [] afterModelValue.value = [] issueModelValue.value = [] fileList.value = [] hasException.value = null exceptionDescription.value = '' } @@ -367,10 +296,18 @@ .form-container { margin-bottom: 20px; &:last-child { margin-bottom: 0; } } .upload-count { display: block; font-size: 12px; color: #999; margin-bottom: 8px; padding-left: 10px; } .title { @@ -381,7 +318,7 @@ padding-left: 10px; position: relative; margin: 6px 0 10px; &::before { content: ""; position: absolute; @@ -423,12 +360,12 @@ cursor: pointer; transition: all 0.3s; background-color: #fff; &.active { border-color: #1890ff; background-color: #e6f7ff; } &:active { opacity: 0.8; } @@ -450,7 +387,7 @@ background-color: #f6ffed; border: 1px dashed #b7eb8f; border-radius: 8px; .tip-text { margin-top: 15px; font-size: 14px; src/pages/inspectionUpload/components/qrCodeFormDia.vue
@@ -40,16 +40,15 @@ </u-input> </u-form-item> <u-form-item label="附件" prop="storageBlobDTO" labelWidth="80"> <u-form-item label="附件" prop="commonFileListDTO" labelWidth="80"> <view class="upload-container"> <ImageUpload v-model="form.storageBlobDTO" v-model="form.commonFileListDTO" :limit="10" :fileSize="50" :fileType="['jpg', 'jpeg', 'png', 'mp4', 'mov']" :maxVideoDuration="60" :statusType="0" @update:modelValue="handleStorageBlobUpdate" @update:modelValue="handleFileListUpdate" /> </view> </u-form-item> @@ -116,7 +115,7 @@ scannerName: '', scannerId: '', scanTime: '', storageBlobDTO: [], commonFileListDTO: [], qrCode: { id: '' } @@ -144,9 +143,9 @@ } }) // 处理storageBlobDTO数据更新 const handleStorageBlobUpdate = (value) => { form.storageBlobDTO = value || [] // 处理commonFileListDTO数据更新 const handleFileListUpdate = (value) => { form.commonFileListDTO = value || [] } // 获取当前位置 @@ -259,7 +258,7 @@ form.deviceName = row.deviceName || '' form.location = row.location || '' form.qrCodeId = row.qrCodeId form.storageBlobDTO = [] form.commonFileListDTO = [] console.log('弹框表单数据:', form) // 强制更新视图 @@ -308,8 +307,8 @@ scannerName: form.scannerName, scannerId: form.scannerId, scanTime: form.scanTime, storageBlobDTO: form.storageBlobDTO.map(file => ({ id: file.id, // 添加id字段 commonFileListDTO: form.commonFileListDTO.map(file => ({ id: file.id, url: file.url, bucketFilename: file.bucketFilename || file.name, downloadUrl: file.downloadUrl || file.url, src/pages/inspectionUpload/upload.vue
@@ -57,33 +57,16 @@ 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="fileList.length >= uploadConfig.limit" :customStyle="{ marginRight: '10px', flex: 1 }"> <u-icon name="camera" size="18" @@ -94,7 +77,7 @@ <u-button type="success" @click="chooseMedia('video')" :loading="uploading" :disabled="getCurrentFiles().length >= uploadConfig.limit" :disabled="fileList.length >= uploadConfig.limit" :customStyle="{ flex: 1 }"> <uni-icons type="videocam" size="18" @@ -110,10 +93,10 @@ :showText="true" activeColor="#409eff"></u-line-progress> </view> <!-- 当前分类的文件列表 --> <view v-if="getCurrentFiles().length > 0" <!-- 文件列表 --> <view v-if="fileList.length > 0" class="file-list"> <view v-for="(file, index) in getCurrentFiles()" <view v-for="(file, index) in fileList" :key="index" class="file-item"> <view class="file-preview-container"> @@ -129,7 +112,6 @@ style="margin-right: 5px"></uni-icons> <text class="video-text">视频</text> </view> <!-- 删除按钮 --> <view class="delete-btn" @click="removeFile(index)"> <u-icon name="close" @@ -143,18 +125,13 @@ </view> </view> </view> <view v-if="getCurrentFiles().length === 0" <view v-if="fileList.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 }}个文件 </text> <text class="summary-text">已上传: {{ fileList.length }}个文件</text> </view> </view> <!-- 正常状态提示 --> @@ -197,13 +174,8 @@ const uploading = ref(false); const uploadProgress = ref(0); // 三个分类的上传状态 const beforeModelValue = ref([]); // 生产前 const afterModelValue = ref([]); // 生产中 const issueModelValue = ref([]); // 生产后 // 当前激活的上传类型 const currentUploadType = ref("before"); // 'before', 'after', 'issue' // 统一的文件列表 const fileList = ref([]); // 异常状态 const hasException = ref(null); // null: 未选择, true: 存在异常, false: 正常 @@ -232,73 +204,37 @@ const info = JSON.parse(decodeURIComponent(options.taskInfo)); taskInfo.value = info; // 回显逻辑:从 taskInfo 中恢复已上传的文件 const mapFiles = list => { if (!list || !Array.isArray(list)) return []; return list.map(item => { // 处理 URL,去除可能的空格 const finalUrl = (item.url || item.previewURL || "").trim(); // 自动推断文件类型 let fileType = item.type; if (!fileType && item.contentType) { fileType = item.contentType.startsWith("video") ? "video" : "image"; } else if (!fileType) { fileType = "image"; // 默认图片 } // 回显逻辑:从 commonFileListVO 恢复已上传的文件 const fileVOList = Array.isArray(info.commonFileListVO) ? info.commonFileListVO : []; fileList.value = fileVOList.map(item => { const finalUrl = (item.url || item.previewURL || "").trim(); let fileType = item.type; if (!fileType && item.contentType) { fileType = item.contentType.startsWith("video") ? "video" : "image"; } else if (!fileType) { fileType = "image"; } return { ...item, url: finalUrl, name: item.name || item.originalFilename, tempId: item.tempId || item.id || item.tempFileId, size: item.size || item.byteSize || 0, type: fileType, status: "success", }; }); return { ...item, url: finalUrl, name: item.name || item.originalFilename, tempId: item.tempId || item.id || item.tempFileId, size: item.size || item.byteSize || 0, // 映射大小字段 type: fileType, status: "success", }; }); }; // 修正字段映射:BeforeVO(生产前), VO(生产中), AfterVO(生产后) if ( info.commonFileListBeforeVO && Array.isArray(info.commonFileListBeforeVO) ) { beforeModelValue.value = mapFiles(info.commonFileListBeforeVO); } console.log(beforeModelValue.value, "beforeModelValue"); if (info.commonFileListVO && Array.isArray(info.commonFileListVO)) { afterModelValue.value = mapFiles(info.commonFileListVO); } if ( info.commonFileListAfterVO && Array.isArray(info.commonFileListAfterVO) ) { issueModelValue.value = mapFiles(info.commonFileListAfterVO); } // 如果有异常描述,也恢复 if (info.abnormalDescription) { abnormalDescription.value = info.abnormalDescription; } // 如果有异常状态,也恢复 if (info.hasException !== undefined && info.hasException !== null) { hasException.value = info.hasException; } else if ( info.inspectionResult !== undefined && info.inspectionResult !== null ) { // 0-异常,1-正常 } else if (info.inspectionResult !== undefined && info.inspectionResult !== null) { hasException.value = String(info.inspectionResult) === "0"; } // 自动兜底:如果存在已上传文件,则必然是异常状态,确保 UI 正常显示 if ( !hasException.value && (beforeModelValue.value.length > 0 || afterModelValue.value.length > 0 || issueModelValue.value.length > 0) ) { if (!hasException.value && fileList.value.length > 0) { hasException.value = true; } } catch (e) { @@ -310,39 +246,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 ""; } }; // 设置异常状态 @@ -360,14 +263,8 @@ inspector: taskInfo.value?.inspector, hasException: hasException.value, inspectionResult: hasException.value ? 0 : 1, // 0-异常,1-正常 commonFileListBeforeDTO: beforeModelValue.value, commonFileListDTO: afterModelValue.value, commonFileListAfterDTO: issueModelValue.value, uploadedFiles: { before: beforeModelValue.value, after: afterModelValue.value, issue: issueModelValue.value, }, commonFileListDTO: fileList.value, uploadedFiles: fileList.value, }; uni.setStorageSync("repairTaskInfo", JSON.stringify(taskData)); @@ -398,11 +295,7 @@ // 如果是异常状态,检查是否有上传文件和描述 if (hasException.value === true) { const totalFiles = beforeModelValue.value.length + afterModelValue.value.length + issueModelValue.value.length; if (totalFiles === 0) { if (fileList.value.length === 0) { uni.showToast({ title: "请上传异常照片", icon: "none", @@ -425,17 +318,10 @@ mask: true, }); // 按照逻辑合并所有分类的文件用于提取ID const allFiles = [ ...beforeModelValue.value, ...afterModelValue.value, ...issueModelValue.value, ]; // 传给后端的临时文件ID列表 let tempFileIds = []; if (allFiles.length > 0) { tempFileIds = allFiles if (fileList.value.length > 0) { tempFileIds = fileList.value .map(item => item?.tempId ?? item?.tempFileId ?? item?.id) .filter(v => v !== undefined && v !== null && v !== ""); } @@ -443,9 +329,7 @@ // 提交数据 const submitData = { ...taskInfo.value, commonFileListBeforeDTO: beforeModelValue.value, // 生产前 commonFileListDTO: afterModelValue.value, // 生产中 commonFileListAfterDTO: issueModelValue.value, // 生产后 commonFileListDTO: fileList.value, hasException: hasException.value, inspectionResult: hasException.value ? 0 : 1, // 0-异常,1-正常 abnormalDescription: abnormalDescription.value, @@ -498,7 +382,7 @@ // 拍照/拍视频 const chooseMedia = type => { if (getCurrentFiles().length >= uploadConfig.limit) { if (fileList.value.length >= uploadConfig.limit) { uni.showToast({ title: `最多只能选择${uploadConfig.limit}个文件`, icon: "none", @@ -506,7 +390,7 @@ return; } const remaining = uploadConfig.limit - getCurrentFiles().length; const remaining = uploadConfig.limit - fileList.value.length; // 优先使用 chooseMedia if (typeof uni.chooseMedia === "function") { @@ -612,7 +496,7 @@ Authorization: `Bearer ${token}`, }, formData: { type: getTabType(), type: 10, }, success: res => { try { @@ -638,14 +522,7 @@ status: "success", }; // 根据当前类型添加到对应数组 if (currentUploadType.value === "before") { beforeModelValue.value.push(uploadedFile); } else if (currentUploadType.value === "after") { afterModelValue.value.push(uploadedFile); } else if (currentUploadType.value === "issue") { issueModelValue.value.push(uploadedFile); } fileList.value.push(uploadedFile); uni.showToast({ title: "上传成功", icon: "success" }); } else { @@ -670,23 +547,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(); const files = fileList.value; files.splice(index, 1); }; </script> @@ -810,30 +673,6 @@ outline: none; border-color: #409eff; background: #fff; } /* 分类标签页 */ .upload-tabs { display: flex; gap: 10px; margin-bottom: 15px; } .tab-item { flex: 1; padding: 10px; text-align: center; background: #f5f5f5; border-radius: 6px; font-size: 13px; color: #666; cursor: pointer; transition: all 0.3s; } .tab-item.active { background: #409eff; color: #fff; } /* 上传区域 */