uniapp文件上传组件,支持图片、视频上传

html部分:

<template>
  <view class="annex-wrap">
    <view class="annex-box">
      <view class="lab">上传附件</view>
      <view class="file-list">
        <view
          class="file-list-item"
          v-for="(item, index) in dataList"
          :key="index"
        >
          <view class="file-list-item-inner">
            <!--图片-->
            <image
              :src="item.tempFilePath"
              class="upload-temp-img"
              v-if="item.fileType === '1'"
              mode="aspectFill"
              @click="toPreview(item)"
            ></image>
            <!--视频-->
            <view v-if="item.fileType === '2'" class="file-area">
              <view @click="toPreview(item)">
                <u-icon
                  name="play-circle-fill"
                  color="#1890FF"
                  size="80"
                ></u-icon>
                <view class="name">{{ item.fileName }}</view>
              </view>
            </view>
            <view @click="removeFile(index)" class="close-btn">
              <u-icon name="close" size="20"></u-icon>
            </view>
          </view>
        </view>
        <view class="file-list-item" @click="toShowChooseSheet()">
          <view class="file-list-item-inner upload-btn">
            <view>
              <u-icon name="plus" color="#929AA5" size="36"></u-icon>
              <view>{{ dataList.length }}/{{ maxFilesNum }}</view>
            </view>
          </view>
        </view>
        <u-empty
          text="暂无数据"
          mode="data"
          v-if="dataList.length === 0"
        ></u-empty>
      </view>
    </view>
    <u-action-sheet
      :list="uploadTypeList"
      v-model="showUploadTypeSheet"
      :tips="uploadTypeTips"
      :border-radius="20"
      @click="chooseUploadType"
    ></u-action-sheet>
    <u-popup
      v-model="showPreview"
      border-radius="14"
      mode="center"
      width="95%"
      closeable
      @close="closePreview"
    >
      <view class="preview-content">
        <image
          :src="previewItem.url"
          mode="aspectFit"
          style="width: 100%; height: 100%"
          v-if="previewItem.fileType === '1'"
        ></image>
        <video
          :src="previewItem.url"
          id="preview-video"
          :autoplay="false"
          controls
          :show-progress="true"
          :show-center-play-btn="true"
          v-if="previewItem.fileType === '2'"
        ></video>
      </view>
    </u-popup>
  </view>
</template>

 

css:

<style lang="scss" scoped>
.annex-wrap {
  background: #f5f7fa;

  .err-color {
    color: #f00;
  }

  .border-dashed {
    margin: 24rpx 0;
    border: 1px dashed #eff0f2;
  }

  .icon-arrow {
    margin-left: 12rpx;
  }

  .annex-box {
    background: #ffffff;
    border-radius: 10px;
    margin-top: 24rpx;
    color: #475569;
    margin-top: 24rpx;
    padding: 24rpx 34rpx;
    position: relative;
    overflow: hidden;
    position: relative;
    line-height: 1.6;

    .lab {
      font-size: 28rpx;
      color: #475569;
      padding: 10rpx;
    }

    &.select-box {
      padding: 0 34rpx;

      .u-form-item {
        color: #475569;
        padding: 16rpx 0;
      }
    }

    .mt-10 {
      margin-top: 10rpx;
    }

    .top {
      position: relative;

      .left {
        margin-right: 150rpx;
      }

      .title {
        font-size: 34rpx;
        color: #303133;
        font-weight: 550;
      }

      .desc {
        color: #929aa5;
        font-size: 24rpx;
      }

      .user-tag {
        margin-left: 24rpx;
        background-color: #f4f9ff;
        color: #1890ff;
        font-size: 22rpx;
        display: inline-block;
        border-radius: 6rpx;
        padding: 4rpx 20rpx;

        .text {
          margin-left: 10rpx;
        }
      }

      .right-icon {
        position: absolute;
        right: 0rpx;
        top: -20rpx;

        .icon {
          width: 170rpx;
          height: 170rpx;
        }
      }

      .result-items {
        overflow: hidden;

        .result-item {
          width: 33%;
          float: left;
          padding: 12rpx 0;
          text-align: center;
          position: relative;

          .red {
            color: #ff7676;
          }
        }

        .result-item:after {
          height: 60rpx;
          width: 1px;
          border-left: 1px solid #eff0f2;
          content: "";
          display: inline-block;
          position: absolute;
          right: 0;
          top: 50%;
          transform: translateY(-50%);
        }

        .result-item:nth-child(3n):after {
          display: none;
        }

        .name {
          color: #929aa5;
          font-size: 24rpx;
        }

        .num {
          color: #303133;
          font-size: 30rpx;
        }
      }
    }
  }

  .file-list-item {
    width: 31%;
    padding: 15.5% 0 15.5% 0;
    margin-right: 3.5%;
    position: relative;
    display: inline-block;
    overflow: hidden;

    &-inner {
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      width: 100%;
      height: 100%;
      display: flex;

      .upload-temp-img {
        max-width: 100%;
        max-height: 100%;
        border-radius: 10rpx;
      }
    }

    .upload-btn {
      border: 2px dashed #e2e8f0;
      border-radius: 10rpx;
      background: #fff;
      text-align: center;
      font-size: 26rpx;
      color: #929aa5;
      cursor: pointer;
    }

    &:nth-child(3n) {
      margin-right: 0;
    }

    .file-area {
      position: relative;
      display: flex;
      align-items: center;
      justify-content: center;
      background: rgba(77, 151, 255, 0.05);
      text-align: center;
      width: 100%;
      border-radius: 10rpx;

      .name {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        width: 160rpx;
      }

      .icon-file {
        width: 60rpx;
        height: 74rpx;
      }

      video {
        width: 100%;
        height: 100%;
      }
    }

    .close-btn {
      position: absolute;
      right: 0;
      top: 0;
      background: rgba(71, 85, 105, 0.3);
      border-radius: 0px 10rpx 0px 10rpx;
      display: inline-block;
      width: 40rpx;
      height: 40rpx;
      cursor: pointer;
      text-align: center;
      color: #fff;
      z-index: 9;
    }
  }
}

