微信小程序文件上传(唤起文件资源管理器)

问题

在微信小程序开发开发中,可能会遇到需要上传文件的场景,用户需要从手机文件管理器中选择文件,然后上传到服务器.但是微信小程序只支持选择回话中的文件,无法从手机中选择

方案

我们可以通过小程序的web-view实现.通过html的实现文件上传.

代码

这个代码是基于微信小程序的,当前集成在一个组件中。上传一个文件就会返回到组件中。

web-view页面

<template>
  <web-view
    class="web-view"
    @message="message"
    :src="src"
  ></web-view>
</template>

<script>
import { HOMEINDEX_NAME, ACCESS_TOKEN, CURRENT_URL } from "@/common/util/constants"
export default {
  name: 'JkyJeecgSlpzWxIdentification',

  data() {
    return {
      src: ``,
      prevKey: ''
    };
  },
  onLoad(options) {
    this.prevKey = JSON.parse(decodeURIComponent(options.query)).prevKey
    let baseUrl = this.$config.identificationUrl + '/' + uni.getStorageSync(CURRENT_URL)
    let token = uni.getStorageSync(ACCESS_TOKEN)
    this.src = this.$config.identificationUrl + `/fileUpload/index.html?baseUrl=` + baseUrl + `&token=` + token
    console.log(this.src, 'this.src')
  },
  created() {
  },
  beforeMount() {
  },
  mounted() {
  },
  methods: {
    message(arg) {
      console.log(arg, '接收h5页面发来的消息')
      let pages = getCurrentPages();
      let prev = pages[pages.length - 2]; //上一页
      if (this.prevKey) {
        if (arg.target.data[0].message == '上传成功') {
          prev.$vm[this.prevKey + 'Msg'] = '上传成功'
          prev.$vm[this.prevKey] = arg.target.data[0].url
        } else if (arg.target.data[0].message == '上传失败') {
          prev.$vm[this.prevKey + 'Msg'] = '上传失败'
        }
      }
    },
  },
};
</script>

<style lang="less" scoped>
</style>

my-file-upload.vue

<template>
  <view>
    <l-file
      ref="lFile"
      :prevKey="prevKey"
      :logo="logo"
      @up-success="onSuccess"
    ></l-file>
    <button
      type="primary"
      size="mini"
      @click="upload"
      v-if="!disabled"
    >文件上传</button>
    <view>
      <view
        class="cu-form-group"
        v-for="(item,index) in fileList"
        :key="index"
        :data-url="item"
      >
        <view
          class="text_fileName"
          @tap.stop="reviewFile(item)"
        >{{item}}</view>
        <view
          v-if="!disabled"
          class="cu-tag"
          @tap.stop="DelImg"
          :data-index="index"
        >
          <uni-icons
            type="clear"
            color="#c0c4cc"
            size="24"
          ></uni-icons>
        </view>
      </view>
    </view>
  </view>
</template>

