【vue2+avue】实现文件上传 技术栈用的是kkfileview

前言:avue实现原文件上传是有格式限制的,且预览展示效果稍微差点,我使用kkfileview去写,这样不会限制文件上传格式

1、kkfileview使用参考我另一篇文章【kkFileView】文件文档在线预览解决方案,基本支持主流办公文档的在线预览,如doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,rar,图片,视频,音频等等_^草莓牛乳茶^的博客-CSDN博客

2、全部代码展示

javascript 复制代码
<template>
  <basic-container>
    <avue-crud ref="crud" 
               v-model="form" 
               :before-open="beforeOpen"
               :data="data" 
               :option="option"
               :page.sync="page"
               :permission="permissionList"
               :table-loading="loading"  
               :upload-after="uploadAfter"
               :upload-before="uploadBefore"
               :upload-delete="uploadDelete" 
               :upload-preview="uploadPreview"
               @row-update="rowUpdate"
               @row-save="rowSave"
               @row-del="rowDel" 
               @search-change="searchChange" 
               @search-reset="searchReset" 
               @selection-change="selectionChange"
               @current-change="currentChange"
               @size-change="sizeChange"
               @on-load="onLoad">
      <template slot="fileName" slot-scope="{ type, size, row }">
        <div class="download">
          <div v-for="(item, index) in row.fileLocation" :key="index">
            <el-tooltip :content="item.name" class="item" effect="dark 
                         placement="bottom">
              <div @click="downloadAnnex(row.fileLocation[index], item)">
                {{ item.name }}
              </div>
            </el-tooltip>
          </div>
        </div>
    </avue-crud>
    <showFile ref="showFileList" :fileLocationName="fileLocationName" 
             :fileUpdata="fileUpdata"></showFile>
  </basic-container>
</template>


<script>
import { add, getDetail, getList, remove, update, upspecialdata } from "@/api/safety/administration/safetylicense";
import { mapGetters } from "vuex";
import showFile from "../../safety/showFile.vue";
import safetydivicecloseopenshrecord from "@/views/public/safetydivicecloseopenshrecord.vue";
import { updateFileLocation } from "@/mixins/update-file-location"

