<template>
  <div :class="['image-list-upload', prefixCls]">
    <!-- message alert -->
    <el-alert
      :title="res.msg"
      :type="res.success ? 'success' : 'error'"
      v-if="res.msg"
    >
    </el-alert>
    <!-- image-list -->
    <div :class="[`${prefixCls}-list`]">
      <div
        v-for="(item, index) in fileList"
        :class="[
          `${prefixCls}-item`,
          `${prefixCls}-item-${item.status}`,
          { [`${prefixCls}-item-unvalid`]: !item.valid },
        ]"
        :key="index"
      >
        <div :class="[`${prefixCls}-box`, `${prefixCls}-info`]">
          <img :src="item.url" :alt="item.name" />
        </div>
        <div :class="[`${prefixCls}-box`, `${prefixCls}-progress`]">
          <el-progress
            :percentage="item.percentage"
            :stroke-width="8"
            :show-text="false"
          ></el-progress>
        </div>
        <div :class="[`${prefixCls}-box `, `${prefixCls}-action`]">
          <i
            :class="['el-icon-delete-solid', `${prefixCls}-del`]"
            title="删除"
            @click="handleRemove(index)"
          ></i>
        </div>
        <div
          :class="[
            `${prefixCls}-tips`,
            {
              [`${prefixCls}-tips-success`]:
                item.valid && item.status === 'success',
              [`${prefixCls}-tips-error`]:
                !item.valid || item.status === 'error',
            },
          ]"
        >
          <span>{{ item.statusTips }}</span>
        </div>
      </div>

      <!-- upload add btn -->
      <div
        :class="[`${prefixCls}-item`]"
        @click="handleClick"
        title="点击添加图片"
      >
        <div :class="[`${prefixCls}-box`, `${prefixCls}-add`]">
          <i class="el-icon-upload"></i>
        </div>
        <input
          ref="fileInput"
          type="file"
          :class="[`${prefixCls}-input`]"
          @change="handleChange"
          :multiple="multiple"
        />
      </div>
    </div>
  </div>
</template>

<script>
import ajax from "../utils/ajax";

/**
 * 图片上传状态：
 * init 初始状态
 * ready 准备上传
 * uploading 正在上传
 * sucess 上传成功
 * error 上传失败
 *
 * 图片上传流程：
 * 1 选中图片
 * 2 校验合法性：大小，类型
 * 3 自动上传合法图片
 *
 * 说明：
 * 1 继发上传
 * 2 上传中不可删除图片，上传结束之后可以删除图片
 * 3 每一个图片上传都会触发开始上传、上传成功以及上传失败事件
 * 4 所有合法图片上传完成之后，以及删除图片时，都会触发完成事件。
 */

const prefixCls = "cc-list-upload";

