• vue2 结合 elementui 实现图片裁剪上传


    1、效果

    vue 实现图片裁剪上传

    2、准备

    第三方库:vue-cropper - npm

    npm i vue-cropper

    3、封装组件

    1. <template>
    2. <div class="cropper-wrapper">
    3. <!-- element 上传图片按钮 -->
    4. <template v-if="!isPreview">
    5. <el-upload
    6. ref="upload"
    7. class="upload-demo"
    8. :action="actionUrl"
    9. drag
    10. :on-change="handleChangeUpload"
    11. :auto-upload="false"
    12. :show-file-list="false"
    13. :disabled="disabled"
    14. >
    15. <i class="el-icon-plus upload-demo-icon"></i>
    16. </el-upload>
    17. </template>
    18. <div v-else class="pre-box">
    19. <el-upload
    20. class="upload-demo"
    21. action=""
    22. :auto-upload="false"
    23. :show-file-list="false"
    24. :on-change="handleChangeUpload"
    25. :disabled="disabled"
    26. >
    27. <img :src="previewImg" alt="裁剪图片" style="width: 100px;height: 100px" />
    28. </el-upload>
    29. </div>
    30. <!-- vueCropper 剪裁图片实现-->
    31. <el-dialog
    32. title="图片剪裁"
    33. :visible.sync="dialogVisible"
    34. class="crop-dialog"
    35. append-to-body
    36. >
    37. <div class="cropper-content">
    38. <div class="cropper" style="text-align: center">
    39. <VueCropper
    40. ref="cropper"
    41. :img="option.img"
    42. :output-size="option.size"
    43. :output-type="option.outputType"
    44. :info="true"
    45. :full="option.full"
    46. :can-move="option.canMove"
    47. :can-move-box="option.canMoveBox"
    48. :original="option.original"
    49. :auto-crop="option.autoCrop"
    50. :fixed="option.fixed"
    51. :fixed-number="option.fixedNumber"
    52. :center-box="option.centerBox"
    53. :info-true="option.infoTrue"
    54. :fixed-box="option.fixedBox"
    55. :auto-crop-width="option.autoCropWidth"
    56. :auto-crop-height="option.autoCropHeight"
    57. @cropMoving="cropMoving"
    58. />
    59. </div>
    60. </div>
    61. <div class="action-box">
    62. <el-upload
    63. class="upload-demo"
    64. action=""
    65. :auto-upload="false"
    66. :show-file-list="false"
    67. :on-change="handleChangeUpload"
    68. >
    69. <el-button type="primary">更换图片</el-button>
    70. </el-upload>
    71. <!-- <el-button type="primary" @click="clearImgHandle">
    72. 清除图片
    73. </el-button> -->
    74. <el-button type="primary" @click="rotateLeftHandle">
    75. 左旋转
    76. </el-button>
    77. <el-button type="primary" @click="rotateRightHandle">
    78. 右旋转
    79. </el-button>
    80. <!-- <el-button type="primary" plain @click="changeScaleHandle(1)">
    81. 放大
    82. </el-button>
    83. <el-button type="primary" plain @click="changeScaleHandle(-1)">
    84. 缩小
    85. </el-button> -->
    86. <el-button type="primary" @click="option.fixed = !option.fixed">
    87. {{ option.fixed ? '固定比例' : '自由比例' }}
    88. </el-button>
    89. <el-button type="primary" @click="downloadHandle('blob')">
    90. 下载
    91. </el-button>
    92. </div>
    93. <div style="height: 30px"></div>
    94. <div slot="footer" class="dialog-footer">
    95. <el-button @click="dialogVisible = false">取 消</el-button>
    96. <el-button type="primary" :loading="loading" @click="finish">
    97. 确认
    98. </el-button>
    99. </div>
    100. </el-dialog>
    101. </div>
    102. </template>
    103. <script>
    104. import { VueCropper } from 'vue-cropper'
    105. import { uploadImage } from '@/api/bff'
    106. export default {
    107. name: 'Cropper',
    108. components: {
    109. VueCropper,
    110. },
    111. model: {
    112. prop: 'value',
    113. event: 'input'
    114. },
    115. props: {
    116. value: {
    117. type: String,
    118. default: ''
    119. },
    120. disabled: {
    121. type: Boolean,
    122. default: false
    123. }
    124. },
    125. data() {
    126. return {
    127. actionUrl: process.env.VUE_APP_UPLOAD_API + '/api/v1.0/oss/upload',
    128. isPreview: false,
    129. dialogVisible: false,
    130. previewImg: '', // 预览图片地址
    131. // 裁剪组件的基础配置option
    132. option: {
    133. img: '', // 裁剪图片的地址
    134. info: true, // 裁剪框的大小信息
    135. outputSize: 1, // 裁剪生成图片的质量
    136. outputType: 'png', // 裁剪生成图片的格式
    137. canScale: true, // 图片是否允许滚轮缩放
    138. autoCrop: true, // 是否默认生成截图框
    139. canMoveBox: true, // 截图框能否拖动
    140. autoCropWidth: 200, // 默认生成截图框宽度
    141. autoCropHeight: 200, // 默认生成截图框高度
    142. fixedBox: false, // 固定截图框大小 不允许改变
    143. fixed: false, // 是否开启截图框宽高固定比例
    144. fixedNumber: [1, 1], // 截图框的宽高比例
    145. full: false, // 是否输出原图比例的截图
    146. original: false, // 上传图片按照原始比例渲染
    147. centerBox: true, // 截图框是否被限制在图片里面
    148. infoTrue: true, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
    149. },
    150. // 防止重复提交
    151. loading: false,
    152. };
    153. },
    154. watch: {
    155. value: {
    156. handler(val, newval) {
    157. if (val !== newval && val) {
    158. this.isPreview = true;
    159. this.previewImg = val
    160. }
    161. },
    162. immediate: true
    163. }
    164. },
    165. methods: {
    166. // 上传按钮 限制图片大小和类型
    167. handleChangeUpload(file) {
    168. const isJPG =
    169. file.raw.type === 'image/jpeg' || file.raw.type === 'image/png';
    170. const isLt2M = file.size / 1024 / 1024 < 2;
    171. if (!isJPG) {
    172. this.$message.error('上传头像图片只能是 JPG/PNG 格式!');
    173. return false;
    174. }
    175. if (!isLt2M) {
    176. this.$message.error('上传头像图片大小不能超过 2MB!');
    177. return false;
    178. }
    179. // 上传成功后将图片地址赋值给裁剪框显示图片
    180. this.$nextTick(async () => {
    181. // base64方式
    182. // this.option.img = await fileByBase64(file.raw)
    183. this.option.img = URL.createObjectURL(file.raw);
    184. this.loading = false;
    185. this.dialogVisible = true;
    186. });
    187. },
    188. // 放大/缩小
    189. changeScaleHandle(num) {
    190. num = num || 1;
    191. this.$refs.cropper.changeScale(num);
    192. },
    193. // 左旋转
    194. rotateLeftHandle() {
    195. this.$refs.cropper.rotateLeft();
    196. },
    197. // 右旋转
    198. rotateRightHandle() {
    199. this.$refs.cropper.rotateRight();
    200. },
    201. // 下载
    202. downloadHandle(type) {
    203. let aLink = document.createElement('a');
    204. aLink.download = 'author-img';
    205. if (type === 'blob') {
    206. this.$refs.cropper.getCropBlob((data) => {
    207. aLink.href = URL.createObjectURL(data);
    208. aLink.click();
    209. });
    210. } else {
    211. this.$refs.cropper.getCropData((data) => {
    212. aLink.href = data;
    213. aLink.click();
    214. });
    215. }
    216. },
    217. // 清理图片
    218. clearImgHandle() {
    219. this.option.img = '';
    220. },
    221. // 截图框移动回调函数
    222. cropMoving() {
    223. // 截图框的左上角 x,y和右下角坐标x,y
    224. // let cropAxis = [data.axis.x1, data.axis.y1, data.axis.x2, data.axis.y2]
    225. // console.log(cropAxis)
    226. },
    227. finish() {
    228. // 获取截图的 blob 数据
    229. this.$refs.cropper.getCropBlob((blob) => {
    230. this.loading = true;
    231. this.dialogVisible = false;
    232. this.previewImg = URL.createObjectURL(blob);
    233. this.isPreview = true;
    234. // this.$refs.submit()
    235. this.uploadImage(blob)
    236. });
    237. // 获取截图的 base64 数据
    238. // this.$refs.cropper.getCropData(data => {
    239. // console.log(data)
    240. // })
    241. },
    242. uploadImage(blob) {
    243. let file = new File([blob], new Date() + '图片.png', { type: 'image/png', lastModified: Date.now() });
    244. file.uid = Date.now();
    245. var fd = new FormData();
    246. fd.append("file", file, new Date() + "图片.png");
    247. fd.append("project", "micropark_coordination");
    248. uploadImage(fd).then(res => {
    249. console.log('图片上传', res)
    250. if (res.Type === 200) {
    251. const imageUrl = res.Data.HostSetting.External + res.Data.PathSetting.Path;
    252. this.$emit('input', imageUrl);
    253. }
    254. });
    255. }
    256. },
    257. };
    258. </script>
    259. <style lang="scss" scoped>
    260. :deep(.el-upload-dragger) {
    261. width: 100px !important;
    262. height: 100px !important;
    263. }
    264. .cropper-wrapper {
    265. width: 100%;
    266. height: 100%;
    267. .upload-demo .el-upload {
    268. border: 1px dashed #d9d9d9;
    269. border-radius: 6px;
    270. cursor: pointer;
    271. position: relative;
    272. overflow: hidden;
    273. }
    274. .upload-demo .el-upload:hover {
    275. border-color: #409eff;
    276. }
    277. .upload-demo-icon {
    278. font-size: 28px;
    279. color: #8c939d;
    280. width: 100px;
    281. height: 100px;
    282. line-height: 100px;
    283. text-align: center;
    284. }
    285. .pre-box {
    286. display: flex;
    287. flex-direction: column;
    288. align-items: center;
    289. button {
    290. width: 100px;
    291. margin-top: 15px;
    292. }
    293. }
    294. }
    295. .crop-dialog {
    296. .cropper-content {
    297. padding: 0 40px;
    298. .cropper {
    299. width: auto;
    300. height: 350px;
    301. }
    302. }
    303. .action-box {
    304. padding: 25px 40px 0 40px;
    305. display: flex;
    306. justify-content: center;
    307. button {
    308. width: 80px;
    309. margin-right: 15px;
    310. }
    311. }
    312. .dialog-footer {
    313. button {
    314. width: 100px;
    315. }
    316. }
    317. }
    318. </style>

    4、使用

    <upload-picture-cropper v-model="info.UserExternalIdentify.WeChatQrCode" :disabled="isDisable" />

  • 相关阅读:
    RL note
    异或的4种堪称神奇的运用场景
    Vite入门 | 青训营笔记
    1、项目准备与新建
    Java内存模型
    Golang 手写一个并发任务 manager
    Github 2024-06-20 Go开源项目日报 Top10
    设计模式(三十一)----综合应用-自定义Spring框架-自定义Spring IOC-定义解析器、IOC容器相关类
    【OJ比赛日历】快周末了,不来一场比赛吗? #10.21-10.27 #11场
    医疗产品设计的重要性,你了解多少?
  • 原文地址:https://blog.csdn.net/qq_57952018/article/details/138082920