<template>
  <div>
    <div class="header">
      <span class="title">{{ type == '1' ? $t('modelManage.addModelFiles') : $t('modelManage.uploadModelFiles')
      }}</span>
    </div>
    <div class="content ui form">
      <div class="guide-c" v-if="type != '1'">
        <div class="step">
          <div class="num">1</div>
          <div class="txt">{{ $t('modelManage.createModel') }}</div>
        </div>
        <div class="line"></div>
        <div class="step focused">
          <div class="num">2</div>
          <div class="txt">{{ $t('modelManage.uploadModelFiles') }}</div>
        </div>
      </div>
      <div class="row-c">
        <div class="row">
          <div class="r-title"><label class="required">{{ $t('modelManage.modelName') }}</label></div>
          <div class="r-content">
            <el-input size="medium" class="input-disabled" v-model="state.name"
              :placeholder="$t('modelManage.pleaseInputModelName')" readonly>
            </el-input>
          </div>
        </div>
        <div class="row" style="align-items:flex-start;">
          <div class="r-title"><label class="required">{{ $t('modelManage.fileUpload') }}</label></div>
          <div class="r-content">
            <div style="position:relative">
              <form class="dropzone" ref="dropzoneRef">
                <div class="dropzon-err-tips ui red message" v-show="showUploadErr" style="display:none;margin:2.5rem">
                  {{ uploadErrTxt }}</div>
              </form>
              <div class="not-allowed-placeholder" v-show="uploading"></div>
            </div>
          </div>
        </div>
        <div class="row" style="margin-top:10px">
          <div class="r-title"><label></label></div>
          <div class="r-content">
            <el-button size="medium" class="green" @click="submit" :disabled="uploading">{{ $t('modelManage.upload') }}
            </el-button>
            <el-button size="medium" @click="cancel">{{ $t('modelManage.cancel') }}</el-button>
          </div>
        </div>
        <div class="row" style="align-items:flex-start;">
          <div class="r-title"><label>{{ $t('modelManage.uploadStatus') }}：</label></div>
          <div class="r-content">
            <div v-for="(item, index) in uploadFiles" :key="item.upload.uuid" class="datast-upload-progress">
              <span class="dataset-name nowrap" :title="item.name">{{ item.name }}</span>
              <div class="dataset-progress">
                <el-progress :text-inside="true" :stroke-width="14" :percentage="uploadStatusList[index].progress">
                </el-progress>
              </div>
              <div class="dataset-status nowrap">
                <div class="status-flex">
                  <i v-if="uploadStatusList[index].infoCode === 1 || uploadStatusList[index].infoCode === 2"
                    class="ri-close-circle-line failed"></i>
                  <i v-if="uploadStatusList[index].infoCode === 0" class="ri-checkbox-circle-line success"></i>
                  <span>{{ uploadStatusList[index].status }}</span>
                  <el-tooltip v-if="uploadStatusList[index].infoCode === 1" class="item" effect="dark" placement="top">
                    <div slot="content"> {{ uploadStatusList[index].failedInfo }} </div>
                    <i style="font-size: 16px; margin-left: 0.5rem; cursor: pointer" class="ri-question-fill"></i>
                  </el-tooltip>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>

import 'dropzone/dist/dropzone.css';
import Dropzone from 'dropzone';
import SparkMD5 from "spark-md5";
import { getModelInfoByName, getChunks, getNewMultipart, getMultipartUrl, setCompleteMultipart } from '~/apis/modules/modelmanage';
import { getUrlSearchParams } from '~/utils';

Dropzone.autoDiscover = false;

const uploadChunkSize = 1024 * 1024 * 64;
const md5ChunkSize = 1024 * 1024 * 64;
const maxFileSize = 10;
const maxModelFilesSize = window.MAX_MODEL_SIZE || 200 * 1024 * 1024 * 1024; // 200 GB

