weixin_55005618 2022-02-26 17:23 采纳率: 100%
浏览 884
已结题

使用vue-cropper遇到的问题

vue-cropper使用问题请教(可以留下联系方式沟通,问题解决了有偿)

因为需要使用到图片裁剪功能,网上找到了vue-cropper,参考了网上的使用教程及示例,自己在copy过程中还是遇到了很多问题,希望能得到指教(刚接触前端开发不到一个月,实属人菜),以下是自己的代码(vue2+TS+ant-design-vue)

目前的问题是:

1.a-upload 组件无法预览后台的图片

img

2.裁剪框能获取到裁剪后的base64数据,不知道如何转换格式及上传到后台服务器上去

img

上传按钮组件 index.vue

<template>
  <div class="ant-upload-preview">
    <div style="width: 500px">
      <a-upload
        listType="picture-card"
        :showUploadList="false"
        :beforeUpload="beforeUpload"
        :customRequest="function () {}"
        @change="handleChange"
        @preview="handlePreview"
      >
        <img
          alt="example"
          style="width: 100%"
          v-if="imageUrl"
          :src="previewImage"
        />
        <div v-else>
          <a-icon :type="loading ? 'loading' : 'plus'" />
          <div class="ant-upload-text">上传封面</div>
        </div>
      </a-upload>
    </div>
    <!-- modal -->
    <cropper-modal
      ref="CropperModal"
      :imgType="imgType"
      :visible="previewVisible"
      @cropper-no="handleCropperClose"
      @cropper-ok="handleCropperSuccess"
    ></cropper-modal>
  </div>
</template>
<script lang="ts">
import CropperModal from './CropperModal.vue'

function getBase64(file: any) {
  return new Promise((resolve: any, reject: any) => {
    const reader = new FileReader()
    reader.readAsDataURL(file)
    reader.onload = () => resolve(reader.result)
    reader.onerror = (error) => reject(error)
  })
}
export default {
  components: {
    CropperModal
  },
  props: {
    //图片裁切配置
    options: {
      type: Object,
      default: function () {
        return {
          autoCrop: true, //是否默认生成截图框
          autoCropWidth: 280, //默认生成截图框宽度
          autoCropHeight: 396, //默认生成截图框高度
          fixedBox: true, //是否固定截图框大小 不允许改变
          previewsCircle: false, //预览图是否是原圆形
          title: '调整图片'
        }
      }
    },
    // 上传图片的大小,单位M
    imgSize: {
      type: Number,
      default: 5
    },
    //图片存储在oss上的上级目录名
    imgType: {
      type: String,
      default: ''
    },
    // 图片地址
    imageUrl: {
      type: String,
      default: ''
    },
    previewVisible: {
      type: Boolean,
      default: false
    },
    previewImage: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      loading: false,
      isStopRun: false
    }
  },

  methods: {
    async handlePreview(file: any) {
      if (!file.url && !file.preview) {
        file.preview = await getBase64(file.originFileObj)
      }
      this.previewImage = file.url || file.preview
      this.previewVisible = true
    },
    //从本地选择文件
    handleChange(info: any) {
      if (this.isStopRun) {
        return
      }
      this.loading = true
      const { options } = this
      this.getBase64(info.file.originFileObj, (imageUrl: string) => {
        const target = Object.assign({}, options, {
          img: imageUrl
        })
        this.$refs.CropperModal.edit(target)
      })
    },
    // 上传之前 格式与大小校验
    beforeUpload(file: any) {
      this.isStopRun = false
      var fileType = file.type
      if (fileType.indexOf('image') < 0) {
        this.$message.warning('请上传图片')
        this.isStopRun = true
        return false
      }
      const isJpgOrPng =
        file.type === 'image/jpeg' ||
        file.type === 'image/png' ||
        file.type === 'image/jpg'
      if (!isJpgOrPng) {
        this.$message.error('你上传图片格式不正确!')
        this.isStopRun = true
      }
      const isLtSize = file.size < this.imgSize * 1024 * 1024
      if (!isLtSize) {
        this.$message.error('图片大小不能超过' + this.imgSize + 'MB!')
        this.isStopRun = true
      }
      return isJpgOrPng && isLtSize
    },
    getBase64(img: any, callback: any) {
      const reader = new FileReader()
      reader.addEventListener('load', () => callback(reader.result))
      reader.readAsDataURL(img)
      // reader.readAsArrayBuffer(img)
    },
    //获取服务器返回的地址
    handleCropperSuccess(data: any) {
      this.loading = false
      this.$emit('crop-upload-success', data)
    },
    // 取消上传
    handleCropperClose() {
      this.loading = false
      this.$emit('crop-upload-close')
    }
  }
}
</script>