<script>
import { ACCESS_TOKEN, CURRENT_URL } from '@/common/util/constants.js'
export default {
  name: 'MyFileUpoad',
  props: {
    prevKey: { type: String },
    disabled: {
      type: Boolean,
      default: () => {
        return false
      }
    },
    value: { type: String },
    size: { //文件大小限制
      type: Number,
      default: () => {
        return 100
      }
    },
    extName: { //文件大小限制
      type: Array,
      default: () => {
        return [".docx", ".doc", ".rtf", ".txt", ".xltx", ".xls", ".xlsm", ".xlsb", ".csv", ".xml", ".pptx", ".ppt", ".xps", ".pdf", ".jpg", ".png", ".jpeg", ".odf", ".tiff", ".svg", ".zip", ".rar", ".xlsx"]
      }
    },
  },
  mounted: function () {
  },
  data() {
    return {
      logo: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F00%2F00%2F07%2F155788a6d8a5c42.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1619847627&t=2da40b583002205c204d980b54b35040',
      header: '',
      fileList: [],
      pathlist: [],
      uploadUrl: "/sys/common/upload",
      downLoadUrl: "/sys/common/static/",
      num: 0
    }
  },
  watch: {
    value: {
      handler(val) {
        console.log(val, 79)
        if (val && !this.num) {
          this.fileList = val.split(',');
          this.num++;
        }
      },
      deep: true,
      immedaite: true
    }
  },
  created() {
    if (this.value) {
      this.fileList = this.value.split(',');
    }
  },
  methods: {
    reviewFile(item) {
      let fileType = item.split('.')[item.split('.').length - 1]
      let url = this.$config.apiUrl + uni.getStorageSync(CURRENT_URL) + this.downLoadUrl + item
      /* 下载返回临时路径(退出应用失效) */
      this.$refs.lFile.download({ url })
        .then(path => {
          /* 打开附件 */
          this.$refs.lFile.open(path, fileType);
        });
    },
    onWebViewSuccess(path) {
      if (path) {
        this.pathlist.push(path);
        this.$emit('file-change', this.pathlist.join(','))
        if (this.fileList.length != 0) {
          this.fileList = this.fileList.concat([path])
        } else {
          this.fileList.push(path)
        }
      } else {
        uni.showToast({
          title: `上传失败`
        })
      }
    },
    onSuccess(res) {
      if (res.data.success) {
        let path = res.data.message;
        this.pathlist.push(path);
        this.$emit('file-change', this.pathlist.join(','))
        if (this.fileList.length != 0) {
          this.fileList = this.fileList.concat([path])
        } else {
          this.fileList.push(path)
        }
      } else {
        uni.showToast({
          title: `上传失败`
        })
      }
    },
    // 选择上传触发函数
    upload() {
      let that = this
      this.$refs.lFile.upload({
        //替换为你的上传接口地址
        url: that.$config.apiUrl + uni.getStorageSync(CURRENT_URL) + that.uploadUrl,
        // 服务端接收附件的key
        name: 'file',
        //根据你接口需求自定义 (优先不传content-type,安卓端无法收到参数再传)
        header: {
          'X-Access-Token': uni.getStorageSync(ACCESS_TOKEN)
        },
        // 限制选择附件的大小上限,默认10M
        // maxSize: 20,
        // 若需要在body单独添加附件名或附件大小如下方式传入组件:
        // addName: '后端要的附件名称字段key,此处请勿写name的同值如(file),会覆盖name',
        // addSize: '后端要的附件大小字段key'
        // body参数直接写key,value,如:
        // date: '2020-1-1',
        // key2: 'value2',
      });
    },
    DelImg(e) {
      uni.showModal({
        title: '提示',
        content: '确认要删除吗',
        cancelText: '取消',
        confirmText: '确认',
        success: res => {
          if (res.confirm) {
            this.pathlist.splice(e.currentTarget.dataset.index, 1)
            this.fileList.splice(e.currentTarget.dataset.index, 1)
            this.$emit('file-change', this.pathlist.join(','))
          }
        }
      })
    },
  }
}
</script>

<style lang="less" scoped>
.cu-tag {
  background: transparent !important;
}
.text_fileName {
  width: 85%;
  overflow: hidden;
  text-overflow: ellipsis;
  word-break: break-all;
}
</style>

组件用法

<myFileUpload
          ref="sceneFileRef"
          prevKey="sceneFile"
          :disabled="lookDis"
          v-model="model.sceneFile"
          @file-change="fileChange('sceneFile',$event)"
        />
          fileChange(key, value) {
      this.$set(this.model, key, value);
    },

l-file.vue

<template>
  <view>
    <view
      class='t-toptips'
      :style="{top: top,background: cubgColor}"
      :class="[show?'t-top-show':'']"
    >
      <view
        v-if="loading"
        class="flex flex-sub"
      >
        <view class="flex flex-sub">
          <view class="cu-progress flex-sub round striped active sm">
            <view :style="{ background: color,width: value + '%'}"></view>
          </view>
          <text class="margin-left">{{value}}%</text>
        </view>
        <view
          @click="downOnAbort"
          v-if="value<100"
          class="close"
        >取消</view>
      </view>
      <block v-else>{{msg}}</block>
    </view>
    <!-- #ifdef H5 -->
    <h5-file
      v-if="showH5"
      ref="h5File"
      :logo="logo"
      :showTip="showTip"
      :progress="value"
      @abort="onAbort"
      @close="onCloseH5"
    ></h5-file>
    <!-- #endif -->
  </view>