export default {
  data() {
    return {
      dropzoneHandler: null,
      type: '0', // 1-修改，其它-新增
      state: {
        type: '',
        id: '',
        name: '',
        version: '',
        engine: '',
        label: '',
        description: '',
        size: 0,
      },
      originData: null,
      showUploadErr: false,
      uploadErrTxt: '',

      uploadFiles: [],
      uploadLength: 0,
      uploadSuccessLength: 0,
      uploadStatusList: [],

      uploading: false,
    };
  },
  components: {},
  methods: {
    initModelData() {
      const urlParams = getUrlSearchParams();
      if (urlParams.name && urlParams.id) {
        this.type = urlParams.type || '0';
        this.state.name = urlParams.name;
        this.state.id = urlParams.id;
        this.loading = true;
        getModelInfoByName({
          repo: location.pathname.split('/').slice(0, 3).join('/'),
          name: urlParams.name,
          id: urlParams.id
        }).then(res => {
          this.loading = false;
          const list = res.data;
          if (list && list.length) {
            const data = list[0];
            this.state.type = data.type;
            this.state.id = data.id;
            this.state.name = data.name;
            this.state.version = data.version;
            this.state.engine = data.engine.toString();
            this.state.label = data.label;
            this.state.description = data.description;
            this.state.size = data.size || 0;
            this.originData = data;
          } else {
            this.cancel();
          }
        }).catch(err => {
          this.loading = false;
          console.log(err);
          this.cancel();
        });
      } else {
        this.cancel();
      }
    },
    initDropZone() {
      this.dropzoneHandler = new Dropzone(this.$refs.dropzoneRef, {
        url: '/',
        maxFiles: 10,
        parallelUploads: 20,
        uploadMultiple: true,
        maxFilesize: maxModelFilesSize,
        timeout: 0,
        addRemoveLinks: true,
        autoProcessQueue: false,
        dictDefaultMessage: this.$t('modelManage.modelFileUploadDefaultTips'),
        dictFileTooBig: this.$t('modelManage.fileIstoBig'),
        dictRemoveFile: this.$t('modelManage.removeFile'),
        dictMaxFilesExceeded: this.getDefaultErrTxt(),
      });
      this.dropzoneHandler.on("addedfile", file => {
        this.checkFiles(file);
      });
      this.dropzoneHandler.on("removedfile", file => {
        this.checkFiles();
      });
    },
    getDefaultErrTxt() {
      return this.$t('modelManage.modelFileUploadErrTips', { size: (maxModelFilesSize / (1024 * 1024 * 1024)).toFixed(2) });
    },
    showUploadErrInfo(state, info) {
      if (state) {
        this.uploadErrTxt = info;
        this.showUploadErr = true;
      } else {
        this.uploadErrTxt = '';
        this.showUploadErr = false;
      }
    },
    checkFiles(file, countStay) {
      const fileList = this.dropzoneHandler.getAcceptedFiles();
      const filesSize = fileList.reduce((acc, item, index) => acc + item.size, 0) + (file && !countStay ? file.size : 0);
      const filesCount = fileList.length + (file && !countStay ? 1 : 0);
      const allfilesSize = filesSize + this.state.size;
      if (file && filesCount > maxFileSize) {
        this.dropzoneHandler.removeFile(file);
        this.showUploadErrInfo(true, this.getDefaultErrTxt());
        return false;
      }
      if (filesCount > maxFileSize || allfilesSize > maxModelFilesSize) {
        this.showUploadErrInfo(true, this.getDefaultErrTxt());
        return false;
      }
      if (file && file.name.length > 128) {
        this.showUploadErrInfo(true, this.$t('modelManage.modelFileNameTips'));
        this.uploadError(file, this.$t('modelManage.modelFileNameTips'));
        return false;
      }
      this.showUploadErrInfo(false);
      return true;
    },
    resetFileStatus() {
      this.uploadFiles = [];
      this.uploadLength = 0;
      this.uploadSuccessLength = 0;
      this.uploadStatusList = [];
    },
    updateFileStatus(file, status, progress, infoCode, failedInfo = "") {
      this.uploadStatusList.forEach((item, index) => {
        if (item.uploadUuid === file.upload.uuid) {
          this.uploadStatusList[index].status = status;
          this.uploadStatusList[index].progress = progress;
          this.uploadStatusList[index].infoCode = infoCode;
          this.uploadStatusList[index].failedInfo = failedInfo;
        }
      });
    },
    calcFileMd5(file) {
      const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
      const chunkSize = md5ChunkSize;
      const chunks = Math.ceil(file.size / chunkSize);
      const spark = new SparkMD5.ArrayBuffer();
      const fileReader = new FileReader();
      file.totalChunkCounts = chunks;
      if (file.size == 0) {
        file.totalChunkCounts = 1;
      }
      let currentChunk = 0;
      this.uploadStatusList.push({
        uploadUuid: file.upload.uuid,
        name: file.name,
        status: this.$t('modelManage.calcFileMd5'),
        progress: 0,
        infoCode: 3,
      });
      return new Promise((resolve, reject) => {
        fileReader.onload = function (e) {
          spark.append(e.target.result);
          currentChunk++;
          if (currentChunk < chunks) {
            loadNext();
          } else {
            const md5 = spark.end();
            spark.destroy();
            file.uniqueIdentifier = md5;
            resolve(md5);
          }
        };
        fileReader.onerror = function (e) {
          console.warn(file.name + ': calcFileMd5 went wrong.');
          reject(e);
        };

        function loadNext() {
          const start = currentChunk * chunkSize;
          const end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
          fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
        }
        loadNext();
      });
    },
    getChunksInfo(file) {
      return getChunks({
        md5: file.uniqueIdentifier,
        type: this.state.type,
        modeluuid: this.state.id,
        file_name: file.name,
        scene: 'model',
      }).then(res => {
        const data = res.data;
        file.uploadID = data.uploadID;
        file.uuid = data.uuid;
        file.uploaded = data.uploaded;
        file.chunks = data.chunks;
        file.attachID = data.attachID;
        file._modelUuid = data.modeluuid;
        file._modelName = data.modelName;
        file.realName = data.fileName;
        return file;
      }).catch(err => {
        console.info('getChunksInfo', err);
        return err;
      });
    },
    newUpload(file) {
      return getNewMultipart({
        totalChunkCounts: file.totalChunkCounts,
        md5: file.uniqueIdentifier,
        size: file.size,
        fileType: file.type,
        type: this.state.type,
        file_name: file.name,
        scene: 'model',
        modeluuid: file.modelUuid,
      }).then(res => {
        const data = res.data;
        file.uploadID = data.uploadID;
        file.uuid = data.uuid;
        if (file.uploadID && file.uuid) {
          file.chunks = '';
          this.breakpointUpload(file);
        } else {
          this.uploadError(file, info);
          this.updateFileStatus(file, this.$t('modelManage.uploadFailed'), 0, 2);
        }
        return file;
      }).catch(err => {
        console.log('getNewMultipart', err);
        this.uploadError(file, info);
        this.updateFileStatus(file, this.$t('modelManage.uploadFailed'), 0, 2);
        return err;
      });
    },
    breakpointUpload(file) {
      const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
      const fileReader = new FileReader();
      const time = new Date().getTime();
      const chunkSize = uploadChunkSize;
      const chunks = Math.ceil(file.size / chunkSize);
      const _this = this;
      let currentChunk = 0;
      const successChunks = [];
      const successParts = file.chunks.split(",");
      for (let i = 0; i < successParts.length; i++) {
        successChunks[i] = successParts[i].split("-")[0];
      }
      const urls = [];
      const etags = [];
      const checkSuccessChunks = () => {
        const index = successChunks.indexOf((currentChunk + 1).toString());
        if (index == -1) {
          return false;
        }
        return true;
      }

      const getUploadChunkUrl = async (currentChunk, partSize) => {
        const res = await getMultipartUrl({
          uuid: file.uuid,
          uploadID: file.uploadID,
          size: partSize,
          chunkNumber: currentChunk + 1,
          type: _this.state.type,
          file_name: file.name,
          scene: 'model',
        });
        urls[currentChunk] = res.data.url;
      };

      const uploadMinioNewMethod = async (url, e) => {
        const xhr = new XMLHttpRequest();
        xhr.open("PUT", url, false);
        if (_this.state.type == 0) {
          xhr.setRequestHeader("Content-Type", "text/plain");
          xhr.send(e.target.result);
          const etagValue = xhr.getResponseHeader("etag");
          etags[currentChunk] = etagValue;
        } else if (_this.state.type == 1) {
          xhr.setRequestHeader("Content-Type", "");
          xhr.send(e.target.result);
          const etagValue = xhr.getResponseHeader("ETag");
          etags[currentChunk] = etagValue;
        }
      }

      const uploadChunk = async (e) => {
        try {
          if (!checkSuccessChunks()) {
            const start = currentChunk * chunkSize;
            const partSize = start + chunkSize >= file.size ? file.size - start : chunkSize;
            // 获取分片上传url
            await getUploadChunkUrl(currentChunk, partSize);
            if (urls[currentChunk] != '') {
              await uploadMinioNewMethod(urls[currentChunk], e);
              if (etags[currentChunk] != '') {
              } else {
                console.log("上传到minio uploadChunk etags[currentChunk] == ''"); // TODO
              }
            } else {
              console.log("uploadChunk urls[currentChunk] != ''"); // TODO
            }
          }
        } catch (error) {
          console.log(error);
        }
      }

      const completeUpload = async () => {
        return await setCompleteMultipart({
          uuid: file.uuid,
          uploadID: file.uploadID,
          file_name: file.name,
          size: file.size,
          type: _this.state.type,
          scene: 'model',
          modeluuid: file.modelUuid,
        });
      }

      function loadNext() {
        const start = currentChunk * chunkSize;
        const end = start + chunkSize >= file.size ? file.size : start + chunkSize;
        fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
      }

      fileReader.onload = async (e) => {
        try {
          await uploadChunk(e);
          fileReader.abort();
          currentChunk++;
          if (currentChunk < chunks) {
            console.log(`第${currentChunk}个分片上传完成, 开始第${currentChunk + 1}/${chunks}个分片上传`);
            this.updateFileStatus(file, this.$t('modelManage.uploading'), Number(((currentChunk / chunks) * 100).toFixed(2)), 3);
            loadNext();
          } else {
            try {
              await completeUpload();
              console.log(`文件上传完成：${file.name} \n分片：${chunks} 大小:${file.size} 用时：${(new Date().getTime() - time) / 1000} s`);
              this.uploadLength++;
              this.uploadSuccessLength++;
              this.updateFileStatus(file, this.$t('modelManage.uploadSuccess'), 100, 0);
              this.uploadSuccess(file);
            } catch (err) {
              const info = this.$t('modelManage.uploadFailed');
              console.log(info, file)
              this.uploadLength++;
              this.uploadError(file, info);
              this.updateFileStatus(file, info, Number(((currentChunk / chunks) * 100).toFixed(2)) - 1, 2);
            }
          }
        } catch (err) {
          console.log(err);
          const info = this.$t('modelManage.uploadFailed');
          console.log(info, file)
          this.uploadLength++;
          this.uploadError(file, info);
          this.updateFileStatus(file, info, Number(((currentChunk / chunks) * 100).toFixed(2)) - 1, 2);
        }
      };
      console.log("上传分片...");
      loadNext();
    },
    uploadError(file, info) {
      file.previewTemplate.querySelector('.dz-success-mark').style.opacity = 0;
      file.previewTemplate.querySelector('.dz-error-mark').style.opacity = 1;
      file.previewTemplate.querySelector('.dz-error-message span').innerHTML = info;
      file.previewTemplate.querySelector(".dz-error-message").style.display = 'block';
      file.previewTemplate.querySelector(".dz-details").onmouseover = () => {
        file.previewTemplate.querySelector('.dz-error-message').style.opacity = 1;
      };
      file.previewTemplate.querySelector(".dz-details").onmouseout = () => {
        file.previewTemplate.querySelector('.dz-error-message').style.opacity = 0;
      };
      this.uploadFinishCheck(file);
    },
    uploadSuccess(file) {
      file.previewTemplate.querySelector('.dz-error-mark').style.opacity = 0;
      file.previewTemplate.querySelector('.dz-error-message span').innerHTML = '';
      file.previewTemplate.querySelector('.dz-success-mark').style.opacity = 1;
      file.previewTemplate.querySelector(".dz-error-message").style.display = 'none';
      file.previewTemplate.querySelector(".dz-details").onmouseover = null;
      file.previewTemplate.querySelector(".dz-details").onmouseout = null;
      this.uploadFinishCheck(file);
    },
    uploadFinishCheck(file) {
      console.log('uploadFinishCheck', file, this.uploadLength, '/', this.uploadFiles.length);
      if (this.uploadLength === this.uploadFiles.length && this.uploadFiles.length != 0) {
        console.log('All file has finish, success ' + this.uploadSuccessLength);
        this.uploading = false;
        if (this.uploadSuccessLength == this.uploadLength) {
          window.setTimeout(() => {
            this.goDetail();
          }, 1000);
        } else {
          if (this.uploadSuccessLength > 0) {
            this.initModelData();
          }
        }
      }
    },
    submit() {
      const fileList = this.dropzoneHandler.getAcceptedFiles();
      if (!fileList.length) return;
      for (let i = 0, iLen = fileList.length; i < iLen; i++) {
        if (!this.checkFiles(fileList[i], true)) return;
      }
      this.resetFileStatus();
      this.uploadFiles = fileList;
      this.uploading = true;
      for (let i = 0, iLen = fileList.length; i < iLen; i++) {
        const file = fileList[i];
        file.modelUuid = this.state.id;
        file.modelName = this.state.name;
        this.calcFileMd5(file).then(res => { // 计算MD5
          this.getChunksInfo(file).then(res => { // 获取Chunk信息
            if (file.uploadID == '' || file.uuid == '') { // 未上传过
              this.newUpload(file);
            } else if (file.uploaded == '1') { // 已上传成功 
              if (file.attachID == '0') { // 删除数据集记录，未删除文件
                // await addAttachment(file);
                console.log(`file.attachID == '0'`);
              }
              this.uploadLength++;
              // 同一模型上传同一个文件
              if (file._modelUuid) {
                const info = `${this.$t('modelManage.fileHasAlreadyInTheModel')} ${file._modelName}`;
                this.uploadError(file, info);
                this.updateFileStatus(file, this.$t('modelManage.uploadFailed'), 0, 1, info);
              } else { // 秒传
                this.uploadSuccessLength++;
                this.updateFileStatus(file, this.$t('modelManage.uploadSuccess'), 100, 0);
                this.uploadSuccess(file);
              }
              console.log(file.name, '文件处理完成');
            } else { // 断点续传
              this.breakpointUpload(file);
            }
          }).catch(err => {
            console.info('getChunksInfo', err);
            this.uploadLength++;
            this.uploadError(file, this.$t('modelManage.uploadFailed'));
            this.updateFileStatus(file, this.$t('modelManage.uploadFailed'), 0, 2);
          });
        }).catch(err => {
          console.info('calcFileMd5', err);
          this.uploadLength++;
          this.uploadError(file, this.$t('modelManage.uploadFailed'));
          this.updateFileStatus(file, this.$t('modelManage.uploadFailed'), 0, 2);
        });
      }
    },
    cancel() {
      if (this.type == '1') {
        this.goDetail();
        return;
      }
      const list = window.location.href.split('/');
      list.pop();
      list.push('show_model');
      window.location.href = list.join('/');
    },
    goDetail() {
      const list = window.location.href.split('/');
      list.pop();
      list.push('show_model_info');
      window.location.href = list.join('/') + '?name=' + this.state.name;
    }
  },
  mounted() {
    this.initModelData();
    this.initDropZone();
  },
  beforeDestroy() {
  },
};
</script>

