查看实际效果

20200927112501140.gif

引用vue-cropper

  1. npm install vue-cropper
  2. yarn add vue-cropper
  1. 全局引用
  2. import VueCropper from 'vue-cropper'
  3. Vue.use(VueCropper)
  4. 直接使用
  5. import { VueCropper } from 'vue-cropper'
  6. export default {
  7. name: 'CropperModal',
  8. components: {
  9. VueCropper,
  10. },
  11. data() {
  12. return {
  13. }
  14. },
  15. }
  16. </script>

创建组件文件夹

image.png

上传按钮组件 index.vue

  1. <template>
  2. <div class="ant-upload-preview">
  3. <div style="width: 100%">
  4. <a-upload
  5. name="avatar"
  6. listType="picture-card"
  7. :showUploadList="false"
  8. :beforeUpload="beforeUpload"
  9. :customRequest="function () {}"
  10. @change="handleChange"
  11. >
  12. <img class="upload_img" v-if="imageUrl" :src="imageUrl" alt="avatar" />
  13. <div v-else>
  14. <a-icon :type="loading ? 'loading' : 'plus'" />
  15. <div class="ant-upload-text">上传图片</div>
  16. </div>
  17. </a-upload>
  18. </div>
  19. <!-- modal -->
  20. <cropper-modal
  21. ref="CropperModal"
  22. :imgType="imgType"
  23. @cropper-no="handleCropperClose"
  24. @cropper-ok="handleCropperSuccess"
  25. ></cropper-modal>
  26. </div>
  27. </template>
  28. <script>
  29. import CropperModal from './CropperModal'
  30. export default {
  31. components: {
  32. CropperModal,
  33. },
  34. props: {
  35. //图片裁切配置
  36. options: {
  37. type: Object,
  38. default: function () {
  39. return {
  40. autoCrop: true, //是否默认生成截图框
  41. autoCropWidth: 180, //默认生成截图框宽度
  42. autoCropHeight: 180, //默认生成截图框高度
  43. fixedBox: false, //是否固定截图框大小 不允许改变
  44. previewsCircle: true, //预览图是否是原圆形
  45. title: '修改头像',
  46. }
  47. },
  48. },
  49. // 上传图片的大小,单位M
  50. imgSize: {
  51. type: Number,
  52. default: 2,
  53. },
  54. //图片存储在oss上的上级目录名
  55. imgType: {
  56. type: String,
  57. default: '',
  58. },
  59. // 图片地址
  60. imageUrl: {
  61. type: String,
  62. default: '',
  63. },
  64. },
  65. data() {
  66. return {
  67. loading: false,
  68. isStopRun: false,
  69. }
  70. },
  71. methods: {
  72. //从本地选择文件
  73. handleChange(info) {
  74. if (this.isStopRun) {
  75. return
  76. }
  77. this.loading = true
  78. const { options } = this
  79. this.getBase64(info.file.originFileObj, (imageUrl) => {
  80. const target = Object.assign({}, options, {
  81. img: imageUrl,
  82. })
  83. this.$refs.CropperModal.edit(target)
  84. })
  85. },
  86. // 上传之前 格式与大小校验
  87. beforeUpload(file) {
  88. this.isStopRun = false
  89. var fileType = file.type
  90. if (fileType.indexOf('image') < 0) {
  91. this.$message.warning('请上传图片')
  92. this.isStopRun = true
  93. return false
  94. }
  95. const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg'
  96. if (!isJpgOrPng) {
  97. this.$message.error('你上传图片格式不正确!')
  98. this.isStopRun = true
  99. }
  100. const isLtSize = file.size < this.imgSize * 1024 * 1024
  101. if (!isLtSize) {
  102. this.$message.error('图片大小不能超过' + this.imgSize + 'MB!')
  103. this.isStopRun = true
  104. }
  105. return isJpgOrPng && isLtSize
  106. },
  107. //获取服务器返回的地址
  108. handleCropperSuccess(data) {
  109. //将返回的数据回显
  110. this.loading = false
  111. this.$emit('crop-upload-success', data)
  112. },
  113. // 取消上传
  114. handleCropperClose() {
  115. this.loading = false
  116. this.$emit('crop-upload-close')
  117. },
  118. getBase64(img, callback) {
  119. const reader = new FileReader()
  120. reader.addEventListener('load', () => callback(reader.result))
  121. reader.readAsDataURL(img)
  122. },
  123. },
  124. }
  125. </script>
  126. <style lang="less" scoped>
  127. .avatar-upload-wrapper {
  128. height: 180px;
  129. width: 100%;
  130. }
  131. .ant-upload-preview {
  132. background-color: #fff;
  133. .avatar-uploader > .ant-upload {
  134. width: 128px;
  135. height: 128px;
  136. }
  137. .ant-upload-select-picture-card i {
  138. font-size: 32px;
  139. color: #999;
  140. }
  141. .upload_img {
  142. width: 100%;
  143. }
  144. .ant-upload-select-picture-card .ant-upload-text {
  145. margin-top: 8px;
  146. color: #666;
  147. }
  148. }
  149. </style>