.preview-content {
  width: 100%;
  height: 90vh;
  padding-top: 80rpx;
  padding-bottom: 20rpx;
  text-align: center;
  display: flex;
  align-items: center;

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

 js:

  data() {
    return {
      showPreview: false,
      previewItem: {
        url: "",
        fileType: "",
      },
      dataList: [],
      uploadTypeList: [
        {
          text: "图片",
        },
        {
          text: "视频",
        },
      ],
      uploadTypeTips: {
        text: "请选择上传文件类型",
        color: "#909399",
        fontSize: 32,
      },
      showUploadTypeSheet: false,
      maxFilesNum: 20, //最大上传数
    };
  },
  methods: {
    // 获取附件列表-查看或编辑时候用
    getFileList() {
      let data = {
        id: id,
      };
      getFileList(data).then((res) => {
        if (res.code === 200) {
          this.dataList = res.result;
          this.dataList.forEach((item) => {
            item,
              (item.tempFilePath = `域名/file/downloadFile?filePath=${item.filePath}`);
            //tempFilePath:文件的临时地址,或者拼一个获取文件临时地址的接口,临时文件地址才可以预览
          });
        }
      });
    },
    // 打开预览
    toPreview(item) {
      if (item.fileType === "1") {
        this.showPreview = true;
        // 添加延时加载 避免popup不显示
        setTimeout(() => {
          this.previewItem = {
            url: item.tempFilePath,
            fileType: item.fileType,
          };
        }, 500);
      } else if (item.fileType === "2") {
        this.showPreview = true;
        this.downloadVideo(item.tempFilePath);
      }
    },
    // 关闭预览
    closePreview() {
      this.previewItem = {
        url: "",
        fileType: "",
      };
    },
    // 移除文件
    removeFile(index) {
      let that = this;
      uni.showModal({
        title: "提示",
        content: "确定删除此项吗?",
        success: function (res) {
          if (res.confirm) {
            that.dataList.splice(index, 1);
          }
        },
      });
    },
    // 上传图片
    chooseImage() {
      let that = this;
      uni.chooseImage({
        count: this.maxFilesNum - this.dataList.length, //默认100
        sizeType: ["original", "compressed"], //可以指定是原图还是压缩图,默认二者都有
        sourceType: ["album", "camera"],
        success: (res) => {
          const tempFilePaths = res.tempFilePaths;
          let imgArr = [];
          //   图片批量上传
          for (let i = 0; i < tempFilePaths.length; i++) {
            let obj = new Object();
            obj.name = `files` + i;
            obj.uri = tempFilePaths[i];
            imgArr.push(obj);
            obj = null;
          }
          uni.showLoading();
          uni.uploadFile({
			//url:上传文件接口,返回文件名、文件类型、文件大小、文件地址等
            url: `域名/file/uploadFile`,
            files: imgArr,
            success: (uploadRes) => {
              let data = JSON.parse(uploadRes.data);
              if (data.code === 200) {
                (data.result || []).forEach((item, index) => {
                  that.dataList.push({
                    fileType: "1",//1图片,2视频
                    fileName: item.fileName,
                    filePath: item.filePath,
                    fileSize: item.fileSize,
                    fileSuffix: item.fileSuffix,
                    tempFilePath: tempFilePaths[index],
                  });
                });
              } else {
                uni.showToast({
                  icon: "none",
                  title: "上传失败",
                });
              }
            },
            complete: () => {
              uni.hideLoading();
            },
          });
        },
      });
    },
    // 上传视频
    chooseVideo() {
      let that = this;
      uni.chooseVideo({
        sourceType: ["album", "camera"],
        success: (res) => {
          const tempFilePath = res.tempFilePath;
          uni.showLoading();

          uni.uploadFile({
            //url:上传文件接口,返回文件名、文件类型、文件大小、文件地址等
            url: `域名/file/uploadFile`,
            filePath: tempFilePath,
            name: "file",
            success: (uploadRes) => {
              let data = JSON.parse(uploadRes.data);
              if (data.code === 200) {
                that.dataList.push({
                  fileType: "2",
                  fileName: data.result.fileName,
                  filePath: data.result.filePath,
                  fileSize: data.result.fileSize,
                  fileSuffix: data.result.fileSuffix,
                  tempFilePath: `域名/file/downloadFile?filePath=${data.result.filePath}`,
                  //tempFilePath:文件的临时地址,或者拼一个获取文件临时地址的接口
                });
              } else {
                uni.showToast({
                  icon: "none",
                  title: "上传失败",
                });
              }
            },
            complete: () => {
              uni.hideLoading();
            },
          });
        },
      });
    },
	//查看-下载视频
    downloadVideo(url) {
      let that = this;
      uni.showLoading({
        title: "加载中",
      });

      uni.downloadFile({
        url: url, //仅为示例,并非真实的资源
        success: (res) => {
          if (res.statusCode === 200) {
            this.previewItem = {
              url: res.tempFilePath,
              fileType: "2",//1图片,2视频
            };

            // 自动播放
            setTimeout(() => {
              let videoContxt = uni.createVideoContext("preview-video", that);
              videoContxt.play();
            }, 500);
          }
          uni.hideLoading();
        },
        error: (e) => {
          uni.hideLoading();
        },
      });
    },
    // 选择上传文件类型
    chooseUploadType(index) {
      if (index === 0) {
        // 图片
        this.chooseImage();
      } else if (index === 1) {
        // 视频
        this.chooseVideo();
      }
    },
    //  打开文件上传选择类型弹窗
    toShowChooseSheet() {
      if (this.dataList.length >= this.maxFilesNum) {
        uni.showToast({
          title: "最多只能上传20个附件",
          icon: "none",
        });
        return false;
      } else {
        this.showUploadTypeSheet = true;
      }
    },
  },

注意:图片或者视频预览的时候,需要一个文件的临时地址

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