vue的圖片剪切插件VueCopper及上傳

GitHub:vue-cropper

利用vue-cropper做的關于圖片裁剪把敞、壓縮、上傳壤蚜、預覽等做的一個公共組件

自己做的彈窗裁剪

<template>
  <Modal
    class="ct-personal-info"
    v-model="modalVisible"
    width="550"
    footer-hide
  >
    <h2 slot="header">中教信息</h2>
    <div class="ct-personal-info-content" v-if="!showCropper">
      <div
        class="ct-head-portrait"
        @click="handlerChangePhoto"
        @mouseenter="mouseenterImg"
        @mouseleave="mouseleaveImg"
      >
        <img class="head-photo-img" :src="headPhotoSrc" />
        <div v-if="showCameraIcon" class="camera-icon-wrapper">
          <Icon type="ios-camera" class="camera-icon" size="40" />
        </div>
      </div>
      <input
        ref="fileInput"
        type="file"
        class="file-hidden"
        accept="image/*"
        @change="handlerGetImg($event)"
      />
      <div class="ct-name">
        <label>姓名:</label>
        <Input class="ct-name-ipt" v-model="ctName" placeholder="請輸入姓名" />
      </div>
      <div class="ct-info-change-icon">
        <Icon
          class="tabel-teacher-houseNumAll-icon-1 change-icon"
          custom="iconfont icon-bianji_moren-"
          @click="handlerSubmitInfo"
        />
      </div>
    </div>
    <div class="tailor-photo" v-if="showCropper">
      <div class="cropper-wrapper">
        <VueCropper
          ref="cropper"
          :img="option.imgUrl"
          :outputSize="option.size"
          :outputType="option.outputType"
          :autoCrop="option.autoCrop"
          :autoCropWidth="option.autoCropWidth"
          :autoCropHeight="option.autoCropHeight"
          :fixedBox="option.fixedBox"
        ></VueCropper>
      </div>
      <div class="btns-group">
        <Button class="photo-btn" @click="handlerCancelCropper">取消</Button>
        <Button class="photo-btn" type="primary" @click="handlerRotateRight"
          >順時針旋轉(zhuǎn)</Button
        >
        <Button class="photo-btn" type="primary" @click="handlerRotateLeft"
          >逆時針旋轉(zhuǎn)</Button
        >
        <Button type="primary" @click="handlerFinishPhoto">確定</Button>
      </div>
    </div>
  </Modal>
</template>

<script type="text/javascript">
import { ajaxHelper, mapState, message } from 'utils';
import { postUpdateStudyPlanCtPhotoApi } from '@/api/studentDetail';
import regexp from 'const/regexp';
import { VueCropper } from 'vue-cropper';

export default {
  name: 'ct-personal-info',
  components: { VueCropper },
  data() {
    return {
      modalVisible: true,
      ctName: '',
      option: {
        imgUrl: '',
        size: 0,
        autoCrop: true,
        autoCropWidth: 200,
        autoCropHeight: 200,
        fixedBox: true,
        outputType: 'png',
      },
      showCropper: false,
      headPhotoSrc: '',
      showCameraIcon: false,
      finishCropperData: {},
      fileTargetVal: {},
    };
  },

  computed: {
    ...mapState({
      email: state => state.user.email,
      learningPlanEmployeeName: state => state.user.learningPlanEmployeeName,
      learningPlanEmployeeAvatar: state =>
        state.user.learningPlanEmployeeAvatar,
    }),
  },

  watch: {
    modalVisible: {
      handler(newValue) {
        if (newValue) {
          this.ctName = this.learningPlanEmployeeName;
          this.headPhotoSrc = this.learningPlanEmployeeAvatar;
        }
      },
      immediate: true,
    },

    showCropper(newValue, oldValue) {
      if (!newValue) {
        this.option.imgUrl = '';
      }
    },
  },

  methods: {
    handlerChangePhoto() {
      this.$refs.fileInput.click();
      this.showCropper = true;
    },

    handlerCancelCropper() {
      this.showCropper = false;
    },

    mouseenterImg() {
      this.showCameraIcon = true;
    },

    mouseleaveImg() {
      this.showCameraIcon = false;
    },

    //選擇本地圖片
    handlerGetImg(e) {
      let _this = this;
      //上傳圖片
      let file = e.target.files[0];
      this.fileTargetVal = file;

      if (!regexp.imgType.test(e.target.value)) {
        message('warning', '圖片類型必須是jpeg,jpg,png,bmp中的一種');
        return false;
      }
      let reader = new FileReader();
      reader.onload = e => {
        let data;
        if (typeof e.target.result === 'object') {
          // 把Array Buffer轉(zhuǎn)化為blob 如果是base64不需要
          data = window.URL.createObjectURL(new Blob([e.target.result]));
        } else {
          data = e.target.result;
        }
        _this.option.imgUrl = data;
      };
      //轉(zhuǎn)化為base64;
      // reader.readAsDataURL(file);
      // 轉(zhuǎn)化為blob
      reader.readAsArrayBuffer(file);
    },

    //順時針旋轉(zhuǎn)
    handlerRotateRight() {
      this.$refs.cropper.rotateRight();
    },

    //逆時針旋轉(zhuǎn)
    handlerRotateLeft() {
      this.$refs.cropper.rotateLeft();
    },

    handlerFinishPhoto() {
      // 獲取截圖的blob數(shù)據(jù)
      this.$refs.cropper.getCropBlob(data => {
        this.finishCropperData = data;
        //關閉裁剪
        this.showCropper = false;
        //取消相機icon
        this.mouseleaveImg();
        //blob轉(zhuǎn)回imgsrc用于預覽
        this.headPhotoSrc = window.URL.createObjectURL(data);
      });
    },

    async postUpdateStudyPlanCtPhoto() {
      let res = await ajaxHelper(
        postUpdateStudyPlanCtPhotoApi.bind(
          null,
          this.reqUpdateStudyPlanCtPhoto()
        )
      );
      res && this.resUpdateStudyPlanCtPhoto();
    },

    reqUpdateStudyPlanCtPhoto() {
      let formData = new FormData();
      formData.append(
        'learning_plan_employee_avatar_file',
        this.finishCropperData
      );
      formData.append('learning_plan_employee_name', this.ctName);
      formData.append('employee', this.email);

      return formData;
    },

    resUpdateStudyPlanCtPhoto() {
      message('success', '保存成功');
      //關閉彈窗
      // this.modalVisible=false;
    },

    handlerSubmitInfo() {
      //判斷昵稱和頭像src有無即可,不需要判斷blob對象
      if (!this.ctName || !this.headPhotoSrc) {
        message('warning', '請補充完整信息');
        return;
      }
      this.postUpdateStudyPlanCtPhoto();
    },
  },
};
</script>