<style scoped lang="less">
.header {
  height: 45px;
  border-color: rgb(212, 212, 213);
  border-width: 1px;
  border-style: solid;
  border-radius: 5px 5px 0px 0px;
  font-size: 14px;
  background: rgb(240, 240, 240);
  display: flex;
  align-items: center;

  .title {
    font-weight: 600;
    font-size: 16px;
    color: rgb(16, 16, 16);
    margin-left: 10px;
  }
}

.content {
  margin-top: -1px;
  border-color: rgb(212, 212, 213);
  border-width: 1px;
  border-style: solid;
  padding: 30px 0;
  border-top: none;

  .guide-c {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 20px;

    .step {
      display: flex;
      align-items: center;
      color: rgb(136, 136, 136);

      .num {
        border: 1px solid rgb(136, 136, 136);
        width: 34px;
        height: 34px;
        font-size: 24px;
        text-align: center;
        line-height: 34px;
        border-radius: 17px;
        margin-right: 8px;
      }

      .txt {
        font-weight: 600;
      }

      &.focused {
        color: rgb(22, 132, 252);

        .num {
          border-color: rgb(22, 132, 252);
        }
      }
    }

    .line {
      height: 2px;
      width: 50px;
      background-color: rgb(187, 187, 187);
      margin: 0 10px;
    }
  }

  .row-c {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    margin: 0 auto;

    .row {
      width: 100%;
      display: flex;
      align-items: center;
      margin: 8px 0;
      margin-left: -190px;

      .r-title {
        text-align: right;
        font-size: .92857143em;
        font-weight: 700;
        color: rgba(0, 0, 0, .87);
        width: 200px;
        margin-right: 28px;
        position: relative;

        .required {
          &::after {
            position: absolute;
            margin: -0.2em 0 0 0.2em;
            content: '*';
            color: #db2828;
          }
        }
      }

      .r-content {
        flex: 1;

        .cluster-type-btn {
          display: flex;
          align-items: center;
          justify-content: center;
          border: 1px solid #DCDFE6;
          height: 36px;
          padding: 10px;
          cursor: pointer;
          border-radius: 4px;

          .icon {
            margin-right: 5px;
          }

          &.focused {
            border-color: rgb(50, 145, 248);
            color: rgb(50, 145, 248);
            cursor: default;

            .icon {
              :not([stroke]) {
                fill: rgb(50, 145, 248);
              }
            }
          }
        }
      }
    }
  }
}