export default {
  components: {
    showFile,
  },
  mixins: [updateFileLocation],
  data() {
      fileUpdata: "fileName",       // 表格显示时用
      fileLocationName: "fileLocation",   // 附件本字段
      form: {},
      query: {},
      loading: true,
      page: {
        pageSize: 10,
        currentPage: 1,
        total: 0,
      },
      selectionList: [],
      option: {
        height: "auto",
        calcHeight: 210,
        searchShow: true,
        searchMenuSpan: 6,
        tip: false,
        border: true,
        index: true,
        viewBtn: true,
        selection: true,
        labelWidth: 140,
        column: [
          {
            label: "许可证照上传",
            prop: "fileLocation",
            type: "upload",
            dataType: "object",
            showFileList: true,
            showColumn: false,
            multiple: true,
            drag: false,
            loadText: "附件上传中,请稍等",
            previewSrcList: false,
            propsHttp: {
              name: "originalName",
              url: "link",
              res: "data",
              // home: "domain",
            },
            props: {
              label: 'name',
              value: 'link'
            },
            action: "/api/blade-resource/oss/endpoint/put-file-attach",
            span: 24, rules: [{
              required: true,
              message: "请输入附件",
              trigger: "blur"
            }]


          },

          {
            label: "文件预览",
            prop: "fileName",
            display: false,
            showColumn: true,
            rules: [
              {
                required: true,
                message: "请输入文件名称",
                trigger: "blur",
              },
            ],
          },
          },
        ],
      },
      data: [],
    };
  },

  methods: {
    shrecordfun(row, index) {
      this.closeoropenid = row.id;
      this.shrecordVisible = true;
    },

    // 是否点击放大图片
    uploadPreview(file, column, done) {
      this.$nextTick(() => {
        /**
         * e:文件路径
         * k:源文件名字
         */
        this.$refs.showFileList.downloadAnnex(file.url || file.link, file.name);//调用组件方法
      });
    },
    // 预览方法
    downloadAnnex(e, k) {
      this.$nextTick(() => {
        this.$refs.showFileList.downloadAnnex(e.url || e.link, k);//调用组件方法
      });
    },
    // 上传前方法
    uploadBefore(file, done, loading, column) {
      done()
    },
    // 上传后方法
    uploadAfter(res, done, loading, column) {
      this.form[this.fileLocationName] = this.formFileLocation(res, this.fileLocationName)
      done()
    },
    // 删除前的方法
    uploadDelete(file, column) {
      this.$nextTick(() => {
        this.$refs.showFileList.frontUploadDelete(file, column); //调用组件方法
      });
    },

// 新增数据调取接口
    rowSave(row, done, loading) {
      row[this.fileLocationName] = JSON.stringify(row[this.fileLocationName])
      // 结束
      add(row).then(
        () => {
          done();
          this.onLoad(this.page);
          this.$message({
            type: "success",
            message: "操作成功!",
          });
        },
        (error) => {
          window.console.log(error);
          loading();
        }
      );
    },

// 编辑数据调取接口
    rowUpdate(row, index, done, loading) {
      // 修改事件
      // row[this.tableDisplay] = ""; //表格显示的字段
      row[this.fileLocationName] = JSON.stringify(row[this.fileLocationName])
      // 结束
      update(row).then(
        () => {
          done();
          this.onLoad(this.page);
          this.$message({
            type: "success",
            message: "操作成功!",
          });
        },
        (error) => {
          window.console.log(error);
          loading();
        }
      );
    },
// 点击查看/编辑查看数据
    beforeOpen(done, type) {
      this.$nextTick(() => {
        this.$refs.showFileList.add(type);//调用组件方法
      });
      if (["edit", "view"].includes(type)) {

        getDetail(this.form.id).then((res) => {
          this.form = this.formData(res, this.fileLocationName)
        });
      }
      done();
    },
    searchReset() {
      this.query = {};
      this.onLoad(this.page);
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    },
    selectionChange(list) {
      this.selectionList = list;
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
      this.onLoad(this.page);
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    },

// 页面初始数据加载
    onLoad(page, params = {}) {
      this.loading = true;
      params.mytype = "3";
      getList(
        page.currentPage,
        page.pageSize,
        Object.assign(params, this.query)
      ).then((res) => {
        const data = res.data.data;
        this.page.total = data.total;
        this.data = this.dataRecords(data, this.fileLocationName)
        this.loading = false;
        this.selectionClear();
      });
    },
  },

注意:

文件上传:

1)、type:'upload'

2)、dataType配置数据的结构string或object,

当dataType配置为object时,可以配置props存储的数据结构

  • label图片的名称
  • value路径地址

3)、propsHttp配置请求接口返回的数据结构

  • name图片的名称
  • url路径地址
  • res返回数据层级结构
  • home相对路径的主路径

意思就是你配置dataType为object,传给后台:json数据格式

eg:****"{\\"name\\":\\"原平经济技术开发区化工园区禁限控目录.pdf\\",\\"link\\":\\"/261267-sksoft/upload/20230828/8855ad3f839c2831c2c07c9f8f645af6.pdf\\"}"

字段回显就会是中文,不需要自己在单独处理数据

4)、action:是上传文件调取的接口

5)、showfile文件是我写的公共的上文件组件 不使用原生的预览样式,自己封装的

6)、rowUpdate - rowSave - beforeopen - onLoad页面中使用的方法是我封装的mixins的方法

7)、fileLocationName定义的是上传文件的给后端传接口的字段名字,因为名字不同所以写了这个来定义

3、showfile文件

javascript 复制代码
<template>
  <div>
