<template>
  <div class="image-edit-upload">
    <div :class="[`${prefixCls}-main`]" v-show="!edit">
      <div :class="elPrefixCls">
        <div
          :class="classes"
          @click="handleClick"
          @drop.prevent="onDrop"
          @dragover.prevent="dragOver = true"
          @dragleave.prevent="dragOver = false"
        >
          <input
            ref="input"
            type="file"
            :class="[`${elPrefixCls}-input`]"
            @change="handleChange"
            :accept="accept"
          />
          <div :class="[`${prefixCls}-cover`]" v-if="imgUrl">
            <img :src="imgUrl" alt="default" />
          </div>
          <div :class="[`${prefixCls}-dcover`]" v-else>
            <i class="el-icon-user-solid"></i><br />
            <p>点击或拖拽图片到此处</p>
          </div>
        </div>
      </div>
    </div>
    <div :class="[`${prefixCls}-edit`]" v-show="file.url && edit">
      <div :class="[`${prefixCls}-edit-box`]">
        <img ref="editImage" :src="file.url" />
        <transition name="fade" v-if="file && file.showProgress">
          <div class="img-progress">
            <el-progress
              :show-text="false"
              :stroke-width="4"
              :percentage="file.percentage"
              :status="
                file.status === 'finished' && file.showProgress
                  ? 'success'
                  : null
              "
            ></el-progress>
          </div>
        </transition>
      </div>
      <div :class="[`${prefixCls}-edit-btns`]">
        <el-button type="error" @click="clearEdit">取消</el-button>
        <el-button type="primary" @click="editSave">确认</el-button>
      </div>
    </div>
    <p
      :class="[
        `${prefixCls}-tips`,
        { 'cc-tips-success': res.success, 'cc-tips-error': !res.success },
      ]"
      v-if="res.msg"
    >
      {{ res.msg }}
    </p>
  </div>
</template>

<script>
import ajax from "../utils/ajax";
import Cropper from "cropperjs";
import "cropperjs/dist/cropper.min.css";

const elPrefixCls = "el-upload";
const prefixCls = "cc-edit-upload";

const ratios = {
  double: 1 / 2,
  square: 1,
  rectangle: 3 / 4,
};