export default {
  name: "image-list-upload",
  props: {
    action: {
      type: String,
      default: "/backend/uploadImage",
    },
    multiple: {
      type: Boolean,
      default: true,
    },
    headers: {
      type: Object,
      default() {
        return {};
      },
    },
    withCredentials: {
      type: Boolean,
      default: false,
    },
    data: {
      type: Object,
    },
    name: {
      type: String,
      default: "file",
    },
    maxSize: {
      type: Number,
      default: 10 * 1024,
    },
    rendFileUrl: {
      type: Function,
      default(fileInfo) {
        return fileInfo.url;
      },
    },
  },
  data() {
    return {
      prefixCls,
      fileList: [],
      format: ["jpg", "jpeg", "png"],
      limitNum: 10,
      res: {
        success: true,
        msg: "",
      },
      isUploading: false,
    };
  },
  computed: {
    limitSize() {
      let mn = this.maxSize / 1024;
      return mn >= 1 ? mn + "M" : this.maxSize + "Kb";
    },
  },
  methods: {
    initData() {
      this.fileList = [];
      this.uploadErrorFiles = [];
      this.res = { success: true, msg: "" };
      this.isUploading = false;
    },
    handleClick() {
      if (this.isUploading) return;
      this.$refs.fileInput.click();
    },
    handleChange(e) {
      this.initFileList(e.target.files);
    },
    initFileList(files) {
      if (!files) return;
      if (files.length + this.fileList.length > this.limitNum) {
        this.res = {
          success: false,
          msg: "文件数量不能超过" + this.limitNum,
        };
        return;
      }

      const postFiles = Array.prototype.slice.call(files);

      let fileList = [];
      postFiles.forEach((el, i) => {
        el.uid = Date.now() + i;
        let { statusTips, valid } = this.checkFileValid(el);
        fileList[i] = {
          size: el.size,
          name: el.name,
          type: el.type,
          percentage: 0,
          uid: el.uid,
          url: this.getFileUrl(el),
          fileData: {},
          files: el,
          status: "init",
          statusTips,
          valid,
        };
      });
      this.fileList = [...this.fileList, ...fileList];
      this.toUpload();
    },
    getFileUrl(file) {
      const URL = window.URL || window.webkitURL;
      if (URL && URL.createObjectURL) return URL.createObjectURL(file);
      return "";
    },
    getFileItem(uid) {
      return this.fileList.find((el) => el.uid === uid);
    },
    clearData() {
      this.$refs.fileInput.value = null;
      this.fileList = [];
    },
    checkFileValid(fileItem) {
      let result = {};
      result = this.checkFileSize(fileItem);
      if (!result.valid) return result;

      result = this.checkFileFormat(fileItem);
      if (!result.valid) return result;

      return { valid: true, statusTips: "" };
    },
    checkFileSize(fileItem) {
      const valid = fileItem.size / 1024 <= this.maxSize;
      return {
        statusTips: valid ? "" : "文件过大",
        valid,
      };
    },
    checkFileFormat(fileItem) {
      const formats = this.format.join("|");
      const reg = `\\.(${formats})$`;
      const pattern = new RegExp(reg, "i");

      const valid = pattern.test(fileItem.name);
      return {
        statusTips: valid ? "" : "格式不对",
        valid,
      };
    },
    handleRemove(index) {
      this.fileList.splice(index, 1);
      this.emitUploaded();
      if (!this.fileList.length) this.clearData();
    },
    // action
    async toUpload() {
      if (this.isUploading) return;
      this.isUploading = true;

      this.fileList.map((item) => {
        if (item.status === "init" && item.valid) item.status = "ready";
      });
      // 上传队列，继发上传，返回失败文件
      const queenFunc = async () => {
        for (let fileItem of this.fileList) {
          await this.upload(fileItem).catch(() => {});
        }
      };
      // 开启上传
      await queenFunc().catch(() => {});

      this.isUploading = false;
      this.emitUploaded();
    },
    async upload(fileItem) {
      if (fileItem.status !== "ready") return;
      this.$emit("upload-start", fileItem);

      fileItem.status = "uploading";
      let errorData = "";
      const resData = await this.post(fileItem).catch((error) => {
        errorData = error;
      });

      if (resData) {
        fileItem.status = "success";
        fileItem.statusTips = "上传成功";
        this.$emit("upload-success", resData, fileItem);
      } else {
        fileItem.status = "error";
        fileItem.statusTips = "上传失败";
        this.$emit("upload-error", errorData, fileItem);
      }
    },
    post(fileItem) {
      return new Promise((resolve, reject) => {
        ajax({
          headers: this.headers,
          withCredentials: this.withCredentials,
          file: fileItem.files,
          data: this.data,
          filename: this.name,
          action: this.GLOBAL.domain + this.action,
          onProgress: (e) => {
            fileItem.percentage = e.percent || 0;
          },
          onSuccess: (res) => {
            if (res.code === 0) {
              resolve(res.data);
            } else {
              reject({ res, msg: res.message || "上传失败" });
            }
          },
          onError: (err, res) => {
            reject({ res, msg: "网络错误" });
          },
        });
      });
    },
    emitUploaded() {
      this.$emit(
        "uploaded",
        this.fileList.filter((el) => el.status === "success" && el.valid)
      );
    },
  },
};
</script>