<style lang="less" scoped>
.ant-upload-preview {
  background-color: #fff;
  border-radius: 8px;

  .upload_img {
    width: 100%;
  }

  .ant-upload-select-picture-card .ant-upload-text {
    margin-top: 8px;
    color: #666;
  }
}

.list-upload {
  /deep/ .ant-upload-list-item-name {
    width: unset;
  }
  /deep/ .ant-upload-list-item-card-actions {
    position: relative;
    right: 0;
    margin-left: 11px;
  }
  /deep/ .ant-upload-list {
    line-height: 1;
  }
}
</style>

模态框 CropperModal.vue

<template>
  <a-modal
    :visible="visible"
    :title="options.title"
    :maskClosable="false"
    :confirmLoading="confirmLoading"
    :width="1000"
    @cancel="cancelHandel"
  >
    <a-row>
      <a-col
        :xs="24"
        :md="12"
        :style="{ height: '450px' }"
      >
        <vue-cropper
          ref="cropper"
          :img="options.img"
          :info="true"
          :autoCrop="options.autoCrop"
          :autoCropWidth="options.autoCropWidth"
          :autoCropHeight="options.autoCropHeight"
          :fixedBox="options.fixedBox"
          @realTime="realTime"
        >
        </vue-cropper>
      </a-col>
      <a-col
        :xs="24"
        :md="12"
        :style="{ height: '350px' }"
      >
        <div :class="options.previewsCircle ? 'avatar-upload-preview' : 'avatar-upload-preview_range'">
          <img
            :src="previews.url"
            :style="previews.img"
          />
        </div>
      </a-col>
    </a-row>
    <template slot="footer">
      <a-button
        key="back"
        @click="cancelHandel"
      >取消</a-button>
      <a-button
        key="submit"
        type="primary"
        :loading="confirmLoading"
        @click="okHandel"
      >保存</a-button>
    </template>
  </a-modal>
</template>
<script lang="ts">
// import { UpPic } from './index.js'

export default {
  name: 'CropperModal',
  components: {},
  data() {
    return {
      visible: false,
      img: null,
      confirmLoading: false,

      options: {
        img: '', //裁剪图片的地址
        autoCrop: false, //是否默认生成截图框
        autoCropWidth: 280, //默认生成截图框宽度
        autoCropHeight: 396, //默认生成截图框高度
        fixedBox: true, //是否固定截图框大小 不允许改变
        previewsCircle: false, //预览图是否是原圆形
        centerBox: true, //截图框是否被限制在图片里面
        title: '调整图片'
      },
      previews: {},
      url: {
        upload: 'xxxxxxx'
      }
    }
  },

  props: {
    //图片存储在oss上的上级目录名
    imgType: {
      type: String,
      default: ''
    }
  },
  methods: {
    edit(record: any) {
      const { options } = this
      this.visible = true
      this.options = Object.assign({}, options, record)
    },
    /**
     * 确认截图
     */
    okHandel() {
      const that = this
      that.confirmLoading = true
      // 获取截图的base64 数据
      this.$refs.cropper.getCropData((data: any) => {
        //将裁剪后的图片对象给**父组件**,然后关闭对话框
        that.$emit('cropper-ok', data)
        that.cancelHandel()
      })
    },
    /**
     * 取消截图
     */
    cancelHandel() {
      this.confirmLoading = false
      this.visible = false
      this.$emit('cropper-no')
    },
    //移动框的事件
    realTime(data: any) {
      this.previews = data
    }
  }
}
</script>