export default {
  name: "image-edit-upload",
  props: {
    action: {
      type: String,
      required: true,
    },
    headers: {
      type: Object,
      default() {
        return {};
      },
    },
    defImage: {
      type: String,
    },
    data: {
      type: Object,
    },
    name: {
      type: String,
      default: "file",
    },
    withCredentials: {
      type: Boolean,
      default: false,
    },
    format: {
      type: Array,
      default() {
        return ["jpg", "png"];
      },
    },
    accept: {
      type: String,
    },
    maxSize: {
      type: Number,
      default() {
        return 2 * 1024;
      },
    },
    ratioType: {
      type: String,
      default: "rectangle",
      validator(val) {
        return ["square", "rectangle", "double"].indexOf(val) > -1;
      },
    },
    onProgress: {
      type: Function,
      default() {
        return {};
      },
    },
    onSuccess: {
      type: Function,
      default() {
        return {};
      },
    },
    onError: {
      type: Function,
      default() {
        return {};
      },
    },
    onRemove: {
      type: Function,
      default() {
        return {};
      },
    },
    onPreview: {
      type: Function,
      default() {
        return {};
      },
    },
    onExceededSize: {
      type: Function,
      default() {
        return {};
      },
    },
    onFormatError: {
      type: Function,
      default() {
        return {};
      },
    },
  },
  data() {
    return {
      file: {},
      type: "drag",
      edit: false,
      cropper: false,
      imgUrl: this.defImage,
      prefixCls,
      elPrefixCls,
      dragOver: false,
      tempIndex: 1,
      res: {
        success: true,
        msg: "",
      },
    };
  },
  computed: {
    classes() {
      return [
        `${elPrefixCls}`,
        {
          [`${elPrefixCls}-dragger`]: this.type === "drag",
          [`${elPrefixCls}-dragOver`]: this.type === "drag" && this.dragOver,
        },
      ];
    },
    aspectRatio() {
      return ratios[this.ratioType];
    },
    limitSize() {
      const mn = this.maxSize / 1024;
      return mn >= 1 ? mn + "M" : this.maxSize + "Kb";
    },
    // accept() {
    //   let formats = this.format.map(item => {
    //     return "image/" + item;
    //   });
    //   return formats.join();
    // }
  },
  watch: {
    edit(value) {
      if (value) {
        this.$nextTick(function () {
          if (!this.$refs.editImage) {
            return;
          }
          this.cropper = new Cropper(this.$refs.editImage, {
            aspectRatio: this.aspectRatio,
            minCropBoxWidth: 120,
            minCropBoxHeight: 120 / this.aspectRatio,
            viewMode: 1,
          });
        });
      } else {
        if (this.cropper) {
          this.cropper.destroy();
          this.cropper = false;
        }
      }
    },
    defImage(val) {
      this.imgUrl = val;
    },
  },
  methods: {
    isIe() {
      let userAgent = navigator.userAgent;
      let isIE =
        userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1;
      let isEdge = userAgent.indexOf("Edge") > -1 && !isIE;
      let isIE11 =
        userAgent.indexOf("Trident") > -1 && userAgent.indexOf("rv:11.0") > -1;

      return isEdge || isIE11 || isIE;
    },
    handleClick() {
      this.res = { success: true, msg: "" };
      this.$refs.input.click();
    },
    handleChange(e) {
      const files = e.target.files;

      if (!files) return;

      this.uploadFiles(files);
      this.$refs.input.value = null;
    },
    onDrop(e) {
      this.res = { success: true, msg: "" };
      this.dragOver = false;
      this.uploadFiles(e.dataTransfer.files);
    },
    uploadFiles(files) {
      let postFiles = Array.prototype.slice.call(files);

      if (postFiles.length === 0) return;

      if (this.checkValid(postFiles[0])) {
        this.openEdit(postFiles[0]);
      }
    },
    checkValid(file) {
      // check format
      if (this.format.length) {
        const _file_format = file.name.split(".").pop().toLocaleLowerCase();
        const checked = this.format.some(
          (item) => item.toLocaleLowerCase() === _file_format
        );
        if (!checked) {
          this.res = {
            success: false,
            msg: "只支持文件格式为" + this.format.join("/"),
          };
          this.onFormatError(file);
          return false;
        }
      }

      // check maxSize
      if (this.maxSize) {
        if (file.size > this.maxSize * 1024) {
          this.res = {
            success: false,
            msg: "文件大小不能超过" + this.limitSize,
          };
          this.onExceededSize(file);
          return false;
        }
      }

      this.res = {
        success: true,
        msg: "",
      };
      return true;
    },
    openEdit(file) {
      this.initFile(file);
      this.$nextTick(function () {
        this.edit = true;
      });
    },
    initFile(file) {
      this.file = {
        status: "uploading",
        name: file.name,
        size: file.size,
        type: file.type,
        url: this.createUrl(file),
        response: {},
        percentage: 0,
        showProgress: true,
      };
    },
    createUrl(file) {
      let url = "";
      let URL = window.URL || window.webkitURL;
      if (URL && URL.createObjectURL) {
        url = URL.createObjectURL(file);
      }
      return url;
    },
    clearEdit() {
      this.edit = false;
    },
    editSave() {
      const { name, type } = this.file;
      const cropBoxSize = this.cropper.getCropBoxData();
      // 配置width，height之后会压缩原始图片
      const binStr = atob(
        this.cropper
          .getCroppedCanvas({
            width: cropBoxSize.width,
            height: cropBoxSize.height,
          })
          .toDataURL(type)
          .split(",")[1]
      );

      let arr = new Uint8Array(binStr.length);
      for (let i = 0; i < binStr.length; i++) {
        arr[i] = binStr.charCodeAt(i);
      }

      let file;
      if (this.isIe()) {
        file = new Blob([arr], { type });
      } else {
        file = new File([arr], name, { type });
      }

      this.post(file);
    },
    post(file) {
      ajax({
        headers: this.headers,
        withCredentials: this.withCredentials,
        file: file,
        data: this.data,
        filename: this.name,
        action: this.action,
        onProgress: (e) => {
          this.handleProgress(e);
        },
        onSuccess: (res) => {
          this.edit = false;
          this.handleSuccess(res, file);
        },
        onError: (err, response) => {
          this.edit = false;
          this.handleError(err, response);
        },
      });
    },
    handleProgress(e) {
      this.onProgress(e, this.file);
      this.file.percentage = parseFloat(e.percent) || 0;
    },
    handleSuccess(res, file) {
      this.file.status = "finished";
      this.file.response = res;
      this.res = {
        success: true,
        msg: "图片上传成功！",
      };
      this.file.url = this.createUrl(file);
      this.imgUrl = this.file.url;
      this.onSuccess(res, this.file);

      setTimeout(() => {
        this.file.showProgress = false;
      }, 300);
    },
    handleError(err, response) {
      this.file.status = "fail";
      this.res = {
        success: false,
        msg: `图片上传失败:${response.msg || "请求错误"}`,
      };
      this.onError(err, response, this.file);
    },
  },
};
</script>
