| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form |
| | | class="opo-query-form" |
| | | :model="queryParams" |
| | | ref="queryForm" |
| | | :inline="true" |
| | |
| | | size="small" |
| | | style="width: 220px" |
| | | v-model="form.uploadtime" |
| | | type="datetime" |
| | | type="datetime" format="yyyy-MM-dd HH:mm" |
| | | value-format="yyyy-MM-dd HH:mm:ss" |
| | | > |
| | | </el-date-picker> |
| | |
| | | <el-input v-model="form.fileurl" placeholder="请输入文件地址" /> |
| | | </el-form-item> --> |
| | | <el-form-item label="文件上传" align="left" prop="fileurl"> |
| | | <!-- <file-upload ref="fileUpload" :fileType=fileType :limit="1" :on-success="handleUploadSuccess"></file-upload> --> |
| | | <el-upload |
| | | size="mini" |
| | | class="upload-demo" |
| | |
| | | fileurl: null, |
| | | status: null, |
| | | }, |
| | | fileType: ["png", "jpg", "jpeg", "doc", "xls", "ppt", "txt", "pdf"], |
| | | // 表单参数 |
| | | form: {}, |
| | | // 表单校验 |
| | |
| | | //文件列表 |
| | | fileList: [], |
| | | baseUrl: process.env.VUE_APP_BASE_API, |
| | | // 上传的图片服务器地址 |
| | | uploadFileUrl: process.env.VUE_APP_BASE_API + "/common/upload", |
| | | // 政策文件上传接口(2026-10-09:改用模块专属接口,允许任意文件类型; |
| | | // 通用 /common/upload 仍按扩展名白名单校验,不适用于本模块) |
| | | uploadFileUrl: process.env.VUE_APP_BASE_API + "/project/documentfiles/upload", |
| | | //上传附件请求头 |
| | | headers: { |
| | | Authorization: "Bearer " + getToken(), |
| | |
| | | download() { |
| | | let list = this.fileList; |
| | | if (list.length > 0) { |
| | | let downUrl = list[0].url; |
| | | const link = document.createElement("a"); |
| | | link.href = this.baseUrl + downUrl; |
| | | link.setAttribute("download", this.getFileName(list[0].name)); |
| | | document.body.appendChild(link); |
| | | link.click(); |
| | | this.saveFile(list[0].url, this.getFileName(list[0].name, list[0].url)); |
| | | } else { |
| | | this.$modal.msgWarning("未获取到文件地址,请确认"); |
| | | } |
| | | }, |
| | | handleDownload(row) { |
| | | if (row.fileurl != "" && row.fileurl != null) { |
| | | const link = document.createElement("a"); |
| | | link.href = this.baseUrl + row.fileurl; |
| | | link.setAttribute("download", this.getFileName(row.fileurl)); |
| | | document.body.appendChild(link); |
| | | link.click(); |
| | | this.saveFile( |
| | | row.fileurl, |
| | | this.getFileName(row.filename, row.fileurl, row.filename) |
| | | ); |
| | | } else { |
| | | this.$modal.msgWarning("未获取到文件地址,请确认"); |
| | | } |
| | | }, |
| | | // 获取文件名称 |
| | | getFileName(name) { |
| | | if (name.lastIndexOf("/") > -1) { |
| | | return name.slice(name.lastIndexOf("/") + 1).toLowerCase(); |
| | | } else { |
| | | return ""; |
| | | |
| | | /** |
| | | * 下载文件(必须带鉴权)。 |
| | | * |
| | | * 背景:/profile/** 静态资源未在 SecurityConfig 中放行(anyRequest().authenticated()), |
| | | * 而后端 TokenService 只从请求头 Authorization 取 token。 |
| | | * 因此用 <a href> 直接跳转下载不会携带 token,服务端返回 401,表现为“点下载没反应/下不到文件”。 |
| | | * 这里改为 fetch 带 Authorization 头取回文件流,再用 blob 本地另存。 |
| | | * |
| | | * @param {string} fileUrl 文件地址(库中存的是 /profile/upload/... 相对路径) |
| | | * @param {string} fileName 另存为的文件名 |
| | | */ |
| | | saveFile(fileUrl, fileName) { |
| | | if (!fileUrl) { |
| | | this.$modal.msgWarning("未获取到文件地址,请确认"); |
| | | return; |
| | | } |
| | | // 兼容历史数据中已是绝对地址的情况,避免重复拼接 |
| | | const isAbsolute = /^https?:\/\//i.test(fileUrl); |
| | | const url = isAbsolute ? fileUrl : this.baseUrl + fileUrl; |
| | | |
| | | this.$modal.loading("正在下载文件,请耐心等待!"); |
| | | fetch(url, { |
| | | method: "get", |
| | | headers: { Authorization: "Bearer " + getToken() }, |
| | | }) |
| | | .then((response) => { |
| | | if (!response.ok) { |
| | | // 401/403/404 必须显式提示,否则会下到一个内容为错误页的文件 |
| | | throw new Error("HTTP " + response.status); |
| | | } |
| | | return response.blob(); |
| | | }) |
| | | .then((blob) => { |
| | | const blobUrl = window.URL.createObjectURL(blob); |
| | | const link = document.createElement("a"); |
| | | link.href = blobUrl; |
| | | link.setAttribute("download", fileName || "下载文件"); |
| | | document.body.appendChild(link); |
| | | link.click(); |
| | | document.body.removeChild(link); |
| | | window.URL.revokeObjectURL(blobUrl); |
| | | }) |
| | | .catch((err) => { |
| | | console.error("文件下载失败:" + url, err); |
| | | this.$modal.msgError("文件下载失败,请确认文件是否存在或登录是否过期"); |
| | | }) |
| | | .finally(() => { |
| | | this.$modal.closeLoading(); |
| | | }); |
| | | }, |
| | | |
| | | /** |
| | | * 获取另存为的文件名。 |
| | | * |
| | | * 优先级:带扩展名的原始文件名 > 文件地址末段 > 文档标题。 |
| | | * 之所以不直接用 form.filename:该字段在表单中可手工编辑(如“人体器官移植条例”), |
| | | * 不带扩展名,直接用作另存名会导致系统无法识别打开方式。 |
| | | * |
| | | * @param {string} storedName 原始文件名(如 form.filename 中存的 xxx.docx) |
| | | * @param {string} fileUrl 文件地址 |
| | | * @param {string} title 备用名称(文档标题) |
| | | */ |
| | | getFileName(storedName, fileUrl, title) { |
| | | const hasExt = (name) => !!name && name.lastIndexOf(".") > -1; |
| | | |
| | | if (hasExt(storedName)) { |
| | | return storedName; |
| | | } |
| | | |
| | | const source = fileUrl || ""; |
| | | if (source.lastIndexOf("/") > -1) { |
| | | const fromUrl = source.slice(source.lastIndexOf("/") + 1); |
| | | if (fromUrl) { |
| | | return fromUrl; |
| | | } |
| | | } |
| | | |
| | | return title || storedName || "下载文件"; |
| | | }, |
| | | |
| | | //文件上传成功回调 |
| | |
| | | this.$modal.msgSuccess(response.msg); |
| | | this.fileList.push({ name: response.fileName, url: response.fileName }); |
| | | } else { |
| | | this.$modal.msgError(response.msg); |
| | | // 2026-10-09:上传失败必须提示。原来只 console.log,用户看不到失败原因(表现为"点了没反应") |
| | | this.$modal.msgError(response.msg || "上传失败,请重试"); |
| | | } |
| | | //console.log("上传文件后:" + JSON.stringify(this.fileList)); |
| | | }, |
| | |
| | | handleExceed() { |
| | | this.$message.error(`上传文件数量已超过!`); |
| | | }, |
| | | // 上传失败 |
| | | // 上传失败(HTTP 层错误,如登录过期、网络异常) |
| | | handleUploadError(err) { |
| | | this.$message.error("上传失败, 请重试" + JSON.stringify(err)); |
| | | const msg = (err && (err.msg || err.message)) || ""; |
| | | this.$modal.msgError("上传失败,请重试" + (msg ? ":" + msg : "")); |
| | | }, |
| | | }, |
| | | }; |