From 4bc74ff159623d04e4bf483e2be30773ee03c77f Mon Sep 17 00:00:00 2001 From: = <=> Date: Mon, 29 Jun 2026 14:33:31 +0800 Subject: [PATCH] commit --- .../SysH5ScanCodeUploadFileController.java | 46 +++++- .../components/plugins/sysUpload/index.vue | 57 +++++-- .../views/platform/sys/h5upload.html | 154 +++++++++++++++--- 3 files changed, 221 insertions(+), 36 deletions(-) diff --git a/src/main/java/com/budwk/app/sys/controller/SysH5ScanCodeUploadFileController.java b/src/main/java/com/budwk/app/sys/controller/SysH5ScanCodeUploadFileController.java index 99c44b1..02273a5 100644 --- a/src/main/java/com/budwk/app/sys/controller/SysH5ScanCodeUploadFileController.java +++ b/src/main/java/com/budwk/app/sys/controller/SysH5ScanCodeUploadFileController.java @@ -2,7 +2,6 @@ package com.budwk.app.sys.controller; import cn.dev33.satoken.annotation.SaCheckLogin; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.json.JSONObject; import com.budwk.app.base.annotation.SLog; import com.budwk.app.base.constant.RedisConstant; import com.budwk.app.base.result.Result; @@ -10,6 +9,7 @@ import com.budwk.app.sys.enums.SysFileEngineTypeEnum; import com.budwk.app.sys.services.SysFileService; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; import org.nutz.integration.jedis.RedisService; import org.nutz.integration.jedis.pubsub.PubSubService; import org.nutz.ioc.loader.annotation.Inject; @@ -22,10 +22,13 @@ import org.nutz.mvc.upload.UploadAdaptor; import redis.clients.jedis.ScanParams; import redis.clients.jedis.ScanResult; +import java.util.List; + @IocBean @At("/platform/sys/h5ScanCodeUploadFile") @Ok("json:full") @Api(tags = "h5扫码上传文件") +@Slf4j public class SysH5ScanCodeUploadFileController { @Inject @@ -55,6 +58,8 @@ public class SysH5ScanCodeUploadFileController { String url = sysFileService.uploadReturnUrl(SysFileEngineTypeEnum.MINIO.getValue(), file); return Result.success().addData(url); } catch (Exception e) { + log.error("h5扫码上传文件失败,loginName={}, fileName={}, fileSize={}", + getCurrentLoginName(), getUploadFileName(file), getUploadFileSize(file), e); return Result.error(e.getMessage()); } } @@ -63,26 +68,59 @@ public class SysH5ScanCodeUploadFileController { @At @SLog(tag = "文件管理", msg = "同步到PC") @SaCheckLogin - public Result syncPc(@Param("files") JSONObject[] jsonObject, @Param("timestamp") String timestamp) { + public Result syncPc(@Param("files") String files, @Param("timestamp") String timestamp) { try { - if(ObjectUtil.isEmpty(jsonObject)){ + if(ObjectUtil.isEmpty(files)){ + return Result.error("请选择要同步的文件"); + } + if(ObjectUtil.isEmpty(timestamp)){ + return Result.error("二维码已失效,请刷新电脑端二维码后重试"); + } + + // 手机端以表单字符串提交附件 JSON,后端显式解析,避免参数自动转换失败导致 500。 + List fileList = Json.fromJsonAsList(NutMap.class, files); + if(ObjectUtil.isEmpty(fileList)){ return Result.error("请选择要同步的文件"); } ScanParams match = new ScanParams().match(RedisConstant.REDIS_KEY_WSROOM + SecurityUtil.getUserLoginname() + ":*"); ScanResult scan = null; + int sendCount = 0; do { scan = redisService.scan(scan == null ? ScanParams.SCAN_POINTER_START : scan.getStringCursor(), match); for (String key : scan.getResult()) { - NutMap data = NutMap.NEW().addv("action", "h5-scan-code-upload-file-" + timestamp).addv("files", jsonObject); + NutMap data = NutMap.NEW().addv("action", "h5-scan-code-upload-file-" + timestamp).addv("files", fileList); pubSubService.fire(key, Json.toJson(data)); + sendCount++; } } while (!scan.isCompleteIteration()); + if(sendCount == 0){ + return Result.error("未找到电脑端上传窗口,请保持电脑端二维码页面打开"); + } + return Result.success().addData(null); } catch (Exception e) { + log.error("h5扫码上传文件同步到PC失败,loginName={}, timestamp={}, filesLength={}", + getCurrentLoginName(), timestamp, ObjectUtil.isEmpty(files) ? 0 : files.length(), e); return Result.error(e.getMessage()); } } + private String getCurrentLoginName() { + try { + return SecurityUtil.getUserLoginname(); + } catch (Exception e) { + return "unknown"; + } + } + + private String getUploadFileName(TempFile file) { + return file == null ? "" : file.getSubmittedFileName(); + } + + private long getUploadFileSize(TempFile file) { + return file == null ? 0 : file.getSize(); + } + } diff --git a/src/main/resources/static/components/plugins/sysUpload/index.vue b/src/main/resources/static/components/plugins/sysUpload/index.vue index 370d6cf..a35e1bc 100644 --- a/src/main/resources/static/components/plugins/sysUpload/index.vue +++ b/src/main/resources/static/components/plugins/sysUpload/index.vue @@ -6,6 +6,7 @@ :action="action" :on-change="handleChange" :on-success="handleSuccess" + :on-error="handleError" :on-preview="handlePreview" :on-exceed="handleExceed" :before-upload="beforeUpload" @@ -26,6 +27,7 @@ :action="action" :on-change="handleChange" :on-success="handleSuccess" + :on-error="handleError" :on-preview="handlePreview" :on-remove="handleRemove" :before-upload="beforeUpload" @@ -67,6 +69,7 @@ :action="action" :on-change="handleChange" :on-success="handleSuccess" + :on-error="handleError" :on-preview="handlePreview" :on-remove="handleRemove" :on-exceed="handleExceed" @@ -205,15 +208,15 @@ module.exports = { let tips = null if (uploadNumber) { - tips = `只能上传${uploadNumber}个文件;` + tips = '只能上传' + uploadNumber + '个文件;' } if (fileAccept) { - tips = tips + `只能上传${fileAccept}文件;` + tips = tips + '只能上传' + fileAccept + '文件;' } else { tips = tips + '不限格式;' } if (fileSize) { - tips = tips + `单个文件大小不能超过${(fileSize / 1024 / 1024).toFixed(2)}M;` + tips = tips + '单个文件大小不能超过' + (fileSize / 1024 / 1024).toFixed(2) + 'M;' } return tips }, @@ -341,7 +344,7 @@ module.exports = { if (valid) { return true } - this.$message.error(`文件只能是 ${this.fileAccept} 格式!`) + this.$message.error("文件只能是 " + this.fileAccept + " 格式!") return false }, @@ -441,15 +444,15 @@ module.exports = { let result = [] if (response && response.code === 0 && file.status === "success") { fileList.forEach((f) => { - if(f.response.data?.includes('webvpn')) { + if(f.response && f.response.data && f.response.data.includes('webvpn')) { const index = f.response.data.indexOf('/platform') - f.response.data = f.response.data.substring(index) + this.$set(f.response, "data", f.response.data.substring(index)) } result.push(f) }) } else { - this.$message.error(response.msg) - fileList = fileList.splice(file, 1) + this.$message.error(response && response.msg ? response.msg : "文件上传失败,请稍后重试") + fileList = this.filterFileList(fileList, file) } if (result.length > 0) { if (this.upload_result_category === "interval") { @@ -484,6 +487,40 @@ module.exports = { this.$emit("update:value", fileList) }, + handleError(error, file, fileList) { + this.$message.error(this.getUploadErrorMessage(error)) + this.$emit("update:value", this.filterFileList(fileList, file)) + }, + + filterFileList(fileList, file) { + if (!Array.isArray(fileList)) { + return [] + } + return fileList.filter((item) => { + return !file || item.uid !== file.uid + }) + }, + + getUploadErrorMessage(error) { + if (error && error.response && error.response.data && error.response.data.msg) { + return error.response.data.msg + } + if (error && error.status) { + return "文件上传失败,服务器返回" + error.status + } + return "文件上传失败,请检查文件后重试" + }, + + buildQrCodeAddress(timestamp) { + const currentLength = Array.isArray(this.fileList) ? this.fileList.length : 0 + const remainNumber = this.upload_number ? Math.max(this.upload_number - currentLength, 0) : 0 + let address = origin + "/platform/sys/h5ScanCodeUploadFile?timestamp=" + encodeURIComponent(timestamp) + address = address + "&upload_number=" + encodeURIComponent(remainNumber) + address = address + "&upload_size=" + encodeURIComponent(this.upload_size || "") + address = address + "&accept=" + encodeURIComponent(this.fileAccept || "") + return address + }, + // 通过DOM获取上传的文件 uploadFileList() { if (this.fileList) { @@ -546,7 +583,7 @@ module.exports = { mounted() { const timestamp = new Date().getTime() - this.qrCodeAddress = origin + "/platform/sys/h5ScanCodeUploadFile?timestamp=" + timestamp + this.$set(this, "qrCodeAddress", this.buildQrCodeAddress(timestamp)) console.info("qrCodeAddress:", this.qrCodeAddress) // 可选:轮询或 watch ready 状态 const checkReady = () => { @@ -588,7 +625,7 @@ module.exports = { if (validFiles.length < data.files.length) { data.files = validFiles - this.$message.error(`文件只能是 ${this.fileAccept} 格式,已自动过滤掉不符合要求文件!`) + this.$message.error("文件只能是 " + this.fileAccept + " 格式,已自动过滤掉不符合要求文件!") } } diff --git a/src/main/resources/views/platform/sys/h5upload.html b/src/main/resources/views/platform/sys/h5upload.html index 17b2fa9..0d4be91 100644 --- a/src/main/resources/views/platform/sys/h5upload.html +++ b/src/main/resources/views/platform/sys/h5upload.html @@ -44,6 +44,13 @@ text-align: center; } + .upload-tip { + margin-top: 12px; + color: #666; + font-size: 13px; + line-height: 1.6; + } + .van-uploader__tip { color: var(--color-primary); font-size: 14px; @@ -78,8 +85,9 @@ >
- 同步到电脑 + 同步到电脑
+
{{ uploadTips }}