<style lang="less" scoped>
.avatar-upload-preview_range,
.avatar-upload-preview {
  position: absolute;
  top: 65%;
  transform: translate(50%, -50%);
  width: 280px;
  height: 396px;
  box-shadow: 0 0 4px #ccc;
  overflow: hidden;
  img {
    background-color: red;
    height: 100%;
  }
}
.avatar-upload-preview_range {
  border-radius: 0px;
}
</style>

使用组件的主页面(裁剪框部分)

            <template>
              <div class="container">
                <image-cropper
                  :options="cropperOptions"
                  :imgSize="5"
                  :imgType="imgType"
                  :imageUrl="imgUrl"
                  @crop-upload-close="cropClose"
                  @crop-upload-success="cropSuccess"
                />
              </div>
            </template>
     
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'
import ImageCropper from '@/components/ImageCropper'
import { VueCropper } from 'vue-cropper'

@Component({
  components: {
    ImageCropper,
    VueCropper
  }
})
export default class courseCenter extends Vue {
  //===================================data区域begin
  name: any = 'AddBanner'

  cropperOptions: any = {
    autoCrop: true, //是否默认生成截图框
    autoCropWidth: 280, //默认生成截图框宽度
    autoCropHeight: 396, //默认生成截图框高度
    fixedBox: true, //是否固定截图框大小 不允许改变
    previewsCircle: false, //预览图是否是圆形
    title: '调整图片' //模态框上显示的标题
  }
  imgType: any = 'testUp' //图片存储在oss上的上级目录名
  imgUrl: any = '' //上传图片所得到的地址

  previewImage: string = ''
  previewVisible: boolean = false
  //===================================data区域end

  //===================================computed区域begin
  //===================================computed区域end

  //===================================methods区域begin
  //上传操作结束
  cropClose() {
    console.log('上传操作结束')
  }
  //上传图片成功
  cropSuccess(data: any) {
    console.log(data)

    this.imgUrl = data
    console.log(data.url)
  }
  handleOk() {
    this.confirmLoading = true
    setTimeout(() => {
      this.formDialogVisible = false
      this.confirmLoading = false
    }, 2000)
  }
  handleCancel() {
    this.formDialogVisible = false
    this.$nextTick(function () {
      ;(this.$refs.formRef as any).resetFields()
    })
    this.fileList = []
    this.form = formHelper.copyNewForm(formHelper.form)
  }
  handleFileCancel() {
    this.previewVisible = false
  }

  //===================================methods区域end
  mounted() {}
  created() {}
</script>
<style lang="scss" scoped>
@import '../../assets/css/Course/home.scss';
/deep/ .ant-form-item-control {
  line-height: 36px;
}
.create-course /deep/ .ant-modal-footer {
  padding: 18px 22px 18px 0px !important;
}
</style>
  • 写回答

1条回答 默认 最新

  • _念_ 2022-02-26 18:12
    关注
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 3月2日
  • 已采纳回答 3月2日
  • 创建了问题 2月26日

悬赏问题

  • ¥15 服务端控制goose报文控制块的发布问题
  • ¥15 学习指导与未来导向啊
  • ¥15 求多普勒频移瞬时表达式
  • ¥15 如果要做一个老年人平板有哪些需求
  • ¥15 k8s生产配置推荐配置及部署方案
  • ¥15 matlab提取运动物体的坐标
  • ¥15 人大金仓下载,有人知道怎么解决吗
  • ¥15 一个小问题,本人刚入门,哪位可以help
  • ¥30 python安卓开发
  • ¥15 使用R语言GD包一直不出结果