Skip to content

多图上传推拽排序

前期准备

注意

  1. 安装 npm i vuedraggable@next -S

  2. 上传的接口里面 一定要写好 headers: {"Content-Type": "multipart/form-data",},

组件代码

vue
<template>
  <div>
    <el-upload
      v-if="(!multiple && myFileList.length < 1) || multiple"
      ref="upload"
      class="upload-demo"
      accept="image/png, image/jpg, image/jpeg, image/gif"
      action=""
      :show-file-list="false"
      :list-type="listType"
      :multiple="multiple"
      :before-upload="beforeUpload"
      :http-request="upload"
      :file-list="myFileList"
    >
      <el-button size="small" type="primary" plain>{{ btnText }}</el-button>
    </el-upload>
    <draggable
      v-show="myFileList.length > 0"
      v-model="myFileList"
      :disabled="multiple === false ? true : false"
      class="my-file-list"
      item-key="index"
    >
      <template #item="{ element, index }" :key="index">
        <div>
          <div class="my-file-list-item">
            <span
              v-if="element.status === 'uploading'"
              class="el-icon-document"
            ></span>
            <div v-if="element.status === 'success'" class="item-img">
              <img :src="element.url" />
            </div>
            <div class="item-pro-name">
              <div class="name">{{ element.name }}</div>
              <el-progress
                v-if="element.status === 'uploading'"
                :stroke-width="3"
                :percentage="element.percentage"
              ></el-progress>
            </div>
            <span class="el-icon-close" @click="removeFileByUid(element.uid)"
              >X</span
            >
          </div>
        </div>
      </template>
    </draggable>
  </div>
</template>

<script setup>
import draggable from "vuedraggable";
import { defineProps, defineEmits } from "vue";
const emits = defineEmits(["update:value"]);
const props = defineProps({
  value: {
    type: [Array, String],
    required: true,
  },
  multiple: {
    type: Boolean,
    default: false,
  },
  api: {
    type: Function,
    defult: () => {},
  },
  listType: {
    type: String,
    default: "picture",
  },
  btnText: {
    type: String,
    default: "选择文件",
  },
  module: {
    type: String,
    default: "meeting",
  },
});
const myFileList = ref([]);

// 上传以前
const beforeUpload = (file) => {
  if (!props.multiple) {
    myFileList.value = [];
  }
  myFileList.value.push({
    uid: file.uid,
    name: file.name,
    url: "",
    status: "uploading",
    percentage: 0,
  });
};

// 上传文件
const upload = (obj) => {
  let data = new FormData();

  data.append("image", obj.file);
  // 你自己的其他参数
  data.append("module", props.module);

  let api = props.api ? props.api : "你自己引入的上传API方法";
  api(data, (progress) => {
    myFileList.value.map((item, index) => {
      if (item.uid === data.get("image").uid) {
        item.percentage = Math.ceil((progress.loaded / progress.total) * 99);
      }
    });
  })
    .then((res) => {
      if (res.code === 200) {
        myFileList.value.map((item, index) => {
          if (item.uid == obj.file.uid) {
            myFileList.value[index] = {
              uid: obj.file.uid,
              name: obj.file.name,
              url: res.data.path_real,
              status: "success",
              percentage: 100,
            };
          }
        });
        console.log(100);
        console.log(myFileList.value);
      } else {
        removeFileByUid(obj.file.uid);
      }
    })
    .catch(() => {
      removeFileByUid(obj.file.uid);
    });
};

// 删除文件
const removeFileByUid = (uid) => {
  myFileList.value.map((item, index) => {
    if (item.uid == uid) {
      myFileList.value.splice(index, 1);
    }
  });
};

// 设置文件列表
const setFileList = (arr) => {
  if (!arr) {
    myFileList.value = [];
  }
  if (
    myFileList.value.length == 0 ||
    (myFileList.value.length > 0 && myFileList.value[0].uid <= 9999999999)
  ) {
    myFileList.value = [];
    if (typeof arr === "string") {
      arr = arr != "" ? [arr] : [];
    }
    arr.forEach((item) => {
      myFileList.value.push({
        uid: Math.ceil(Math.random() * 1000000000),
        name: item.substr(item.lastIndexOf("/") + 1),
        url: item,
        status: "success",
        percentage: 100,
      });
    });
  }
};

watch(
  () => {
    return props.value;
  },
  (arr, oldArr) => {
    if (
      (typeof arr === "string" && arr === oldArr) ||
      (typeof arr !== "string" && oldArr && arr.join("") === oldArr.join(""))
    ) {
      return false;
    }
    if (typeof arr === "string" && arr != "") {
      arr = [arr];
    }
    setFileList(arr);
  },
  {
    deep: true,
    immediate: true,
  }
);
watch(
  () => {
    return myFileList.value;
  },
  (val) => {
    let data;
    if (!props.multiple) {
      data = val.length > 0 ? val[0].url : "";
    } else {
      data = [];
      for (let i in val) {
        data.push(val[i].url);
      }
    }
    emits("update:value", data);
  },
  {
    deep: true,
    immediate: true,
  }
);

onMounted(() => {
  if (props.value.length > 0) {
    setFileList(props.value);
  }
});
</script>

<style lang="scss" scoped>
/*.upload-demo {
    margin-bottom: 20px;
  }*/
.my-file-list {
  margin-top: 20px;
  width: 100%;
  height: auto;
  overflow: hidden;
  .my-file-list-item {
    width: 360px;
    height: 90px;
    margin-bottom: 10px;
    display: flex;
    align-items: center;
    flex-direction: row;
    justify-content: space-between;
    border: 1px solid #ccc;
    border-radius: 10px;
    padding-left: 10px;
    .el-icon-document {
      font-size: 50px;
    }
    .item-img {
      width: 70px;
      height: 70px;
      min-width: 70px;
      border: 1px solid #ccc;
      img {
        width: 100%;
        height: 100%;
        display: block;
        object-fit: cover;
      }
    }
    .item-pro-name {
      width: 240px;
      line-height: 14px;
      .name {
        width: 100%;
        height: 15px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
      ::v-deep(.el-progress) {
        margin-top: 10px;
        .el-progress-bar {
          width: 100%;
        }
      }
    }
    .el-icon-close {
      font-size: 18px;
      cursor: pointer;
      align-self: flex-start;
      margin: 5px 5px 0 0;
    }
  }
}
</style>

父元素调用

vue
<template>
  <div>
    <div>多图上传</div>
    <UploadVue3
      v-model:value="form.image"
      module="meeting"
      :multiple="multipleflag"
      :btnText="btnText"
      :api="systemUploadMoreImageAPI"
    ></UploadVue3>
  </div>
</template>

<script setup>
import UploadVue3 from "@/components/Upload/UploadImg3";
import {
  systemUploadImageAPI,
  systemUploadMoreImageAPI,
} from "@/api/study/study.js";
const form = ref({
  image: "",
});
watch(
  () => {
    return form.value;
  },
  (newval, oldval) => {
    console.log("watch里面的");
    console.log(newval);
  },
  { deep: true, immediate: true }
);

// 是否多图
const multipleflag = ref(true);

// 上传按钮的文字

const btnText = ref("多图上传");
</script>

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