</template>

<script>
import h5File from './h5-file.vue';
export default {
  components: { h5File },
  props: {
    logo: {
      type: String,
      default: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F00%2F00%2F07%2F155788a6d8a5c42.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1619847627&t=2da40b583002205c204d980b54b35040'
    },
     prevKey: {
      type: String,
      default: ''
    },
    top: {
      type: String,
      default: 'auto'
    },
    bgColor: {
      type: String,
      default: 'rgba(49, 126, 243, 0.5)',
    },
    color: {
      type: String,
      default: '#55aa00',
    }
  },
  data() {
    this.uploadTask = null;
    this.downloadTask = null;
    return {
      cubgColor: '',
      loading: false,
      value: 5,
      show: false,
      msg: '执行完毕~',
      showH5: false,
      showTip: false,
    }
  },
  methods: {
    toast(title = '', { duration = 2000, icon = 'none' } = {}) {
      uni.showToast({ title, duration, icon });
    },
    getRequest(url) {
      let theRequest = new Object();
      let index = url.indexOf("?");
      if (index != -1) {
        let str = url.substring(index + 1);
        let strs = str.split("&");
        for (let i = 0; i < strs.length; i++) {
          theRequest[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]);
        }
      }
      return theRequest;
    },
    /*
      上传说明:
      url:上传接口地址
      name:上传文件key值
      header: 上传接口请求头
      ...:body内其他参数
    */
    appChooseFile({ cuWebview = '', url, name = 'file', header, addName = '', addSize = '', maxSize = 10, ...formData } = {}) {
      // #ifdef APP-PLUS
      let wvPath = '/uni_modules/l-file/hybrid/html/index.html';
      let wv = plus.webview.create("", wvPath, {
        'uni-app': 'none',
        top: 0,
        height: '100%',
        background: 'transparent'
      }, {
        url,
        header,
        addName,
        addSize,
        maxSize,
        formData,
        key: name,
        logo: this.logo
      });
      wv.loadURL(wvPath);
      this.$root.$scope.$getAppWebview().append(wv);
      wv.overrideUrlLoading({ mode: 'reject' }, (e) => {
        let { fileName, size, str } = this.getRequest(e.url);
        fileName = unescape(fileName);
        str = unescape(str);
        return this.handleBack(fileName, str, size);
      });
      // #endif
    },
    wxChooseFile() {
      this.$Router.push({ path: '/components/my-componets/webView-fileUpload',query:{prevKey:this.prevKey}});
    },
    wxChooseFile1({ maxSize = 10, ...param }) {
      wx.chooseMessageFile({
        count: 1,
        type: 'file',
        success: ({ tempFiles }) => {
          this.setdefUI();
          let file = tempFiles[0];
          if (file.size > (1024 * 1024 * Math.abs(maxSize))) {
            uni.showToast({
              title: `单个文件请勿超过${maxSize}M,请重新上传`,
              icon: 'none'
            });
            return this.errorHandler('文件选择失败', this.upErr);
          }
          this.handleWXUpload(param, file);
        },
        fail: () => this.errorHandler('文件选择失败', this.upErr)
      })
    },
    h5ChooseFile({ maxSize = 10, ...param } = {}) {
      this.showH5 = true;
      this.value = 0;
      this.$nextTick(() => {
        this.$refs.h5File.hFile.onchange = (event) => {
          let file = event.target.files[0];
          if (file.size > (1024 * 1024 * Math.abs(maxSize))) {
            uni.showToast({
              title: `单个文件请勿超过${maxSize}M,请重新上传`,
              icon: 'none'
            });
            return;
          }
          this.handleH5Upload(param, file);
        }
      })
    },
    handleH5Upload({ url, name = 'file', header, addName, addSize, ...data } = {}, tempFile) {
      let fileName = tempFile.name;
      let formData = new FormData();
      for (let keys in data) { formData.append(keys, data[keys]); }
      if (addName) { formData.append(addName, fileName); }
      if (addSize) { formData.append(addSize, tempFile.size); }
      formData.append(name, tempFile);
      this.uploadTask = new XMLHttpRequest();
      this.uploadTask.open("POST", url, true);
      for (let keys in header) { this.uploadTask.setRequestHeader(keys, header[keys]); }

      this.uploadTask.ontimeout = () => {
        setTimeout(() => {
          this.showTip = false;
          return this.errorHandler('请求超时', this.upErr);
        }, 1000);
      };

      this.uploadTask.upload.addEventListener("progress", (event) => {
        if (event.lengthComputable) {
          this.value = Math.ceil(event.loaded * 100 / event.total);
          if (this.value > 100) { this.value = 100; }
          this.$forceUpdate();
        }
      }, false);

      this.uploadTask.onreadystatechange = (ev) => {
        if (this.uploadTask.readyState == 4) {
          console.log('status:' + this.uploadTask.status);
          if (this.uploadTask.status == 200) {
            return this.handleBack(fileName, this.uploadTask.responseText, tempFile.size);
          }
          else {
            this.showTip = false;
            if (this.uploadTask.status == 0) {
              console.log('请检查请求头Content-Type与服务端是否匹配,并确认服务端已正确开启跨域');
            }
            return this.errorHandler('文件上传失败', this.upErr);
          }

        }
      };
      this.showTip = true;
      this.uploadTask.send(formData);
    },
    handleWXUpload({ url, name = 'file', header, addName, addSize, ...formData } = {}, tempFile) {
      let opt = { url, name, formData, header, filePath: tempFile.path };
      if (addName) { opt.formData[addName] = tempFile.name; }
      if (addSize) { opt.formData[addSize] = tempFile.size; }
      let fileName = tempFile.name;
      opt['fail'] = (e) => {
        this.showTip = false;
        return this.errorHandler('文件上传失败', this.upErr)
      };
      opt['success'] = (res) => {
        if (res.statusCode == 200) {
          let data = JSON.parse(res.data);
          //可自行添加后台返回状态验证
          return this.onCommit(this.$emit('up-success', { fileName, size: tempFile.size, data }));
        }
        return this.errorHandler('文件上传失败', this.upErr);
      };
      this.showTip = true;
      this.uploadTask = uni.uploadFile(opt);
      this.uploadTask && this.uploadTask.onProgressUpdate(({ progress = 0 }) => {
        if (progress <= 100) {
          this.value = progress;
          this.$forceUpdate();
        }
      });
    },
    onCloseH5() {
      this.showH5 = false;
    },
    onAbort() {
      this.uploadTask && this.uploadTask.abort();
      this.showTip = false;
    },
    downOnAbort() {
      this.downloadTask && this.downloadTask.abort();
      this.onCommit(false, '已取消');
    },
    // app+h5返回内容,此处按实际项目修改
    handleBack(fileName, str = '{}', size) {
      console.log('可根据需求自行修改emit内容,服务端返回:' + str);
      try {
        str = JSON.parse(str);
      } catch (e) {
        console.log('Tips: responseText are not JSON');
      }
      return this.onCommit(this.$emit('up-success', { statusCode: 200, fileName, size, data: str }));
    },
    /* 
    上传
    */
    upload(param = {}) {
      if (!param.url) { this.toast('上传地址不正确'); return; }
      if (this.loading) { this.toast('还有个文件玩命处理中,请稍候..'); return; }

      // #ifdef APP-PLUS
      return this.appChooseFile(param);
      // #endif

      // #ifdef MP-WEIXIN
      return this.wxChooseFile(param);
      // #endif

      // #ifdef H5
      this.h5ChooseFile(param);
      // #endif
    },
    /* 
    打开文件 
    */
    open(filePath, fileType) {
      let system = uni.getSystemInfoSync().platform;
      if (system == 'ios') { filePath = encodeURI(filePath); }
      uni.openDocument({
        filePath,
        fileType: fileType,
        success: (res) => { console.log('打开文档成功'); }
      });
    },
    /* 
    APP自定义保存 
     */
    plusSaveFile({ url, customName = '', opt }) {
      return new Promise((resolve, reject) => {
        // 可自行修改参数
        // 参数api: http://www.html5plus.org/doc/zh_cn/downloader.html#plus.downloader.DownloadOptions
        let downloadOptions = {
          method: "GET",
          timeout: 120,
          retryInterval: 10,
          filename: 'file://storage/emulated/0/lFile/' + customName
        };
        downloadOptions = { ...downloadOptions, ...opt };
        this.downloadTask = plus.downloader.createDownload(url, downloadOptions, (d, status) => {
          // 下载完成
          if (status == 200) {
            let tempFilePath = d.filename;
            this.value = 100;
            this.onCommit(resolve(tempFilePath))
          } else {
            this.errorHandler('下载失败', reject)
          }
          this.downloadTask = null;
        });
        this.downloadTask.addEventListener('statechanged', ({ downloadedSize = 0, state = 0, totalSize = 0 } = {}) => {
          if (state === 3) {
            let total = totalSize > 0 ? totalSize : fileSize;
            let progressVal = Math.ceil(downloadedSize / total * 100);
            this.value = progressVal > 100 ? 100 : progressVal;
            this.$forceUpdate()
          }
        }, false);
        this.downloadTask.start();
      });
    },
    /* 
    下载
     type: temporary=返回临时地址,save=长期保存到本地
     */
    download({ url, type = 'save', customName = '', ...opt }) {
      if (this.loading) {
        this.toast('还有个文件玩命处理中,请稍候..');
        return;
      }
      this.setdefUI();
      // #ifdef APP-PLUS
      /* if (type == 'save') {
        return this.plusSaveFile({url,customName,opt});
      } */
      // #endif
      return new Promise((resolve, reject) => {
        url = encodeURI(url);
        this.downloadTask = uni.downloadFile({
          url,
          ...opt,
          success: ({ statusCode, tempFilePath }) => {
            if (statusCode === 200) {

              if (type == 'save') {
                uni.saveFile({
                  tempFilePath,
                  success: ({ savedFilePath }) => {
                    console.log('errstatusCodestatusCodestatusCode');
                    this.onCommit(resolve(savedFilePath))
                    console.log('errstatusCodestatusCodestatusCode');
                  },
                  fail: (err) => {
                    console.log('下载失败');
                    this.errorHandler('下载失败', reject)
                  }
                });

              }
              else {
                this.onCommit(resolve(tempFilePath))
              }
              // #ifdef H5
              this.onCommit(resolve(tempFilePath))
              // #endif
            }
          },
          fail: () => this.errorHandler('下载失败', reject)
        });
        this.downloadTask.onProgressUpdate(({ progress = 0 }) => {
          if (progress <= 100) {
            this.value = progress;
            this.$forceUpdate();
          }
        });
      })
    },
    onCommit(resolve, msg = '执行完毕~') {
      this.msg = msg;
      this.loading = false;
      this.showTip = false;
      this.cubgColor = 'rgba(57, 181, 74, 0.5)';
      this.uploadTask = null;
      this.downloadTask = null;
      setTimeout(() => { this.show = false; this.showH5 = false; }, 1500);
      return resolve;
    },

    setdefUI() {
      this.cubgColor = this.bgColor;
      this.value = 0;
      this.loading = true;
      this.show = true;
    },

    upErr(errText) {
      this.$emit('up-error', errText);
    },

    errorHandler(errText, reject) {
      this.msg = errText;
      this.loading = false;
      this.cubgColor = 'rgba(229, 77, 66, 0.5)';
      this.uploadTask = null;
      this.downloadTask = null;
      setTimeout(() => { this.show = false; }, 1500);
      return reject(errText);
    }
  }
}
</script>