// 不使用avue原生的预览样式
    <el-dialog title="预览与下载" :visible.sync="dialogVisible" width="60%" :fullscreen="dialogFull"
      :before-close="handleClose" :append-to-body="true" :modal-append-to-body="false">
      <template slot="title">
        <div class="avue-crud__dialog__header">
          <span class="el-dialog__title">
            <span style="
                display: inline-block;
                background-color: #3478f5;
                width: 3px;
                height: 20px;
                margin-right: 5px;
                float: left;
                margin-top: 2px;
              "></span>
            预览与下载
          </span>
          <div class="avue-crud__dialog__menu" @click="dialogFull ? (dialogFull = false) : (dialogFull = true)">
            <i class="el-icon-full-screen"></i>
          </div>
        </div>
      </template>
      <div class="dialogVisible">
        <div class="dialogVisible_left">
          <!-- <img  class="imgurl" :src="imgurl" /> -->
          <iframe id="frmDialog" frameborder="0" width="100%" height="750rpx" allowfullscreen="true"
            :src="pdfWord"></iframe>
        </div>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="handleClose">取 消</el-button>
        <el-button type="primary" @click="dialogVisibleDow">下载</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
export default {
  props: {
    tableDisplay: String,
    fileUpdata: String,
    fileLocationName: String
  },
  data() {
    return {
      dialogFull: false,
      dialogVisible: false,
      pdfWord: "",
      imgurl: "",
      flieName: [],
      fileUrl: [],
      dowName: "",
      addName: [],
      annexStrList: [],
      demoDel: [],
      btnAnnexStrList: []
    };
  },
  methods: {
    // 全屏
    handleClose(done) {
      this.dialogVisible = false
      this.dialogFull = false,
        done();
    },
    // 文件预览
    downloadAnnex(e, data) {
      this.dowName = data;
      // let KKFILEIP = "线上地址";
      let KKFILEIP = "http://127.0.0.1";
      // const fileurl =""
      // if(e.indexOf(window.location.origin)>=0){
      //   fileurl =e
      // }else{
      //   fileurl = window.location.origin + e;
      // }
      // const fileurl = window.location.origin + e;
      // const fileurl = '地址' + e;
      const fileurl = '测试地址' + e;
      console.log(fileurl, 'fileurl');
      this.imgurl = fileurl;
      this.pdfWord =
        KKFILEIP +
        // ":10001/preview/onlinePreview?url=" +
        ":8012/onlinePreview?url=" +
        encodeURIComponent(Base64.encode(fileurl));
      this.dialogVisible = true;
    },
    // 下载图片
    dialogVisibleDow() {
      getBlob(this.imgurl).then((blob) => {
        saveAs(blob, this.dowName);
      });
      function getBlob(url) {
        return new Promise((resolve) => {
          const xhr = new XMLHttpRequest();
          xhr.open("GET", url, true);
          xhr.responseType = "blob";
          xhr.onload = () => {
            if (xhr.status === 200) {
              resolve(xhr.response);
            }
          };
          xhr.send();
        });
      }
      function saveAs(blob, filename) {
        const elelink = document.createElement("a");
        elelink.style.display = "none";
        elelink.download = filename;
        elelink.href = window.URL.createObjectURL(blob);
        document.body.appendChild(elelink);
        elelink.click();
        document.body.removeChild(elelink);
      }
    },
    // 上传前方法
    frontUploadBefore(file, done, loading, column) {
      this.flieName.push(file.name);
      done();
    },
    // 上传后方法
    behindUploadAfter(res, done, loading, column) {
      if (this.annexStrList[0] == "" || this.annexStrList[0] == ",") {
        this.annexStrList = []
      }
      let a = { url: res.link, name: res.originalName }
      // // this.addName.push(res.link + "-" + res.originalName)
      this.addName.push(a)
      this.annexStrList.push(a.url)
      done();
    },
    // 删除前的方法
    frontUploadDelete(file, column) {
      this.fileUrl.push(file.url);
      this.demoDel.push(this.annexStrList[file.uid])
      this.annexStrList.splice(file.uid, 1)
    },
    // 
    //打开时候要执行的东西
    add(type) {
      this.addName = [];
      this.demoDel = [];
      if (type == "add") {
        this.flieName = [];
      }
    },
    addRes(res) {
      if (res.data.data[this.tableDisplay]) {
        let filelist = JSON.parse(res.data.data[this.tableDisplay]);
        let alist = [];
        for (let i = 0; i < filelist.length; i++) {
          let b = { name: filelist[i].name, url: filelist[i].url }
          alist.push(b);
        }
        this.annexStrList = alist
        return alist;
      }

    },

    flieNameList() {
      return this.flieName
    },
    fileUrlList() {
      this.fileUrl = []

    },
    demoDelList() {
      return this.demoDel.toString();

    },
    addNameList() {
      return JSON.stringify(this.addName)
    },
    annexStrNameList() {
      return this.annexStrList.toString()
    },
    annexStrNameListView() {
      return this.annexStrList

    },
    //初始加载
    flieOnload(data) {
      for (let i = 0; i < data.records.length; i++) {
        let allName = [];
        if (data.records[i][this.tableDisplay]) {
          data.records[i][this.tableDisplay] = data.records[i][this.tableDisplay].split(",");
          let arr = data.records[i][this.tableDisplay];
          for (let j = 0; j < arr.length; j++) {
            let index = arr[j].indexOf("-");
            let list = arr[j].substring(index + 1, arr[j].length);
            allName.push(list.toString());
          }
          data.records[i][this.fileUpdata] = allName;
          data.records[i][this.fileLocationName] = data.records[i][this.fileLocationName].split(",");
        }
      }
    },
  },
};
</script>