.not-allowed-placeholder {
  position: absolute;
  width: 100%;
  height: 100%;
  z-index: 20;
  cursor: no-drop;
  top: 0;
}

.dropzone {
  min-height: 186px !important;

  /deep/ .dz-default.dz-message {
    margin: 76px 0 !important;
  }

  /deep/ .dz-remove {
    margin-top: 10px !important;
  }
}

.input-disabled {
  /deep/ .el-input__inner {
    background-color: #f5f5f6 !important;
    color: #888888 !important;
  }
}

/deep/ .dz-progress {
  display: none;
}

.success {
  color: #21ba45;
  font-size: 16px;
  margin-right: 0.5rem;
}

.failed {
  color: red;
  font-size: 16px;
  margin-right: 0.5rem;
}

.datast-upload-progress {
  display: flex;
  align-items: center;
}

.datast-upload-progress .dataset-name {
  text-align: right;
  width: 200px;
  margin-right: 1rem;
}

.datast-upload-progress .dataset-progress {
  flex: 1;
}

.datast-upload-progress .dataset-status {
  width: 140px;
  margin-left: 1rem;
}

.datast-upload-progress .dataset-status .status-flex {
  display: flex;
  align-items: center;
}

/deep/ .el-progress-bar__inner {
  background-color: #21ba45;
}