<style scoped>
.t-toptips {
  width: 100%;
  padding: 18upx 30upx;
  box-sizing: border-box;
  position: fixed;
  z-index: 90;
  color: #fff;
  font-size: 30upx;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  word-break: break-all;
  opacity: 0;
  transform: translateZ(0) translateY(-100%);
  transition: all 0.3s ease-in-out;
}

.close {
  width: 3em;
  text-align: right;
}

.t-top-show {
  transform: translateZ(0) translateY(0);
  opacity: 1;
}

.flex {
  display: flex;
  align-items: center;
}

.flex-sub {
  flex: 1;
}

.round {
  border-radius: 5000upx;
}

/* ==================
	         进度条
	 ==================== */

.cu-progress {
  overflow: hidden;
  height: 28upx;
  background-color: #ebeef5;
  display: inline-flex;
  align-items: center;
  width: 100%;
}

.cu-progress + view,
.cu-progress + text {
  line-height: 1;
}

.cu-progress.xs {
  height: 10upx;
}

.cu-progress.sm {
  height: 20upx;
}

.cu-progress view {
  width: 0;
  height: 100%;
  align-items: center;
  display: flex;
  justify-items: flex-end;
  justify-content: space-around;
  font-size: 20upx;
  color: #ffffff;
  transition: width 0.6s ease;
}