<style scoped lang="scss">
.ct-personal-info {
  .ct-personal-info-content {
    display: flex;
    align-items: center;
  }
  .ct-head-portrait {
    position: relative;
    width: 100px;
    height: 100px;
    border-radius: 50%;
    border: solid 1px;
    margin-right: 8px;
    overflow: hidden;
    .head-photo-img {
      position: absolute;
      width: 100%;
      height: 100%;
      z-index: 10;
    }
    .camera-icon-wrapper {
      width: 100%;
      height: 100%;
      position: absolute;
      text-align: center;
      z-index: 100;
      background-color: rgba(4, 6, 8, 0.2);
      &::before {
        content: ' ';
        display: inline-block;
        height: 100%;
        width: 1%;
        vertical-align: middle;
      }
    }
  }
  .file-hidden {
    display: none;
  }
  .ct-name {
    display: flex;
    align-items: center;
    margin-right: 8px;
    & > label {
      width: 40px;
    }
    .ct-name-ipt {
      width: 240px;
    }
  }
  .change-icon {
    cursor: pointer;
    &:hover {
      color: rgba(24, 144, 255, 1);
    }
  }

  .tailor-photo {
    .cropper-wrapper {
      width: 500px;
      height: 500px;
    }
    .btns-group {
      display: flex;
      justify-content: flex-end;
      margin-top: 20px;
      .photo-btn {
        margin-right: 8px;
      }
    }
  }
}
</style>

注意

上傳文件流需要使用FormData對象,將需要參數(shù)通過append方式加入验庙,最終axios請求只需要data:formData即可

例如:

 reqUpdateStudyPlanCtPhoto() {
      let formData = new FormData();
      //將參數(shù)一一添加
      formData.append(
        'learning_plan_employee_avatar_file',
        this.finishCropperData
      );
      formData.append('learning_plan_employee_name', this.ctName);
      formData.append('employee', this.email);

      return formData;
    },
    
    //data:formData
    function postUpdateStudyPlanCtPhotoApi(formData) {
  return Axios({
    url: ' /api/permission/employee/update/',
    method: 'post',
    data: formData,
  });
}

???

不知道為啥,默認裁剪框?qū)捀邿o效社牲。難受壶谒。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市膳沽,隨后出現(xiàn)的幾起案子汗菜,更是在濱河造成了極大的恐慌让禀,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件陨界,死亡現(xiàn)場離奇詭異巡揍,居然都是意外死亡,警方通過查閱死者的電腦和手機菌瘪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門腮敌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人俏扩,你說我怎么就攤上這事糜工。” “怎么了录淡?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵捌木,是天一觀的道長。 經(jīng)常有香客問我嫉戚,道長刨裆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任彬檀,我火速辦了婚禮帆啃,結果婚禮上,老公的妹妹穿的比我還像新娘窍帝。我一直安慰自己努潘,他們只是感情好,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布坤学。 她就那樣靜靜地躺著慈俯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拥峦。 梳的紋絲不亂的頭發(fā)上贴膘,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天,我揣著相機與錄音略号,去河邊找鬼刑峡。 笑死,一個胖子當著我的面吹牛玄柠,可吹牛的內(nèi)容都是我干的突梦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼羽利,長吁一口氣:“原來是場噩夢啊……” “哼宫患!你這毒婦竟也來了?” 一聲冷哼從身側響起这弧,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤娃闲,失蹤者是張志新(化名)和其女友劉穎虚汛,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體皇帮,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡卷哩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了属拾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片将谊。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖渐白,靈堂內(nèi)的尸體忽然破棺而出尊浓,到底是詐尸還是另有隱情,我是刑警寧澤纯衍,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布栋齿,位于F島的核電站,受9級特大地震影響托酸,放射性物質(zhì)發(fā)生泄漏褒颈。R本人自食惡果不足惜柒巫,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一励堡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧堡掏,春花似錦应结、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至亭畜,卻和暖如春扮休,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拴鸵。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工玷坠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人劲藐。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓八堡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親聘芜。 傳聞我的和親對象是個殘疾皇子兄渺,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容