.el-select-dropdown__item.selected {
  color: #85b7d9;
}

/deep/ .el-button {
  background-color: #e0e1e2;
  color: rgba(0, 0, 0, .6);
  border-color: transparent;
  transition: opacity .1s ease, background-color .1s ease, color .1s ease, box-shadow .1s ease, background .1s ease, -webkit-box-shadow .1s ease;
  will-change: auto;
  -webkit-tap-highlight-color: transparent;

  &:hover {
    border-color: transparent;
    background-color: #cacbcd;
    color: rgba(0, 0, 0, .8);
  }

  &:focus {
    background-color: #cacbcd;
    color: rgba(0, 0, 0, .8);
    border-color: transparent;
  }

  &:active {
    background-color: #babbbc;
    color: rgba(0, 0, 0, .9);
    border-color: transparent;
  }

  &.green {
    background-color: #5bb973;
    color: #fff;

    &:hover {
      background-color: #16ab39;
      border-color: transparent;
    }

    &:focus {
      background-color: #0ea432;
      border-color: transparent;
    }

    &:active {
      background-color: #198f35;
      border-color: transparent;
    }
  }
}

/deep/ .el-select {
  .is-focus {
    .el-input__inner {
      border-color: #85b7d9;
    }
  }
}

/deep/ .el-input__inner {
  &:focus {
    border-color: #85b7d9;
  }
}

/deep/ .el-textarea__inner {
  &:focus {
    border-color: #85b7d9;
  }
}
</style>