.cu-progress text {
  align-items: center;
  display: flex;
  font-size: 20upx;
  color: #333333;
  text-indent: 10upx;
}

.cu-progress.text-progress {
  padding-right: 60upx;
}

.cu-progress.striped view {
  background-image: linear-gradient(
    45deg,
    rgba(255, 255, 255, 0.15) 25%,
    transparent 25%,
    transparent 50%,
    rgba(255, 255, 255, 0.15) 50%,
    rgba(255, 255, 255, 0.15) 75%,
    transparent 75%,
    transparent
  );
  background-size: 72upx 72upx;
}

.cu-progress.active view {
  animation: progress-stripes 2s linear infinite;
}

@keyframes progress-stripes {
  from {
    background-position: 72upx 0;
  }

  to {
    background-position: 0 0;
  }
}
</style>

fileUpload index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件上传</title>
    <script src="vue.min.js"></script>
    <script src="axios.min.js"></script>
    <!--微信小程序 JS-SDK 如果不需要兼容微信小程序,则无需引用此 JS 文件。 -->
    <script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
    <!-- uni 的 SDK -->
    <script type="text/javascript" src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"></script>
    <!-- 在引用依赖的文件后,需要在 HTML 中监听 UniAppJSBridgeReady 事件触发后,才能安全调用 uni 的 API-->
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            background: #f1f1f1;
        }

        .container {
            width: 100%;
            margin: 50px auto 0;
        }

        .container .upload-pannel {
            width: 60%;
            background: #fff;
            border-radius: 10px;
            margin: 20px auto 0;
            overflow: hidden;
        }

        .container .upload-pannel .pannel-title {
            width: 90%;
            margin: 20px auto;
            font-size: 15px;
            color: #333;
            font-weight: bold;
            display: block;
        }

        .container .upload-pannel .file-select {
            width: 90%;
            height: 13rem;
            border: 2px dashed rgb(59, 94, 225);
            background: rgba(59, 94, 225, 0.05);
            margin: 20px auto;
            display: block;
            border-radius: 10px;
            position: relative;
        }

        .container .upload-pannel input[type="file"] {
            width: 100%;
            height: 100%;
            opacity: 0;
            position: absolute;
            left: 0;
            top: 0;
        }

        .container .upload-pannel .upload-icon {
            width: 40px;
            height: 35px;
            display: block;
            margin: 0 auto;
            line-height: 250px;
        }

        .container .upload-pannel .upload-icon img {
            width: 40px;
            height: 35px;
        }

        .container .upload-pannel .file-select .upload-text {
            text-align: center;
            display: block;
            font-size: 14px;
            color: #999;
            line-height: 230px;
        }

        .container .upload-pannel .upload-progress {
            width: 90%;
            height: 60px;
            background: #f1f1f1;
            margin: 20px auto;
            border-radius: 10px;
            overflow: hidden;
        }

        .container .upload-pannel .upload-progress .progress-bar-container {
            position: relative;
            width: 90%;
            height: 10px;
            margin: 25px auto;
            background: #f1f1f1;
            border-radius: 10px;
            overflow: hidden;
        }

        .container .upload-pannel .upload-progress .progress-bar {
            height: 100%;
            background: rgb(59, 94, 225);
            border-radius: 10px;
            transition: width 0.3s ease-in-out;
        }

        .container .upload-pannel .upload-progress .progress-text {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            font-size: 12px;
            color: #fff;
        }

        .container .upload-pannel .imgPreview {
            width: 90%;
            margin: 20px auto;
            display: block;
        }
    </style>