模态框 CropperModal.vue

  1. <template>
  2. <a-modal
  3. :visible="visible"
  4. :title="options.title"
  5. :maskClosable="false"
  6. :confirmLoading="confirmLoading"
  7. :width="800"
  8. @cancel="cancelHandel"
  9. >
  10. <a-row>
  11. <a-col :xs="24" :md="12" :style="{ height: '350px' }">
  12. <vue-cropper
  13. ref="cropper"
  14. :img="options.img"
  15. :info="true"
  16. :autoCrop="options.autoCrop"
  17. :autoCropWidth="options.autoCropWidth"
  18. :autoCropHeight="options.autoCropHeight"
  19. :fixedBox="options.fixedBox"
  20. @realTime="realTime"
  21. >
  22. </vue-cropper>
  23. </a-col>
  24. <a-col :xs="24" :md="12" :style="{ height: '350px' }">
  25. <div :class="options.previewsCircle ? 'avatar-upload-preview' : 'avatar-upload-preview_range'">
  26. <img :src="previews.url" :style="previews.img" />
  27. </div>
  28. </a-col>
  29. </a-row>
  30. <template slot="footer">
  31. <a-button key="back" @click="cancelHandel">取消</a-button>
  32. <a-button key="submit" type="primary" :loading="confirmLoading" @click="okHandel">保存</a-button>
  33. </template>
  34. </a-modal>
  35. </template>
  36. <script>
  37. import { UpPic } from './index'
  38. export default {
  39. name: 'CropperModal',
  40. data() {
  41. return {
  42. visible: false,
  43. img: null,
  44. confirmLoading: false,
  45. options: {
  46. img: '', //裁剪图片的地址
  47. autoCrop: true, //是否默认生成截图框
  48. autoCropWidth: 180, //默认生成截图框宽度
  49. autoCropHeight: 180, //默认生成截图框高度
  50. fixedBox: true, //是否固定截图框大小 不允许改变
  51. previewsCircle: true, //预览图是否是原圆形
  52. title: '修改头像',
  53. },
  54. previews: {},
  55. url: {
  56. upload: '/sys/common/saveToImgByStr',
  57. },
  58. }
  59. },
  60. props: {
  61. //图片存储在oss上的上级目录名
  62. imgType: {
  63. type: String,
  64. default: '',
  65. },
  66. },
  67. methods: {
  68. edit(record) {
  69. const { options } = this
  70. this.visible = true
  71. this.options = Object.assign({}, options, record)
  72. },
  73. /**
  74. * 取消截图
  75. */
  76. cancelHandel() {
  77. this.confirmLoading = false
  78. this.visible = false
  79. this.$emit('cropper-no')
  80. },
  81. /**
  82. * 确认截图
  83. */
  84. okHandel() {
  85. const that = this
  86. that.confirmLoading = true
  87. // 获取截图的base64 数据
  88. this.$refs.cropper.getCropData((data) => {
  89. UpPic({
  90. img_type: this.imgType,
  91. img_byte: data,
  92. })
  93. .then((res) => {
  94. that.$emit('cropper-ok', res)
  95. })
  96. .catch((err) => {
  97. that.$message.error(err)
  98. })
  99. .finally(() => {
  100. that.cancelHandel()
  101. })
  102. })
  103. },
  104. //移动框的事件
  105. realTime(data) {
  106. this.previews = data
  107. },
  108. },
  109. }
  110. </script>
  111. <style lang="less" scoped>
  112. .avatar-upload-preview_range,
  113. .avatar-upload-preview {
  114. position: absolute;
  115. top: 50%;
  116. transform: translate(50%, -50%);
  117. width: 180px;
  118. height: 180px;
  119. border-radius: 50%;
  120. box-shadow: 0 0 4px #ccc;
  121. overflow: hidden;
  122. img {
  123. background-color: red;
  124. height: 100%;
  125. }
  126. }
  127. .avatar-upload-preview_range {
  128. border-radius: 0;
  129. }
  130. </style>

ajax网络接口 index.js

  1. import request from '@/utils/request'
  2. import qs from 'qs'
  3. const Api = {
  4. UpPic: '/UpPic',
  5. }
  6. /**
  7. * 上传图片
  8. * @param parameter
  9. * @returns {*}
  10. */
  11. export function UpPic(parameter) {
  12. return request({
  13. url: Api.UpPic,
  14. method: 'post',
  15. data: qs.stringify(parameter)
  16. })
  17. }

使用组件

  1. <template>
  2. <div class="container">
  3. <image-cropper
  4. :options="cropperOptions"
  5. :imgSize="2"
  6. :imgType="imgType"
  7. :imageUrl="imgUrl"
  8. @crop-upload-close="cropClose"
  9. @crop-upload-success="cropSuccess"
  10. />
  11. </div>
  12. </template>
  13. <script>
  14. import ImageCropper from '@/components/ImageCropper'
  15. export default {
  16. name: 'AddBanner',
  17. data() {
  18. return {
  19. cropperOptions: {
  20. autoCrop: true, //是否默认生成截图框
  21. autoCropWidth: 200, //默认生成截图框宽度
  22. autoCropHeight: 200, //默认生成截图框高度
  23. fixedBox: false, //是否固定截图框大小 不允许改变
  24. previewsCircle: false, //预览图是否是圆形
  25. title: '上传广告图片', //模态框上显示的标题
  26. },
  27. imgType: 'testUp', //图片存储在oss上的上级目录名
  28. imgUrl: '', //上传图片所得到的地址
  29. }
  30. },
  31. components: {
  32. ImageCropper,
  33. },
  34. methods: {
  35. //上传操作结束
  36. cropClose() {
  37. console.log('上传操作结束')
  38. },
  39. //上传图片成功
  40. cropSuccess(data) {
  41. this.imgUrl = data
  42. },
  43. },
  44. }
  45. </script>
  46. <style lang="less" scoped>
  47. .container {
  48. padding-top: 50px;
  49. padding-left: 200px;
  50. background-color: #fff;
  51. min-height: 650px;
  52. .a_select {
  53. width: 220px;
  54. }
  55. }
  56. </style>