<style scoped lang="scss">
/*  dialog*/
.el-dialog__header {
  padding: 15px 20px 15px;
}

.el-dialog__headerbtn {
  top: 15px;
}

/*dialog header*/
/deep/.el-dialog__header {
  background: #e3eaed;
}

/deep/.avue-crud__dialog__header {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
  -webkit-box-pack: justify;
  -ms-flex-pack: justify;
  justify-content: space-between;
}

/deep/.el-dialog__title {
  color: rgba(0, 0, 0, 0.85);
  font-weight: 500;
  word-wrap: break-word;
}

/deep/.avue-crud__dialog__menu {
  padding-right: 20px;
  float: left;
}

/deep/.avue-crud__dialog__menu i {
  color: #909399;
  font-size: 15px;
}

/deep/.el-icon-full-screen {
  cursor: pointer;
}

/deep/.el-icon-full-screen:before {
  content: "\e719";
}

.download {
  color: rgb(50, 185, 226);
}

.dialogVisible {
  display: flex;
}

.dialogVisible_right {
  width: 50%;
}

.dialogVisible_left {
  width: 100%;
}

.imgurl {
  height: 100%;
  width: 100%;
}
</style>

注意:这一块代码可能会有点乱,vue页面 有上传前,上传后方法就不会使用shoefile文件的方法,showfile页面的方法是针对图片上传的方法

4、updateFileLocation文件

javascript 复制代码
export const updateFileLocation = {
  methods: {
    formFileLocation(res, fileLocationName) {
      let a = [
        ...this.form[fileLocationName],
        {
          name: res.originalName,
          link: res.link,
        },
      ]
      return a
    },
    isJSON(data) {
      try {
        JSON.parse(data)
        return true
      } catch (error) {
        return false
      }
    },
    formData(res, fileLocationName) {
      let b = this.isJSON(res.data.data[fileLocationName])
        ? JSON.parse(res.data.data[fileLocationName])
        : ''
      return b
    },
    dataRecords(data, fileLocationName) {
      return data.records.map((i) => {
        const obj = this.isJSON(i[fileLocationName])
          ? JSON.parse(i[fileLocationName])
          : ''
        i[fileLocationName] = obj || null
        return i
      })
    },
  },
}
相关推荐
卸任8 小时前
Vibe coding工具使用心得
前端·ai编程
brien98 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance9 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M9 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写9 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李9 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易9 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
清晨Coding9 小时前
删了 Cookie 还能自动登录?Google系网站登录态自动恢复的原理分析
javascript
风骏时光牛马9 小时前
前沿视界
前端
kyriewen9 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员