</head>

<body>
    <div id="app"></div>
    <script>
        new Vue({
            el: '#app',
            data() {
                return {
                    fiUrl: '',
                    uploadProgress: 0,
                    uploadProgressbg: false,
                    baseUrl: '',
                    token: ''
                };
            },
            mounted() {
                this.baseUrl = this.getQueryParam('baseUrl');
                this.token = this.getQueryParam('token');
            },
            methods: {
                getQueryParam(name) {
                    let search = window.location.search.substring(1); // 去掉开头的?
                    let params = search.split('&');
                    for (let i = 0; i < params.length; i++) {
                        let pair = params[i].split('=');
                        if (pair[0] === name) {
                            return pair[1];
                        }
                    }
                    return null; // 如果没有找到参数,返回null
                },
                async handleImageUpload(event) {
                    const file = event.target.files[0];
                    if (file) {
                        // 上传图片
                        this.uploadProgressbg = true;
                        const formData = new FormData();
                        formData.append('file', file);
                        try {
                            const response = await axios.post(this.baseUrl + '/sys/common/upload', formData, {
                                headers: {
                                    'X-Access-Token': this.token, // 使用后端要求的头名
                                },
                                onUploadProgress: progressEvent => {

                                    // 改变进度条
                                    this.uploadProgress = Math.round((progressEvent.loaded / progressEvent.total) * 100);
                                },
                            });
                            console.log(response, 'responseresponse');
                            let fileUrl
                            if (response.data.success) {
                                fileUrl = response.data.message;
                                this.fiUrl = fileUrl;
                                wx.miniProgram.navigateBack({ delta: 1 })
                                wx.miniProgram.postMessage({ data: { message: '上传成功', url: fileUrl } })

                            } else {
                                wx.miniProgram.navigateBack({ delta: 1 })
                                wx.miniProgram.postMessage({ data: { message: '上传失败' } })
                            }
                        } catch (error) {
                            // 上传失败
                            console.error('Error uploading image:', error);
                            wx.miniProgram.navigateBack({ delta: 1 })
                            wx.miniProgram.postMessage({ data: '上传失败' })
                        } finally {

                            // 隐藏进度条
                            setTimeout(() => {
                                this.uploadProgress = 0;
                                this.uploadProgressbg = false;
                            }, 1500);
                        }
                    }
                },
            },
            template: `
                <div class="container">
                        <div class="upload-pannel">
                            <span class="pannel-title"></span>
                            <span class="file-select">
                                <input type="file" accept=".doc,.docx,.ppt,.pdf,.xls,.xlsx" @change="handleImageUpload">
                                <span class="upload-icon">
                                    <img src="upload.png" />
                                </span>
                                <span class="upload-text"></span>
                            </span>
                            <div class="upload-progress" v-if="uploadProgressbg == true">
                                <div class="progress-bar-container" :class="{ active: uploadProgressbg }">
                                    <div class="progress-bar" :style="{ width: uploadProgress + '%' }"></div>
                                <span class="progress-text" v-if="uploadProgressbg">{{ uploadProgress }}%</span>
                            </div>
                        </div>
                        <img v-if="fiUrl" :src="fiUrl" class="imgPreview">
                    </div>
                </div>`,
        });
    </script>
</body>

</html>

总结

这个组件由于包含了,文件上传进度条以及预览等功能,所有代码较多。核心代码主要在web-view页面和 fileUpload 文件下index.html
这里也是可以携带token进行文件上传的
web-view中message的到的就是上传接口返回的url
注意:web-view得有路由变化以及页面销毁等才会传参。

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值