yxh
3 小时以前 c75027743ea7c3ecc2d0885f01d11d2ad78679a3
src/views/project/documentfiles/index.vue
@@ -1,6 +1,7 @@
<template>
  <div class="app-container">
    <el-form
      class="opo-query-form"
      :model="queryParams"
      ref="queryForm"
      :inline="true"
@@ -273,7 +274,7 @@
                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>
@@ -331,7 +332,6 @@
              <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"
@@ -460,7 +460,6 @@
        fileurl: null,
        status: null,
      },
      fileType: ["png", "jpg", "jpeg", "doc", "xls", "ppt", "txt", "pdf"],
      // 表单参数
      form: {},
      // 表单校验
@@ -469,8 +468,9 @@
      //文件列表
      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(),
@@ -659,34 +659,100 @@
    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 || "下载文件";
    },
    //文件上传成功回调
@@ -698,7 +764,8 @@
        this.$modal.msgSuccess(response.msg);
        this.fileList.push({ name: response.fileName, url: response.fileName });
      } else {
        console.log(response.msg);;
        // 2026-10-09:上传失败必须提示。原来只 console.log,用户看不到失败原因(表现为"点了没反应")
        this.$modal.msgError(response.msg || "上传失败,请重试");
      }
      //console.log("上传文件后:" + JSON.stringify(this.fileList));
    },
@@ -716,9 +783,10 @@
    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 : ""));
    },
  },
};