ADD component sc-cropper
1:增加组件 scCropper 2:scUpload内置scCropper
This commit is contained in:
parent
0908c26d5d
commit
bd4fd271c1
|
|
@ -11,6 +11,7 @@
|
||||||
"@tinymce/tinymce-vue": "4.0.3",
|
"@tinymce/tinymce-vue": "4.0.3",
|
||||||
"axios": "0.21.1",
|
"axios": "0.21.1",
|
||||||
"core-js": "3.15.2",
|
"core-js": "3.15.2",
|
||||||
|
"cropperjs": "^1.5.12",
|
||||||
"crypto-js": "4.0.0",
|
"crypto-js": "4.0.0",
|
||||||
"echarts": "5.1.2",
|
"echarts": "5.1.2",
|
||||||
"element-plus": "1.0.2-beta.54",
|
"element-plus": "1.0.2-beta.54",
|
||||||
|
|
@ -27,11 +28,11 @@
|
||||||
"@vue/cli-plugin-eslint": "4.5.13",
|
"@vue/cli-plugin-eslint": "4.5.13",
|
||||||
"@vue/cli-service": "4.5.13",
|
"@vue/cli-service": "4.5.13",
|
||||||
"@vue/compiler-sfc": "3.1.4",
|
"@vue/compiler-sfc": "3.1.4",
|
||||||
"less": "3.13.1",
|
|
||||||
"less-loader": "7.3.0",
|
|
||||||
"babel-eslint": "10.1.0",
|
"babel-eslint": "10.1.0",
|
||||||
"eslint": "6.8.0",
|
"eslint": "6.8.0",
|
||||||
"eslint-plugin-vue": "7.12.1"
|
"eslint-plugin-vue": "7.12.1",
|
||||||
|
"less": "3.13.1",
|
||||||
|
"less-loader": "7.3.0"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"root": true,
|
"root": true,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
<!--
|
||||||
|
* @Descripttion: 图像裁剪组件
|
||||||
|
* @version: 1.0
|
||||||
|
* @Author: sakuya
|
||||||
|
* @Date: 2021年7月24日17:05:43
|
||||||
|
* @LastEditors:
|
||||||
|
* @LastEditTime:
|
||||||
|
* @other: 代码完全开源,欢迎参考,也欢迎PR
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="sc-cropper">
|
||||||
|
<div class="sc-cropper__img">
|
||||||
|
<img :src="src" ref="img">
|
||||||
|
</div>
|
||||||
|
<div class="sc-cropper__preview">
|
||||||
|
<h4>图像预览</h4>
|
||||||
|
<div class="sc-cropper__preview__img" ref="preview"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Cropper from 'cropperjs'
|
||||||
|
import 'cropperjs/dist/cropper.css'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
src: { type: String, default: "" },
|
||||||
|
compress: {type: Number, default: 1},
|
||||||
|
aspectRatio: {type: Number, default: NaN},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
crop: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch:{
|
||||||
|
aspectRatio(val){
|
||||||
|
this.crop.setAspectRatio(val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.init()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init(){
|
||||||
|
this.crop = new Cropper(this.$refs.img, {
|
||||||
|
viewMode: 2,
|
||||||
|
dragMode: 'move',
|
||||||
|
aspectRatio: this.aspectRatio,
|
||||||
|
preview: this.$refs.preview
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setAspectRatio(aspectRatio){
|
||||||
|
this.crop.setAspectRatio(aspectRatio)
|
||||||
|
},
|
||||||
|
getCropData(cb, type='image/jpeg'){
|
||||||
|
cb(this.crop.getCroppedCanvas().toDataURL(type, this.compress))
|
||||||
|
},
|
||||||
|
getCropBlob(cb, type='image/jpeg'){
|
||||||
|
this.crop.getCroppedCanvas().toBlob((blob) => {
|
||||||
|
cb(blob)
|
||||||
|
}, type, this.compress)
|
||||||
|
},
|
||||||
|
getCropFile(cb, fileName='fileName.jpg', type='image/jpeg'){
|
||||||
|
this.crop.getCroppedCanvas().toBlob((blob) => {
|
||||||
|
let file = new File([blob], fileName, {type: type})
|
||||||
|
cb(file)
|
||||||
|
}, type, this.compress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.sc-cropper {height:300px;}
|
||||||
|
.sc-cropper__img {height:100%;width:400px;float: left;background: #EBEEF5;}
|
||||||
|
.sc-cropper__img img {display: none;}
|
||||||
|
.sc-cropper__preview {width: 120px;margin-left: 20px;float: left;}
|
||||||
|
.sc-cropper__preview h4 {font-weight: normal;font-size: 12px;color: #999;margin-bottom: 20px;}
|
||||||
|
.sc-cropper__preview__img {overflow: hidden;width: 120px;height: 120px;border: 1px solid #ebeef5;}
|
||||||
|
</style>
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
<a v-else :href="img" class="file" target="_blank"><i class="el-icon-document"></i></a>
|
<a v-else :href="img" class="file" target="_blank"><i class="el-icon-document"></i></a>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="sc-upload-uploader">
|
<div v-else class="sc-upload-uploader">
|
||||||
<el-upload ref="upload" class="uploader" :accept="accept" :action="action" :show-file-list="false" :before-upload="before" :on-success="success" :on-error="error" :http-request="request">
|
<el-upload ref="upload" class="uploader" :auto-upload="!cropper" :on-change="change" :accept="accept" :action="action" :show-file-list="false" :before-upload="before" :on-success="success" :on-error="error" :http-request="request">
|
||||||
<slot>
|
<slot>
|
||||||
<div class="file-empty">
|
<div class="file-empty">
|
||||||
<i :class="icon"></i>
|
<i :class="icon"></i>
|
||||||
|
|
@ -17,12 +17,21 @@
|
||||||
</slot>
|
</slot>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
</div>
|
</div>
|
||||||
|
<el-dialog title="剪裁" v-model="cropperDialogVisible" :width="580" destroy-on-close>
|
||||||
|
<sc-cropper :src="cropperImg" :compress="compress" :aspectRatio="aspectRatio" ref="cropper"></sc-cropper>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="cropperDialogVisible=false" >取 消</el-button>
|
||||||
|
<el-button type="primary" @click="cropperSave">确 定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
<el-input v-model="img" style="display:none"></el-input>
|
<el-input v-model="img" style="display:none"></el-input>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import config from "@/config/upload";
|
import config from "@/config/upload"
|
||||||
|
import scCropper from '@/components/scCropper';
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
|
@ -35,8 +44,14 @@
|
||||||
maxSize: { type: Number, default: config.maxSize },
|
maxSize: { type: Number, default: config.maxSize },
|
||||||
title: { type: String, default: "" },
|
title: { type: String, default: "" },
|
||||||
icon: { type: String, default: "el-icon-plus" },
|
icon: { type: String, default: "el-icon-plus" },
|
||||||
|
cropper: { type: Boolean, default: false },
|
||||||
|
compress: {type: Number, default: 1},
|
||||||
|
aspectRatio: {type: Number, default: NaN},
|
||||||
onSuccess: { type: Function, default: () => { return true } }
|
onSuccess: { type: Function, default: () => { return true } }
|
||||||
},
|
},
|
||||||
|
components: {
|
||||||
|
scCropper
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|
@ -46,7 +61,10 @@
|
||||||
style: {
|
style: {
|
||||||
width: this.width + "px",
|
width: this.width + "px",
|
||||||
height: this.height + "px"
|
height: this.height + "px"
|
||||||
}
|
},
|
||||||
|
cropperDialogVisible: false,
|
||||||
|
cropperImg: "",
|
||||||
|
cropperUploadFile: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch:{
|
watch:{
|
||||||
|
|
@ -63,6 +81,14 @@
|
||||||
this.img = this.modelValue;
|
this.img = this.modelValue;
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
cropperSave(){
|
||||||
|
var uploadFile = this.$refs.upload.uploadFiles[0].raw
|
||||||
|
this.$refs.cropper.getCropFile(file => {
|
||||||
|
this.cropperUploadFile = file
|
||||||
|
this.$refs.upload.submit()
|
||||||
|
}, uploadFile.name, uploadFile.type)
|
||||||
|
this.cropperDialogVisible = false
|
||||||
|
},
|
||||||
isImg(fileUrl){
|
isImg(fileUrl){
|
||||||
var strRegex = "(.jpg|.png|.gif|.jpeg)$";
|
var strRegex = "(.jpg|.png|.gif|.jpeg)$";
|
||||||
var re = new RegExp(strRegex);
|
var re = new RegExp(strRegex);
|
||||||
|
|
@ -72,7 +98,22 @@
|
||||||
this.fileIsImg=false;
|
this.fileIsImg=false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
change(file){
|
||||||
|
if(this.cropper && file.status=='ready'){
|
||||||
|
this.isImg(file.name)
|
||||||
|
if(!this.fileIsImg){
|
||||||
|
this.$notify.warning({
|
||||||
|
title: '上传文件警告',
|
||||||
|
message: '选择的文件非图像类文件'
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
this.cropperDialogVisible = true
|
||||||
|
this.cropperImg = URL.createObjectURL(file.raw)
|
||||||
|
}
|
||||||
|
},
|
||||||
before(file){
|
before(file){
|
||||||
|
file = this.cropper ? this.cropperUploadFile : file
|
||||||
const maxSize = file.size / 1024 / 1024 < this.maxSize;
|
const maxSize = file.size / 1024 / 1024 < this.maxSize;
|
||||||
if (!maxSize) {
|
if (!maxSize) {
|
||||||
this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`);
|
this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`);
|
||||||
|
|
@ -114,7 +155,8 @@
|
||||||
apiObj = this.apiObj;
|
apiObj = this.apiObj;
|
||||||
}
|
}
|
||||||
const data = new FormData();
|
const data = new FormData();
|
||||||
data.append("file", param.file);
|
var file = this.cropper ? this.cropperUploadFile : param.file
|
||||||
|
data.append("file", file);
|
||||||
apiObj.post(data).then(res => {
|
apiObj.post(data).then(res => {
|
||||||
param.onSuccess(res)
|
param.onSuccess(res)
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
<!--
|
||||||
|
* @Descripttion: 图像剪裁组件演示文件
|
||||||
|
* @version: 1.0
|
||||||
|
* @Author: sakuya
|
||||||
|
* @Date: 2021年7月24日20:58:51
|
||||||
|
* @LastEditors:
|
||||||
|
* @LastEditTime:
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-main>
|
||||||
|
<el-row :gutter="15">
|
||||||
|
<el-col :lg="14">
|
||||||
|
<el-card shadow="never">
|
||||||
|
<sc-cropper :src="cropperImg" :compress="compress" :aspectRatio="aspectRatio" ref="cropper"></sc-cropper>
|
||||||
|
</el-card>
|
||||||
|
<el-card shadow="never" header="参数和方法">
|
||||||
|
<el-form label-width="100px">
|
||||||
|
<el-form-item label="aspectRatio">
|
||||||
|
<el-select v-model="aspectRatio" placeholder="请选择">
|
||||||
|
<el-option label="自由" :value="0"></el-option>
|
||||||
|
<el-option label="1:1" :value="1/1"></el-option>
|
||||||
|
<el-option label="4:3" :value="4/3"></el-option>
|
||||||
|
<el-option label="16:9" :value="16/9"></el-option>
|
||||||
|
</el-select>
|
||||||
|
<div class="el-form-item-msg">固定选区或者不固定</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="compress">
|
||||||
|
<el-select v-model="compress" placeholder="请选择">
|
||||||
|
<el-option label="0.1" :value="0.1"></el-option>
|
||||||
|
<el-option label="0.5" :value="0.5"></el-option>
|
||||||
|
<el-option label="1" :value="1"></el-option>
|
||||||
|
</el-select>
|
||||||
|
<div class="el-form-item-msg">图像压缩率,值为:0.1-1</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-button type="primary" plain @click="getBase64">Base64</el-button>
|
||||||
|
<el-button type="primary" plain @click="getBlob">Blob</el-button>
|
||||||
|
<el-button type="primary" plain @click="getFile">File</el-button>
|
||||||
|
</el-card>
|
||||||
|
<el-card shadow="never" header="方法结果">
|
||||||
|
<img :src="imgData" />
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :lg="10">
|
||||||
|
<el-card shadow="never" header="已内置剪裁的上传组件">
|
||||||
|
<el-alert title="设置cropper就可以开启上传前剪裁, 并已封装compress和aspectRatio, 打开F12查看网络请求" type="success" style="margin-bottom:20px;"></el-alert>
|
||||||
|
<sc-upload v-model="uploadImg" title="开启剪裁" :cropper="true" :compress="1" :aspectRatio="1/1"></sc-upload>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-main>
|
||||||
|
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import scCropper from '@/components/scCropper'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'cropper',
|
||||||
|
components: {
|
||||||
|
scCropper
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
cropperImg: 'img/avatar.jpg',
|
||||||
|
compress: 0.5,
|
||||||
|
aspectRatio: 0,
|
||||||
|
uploadImg: '',
|
||||||
|
imgData: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getBase64(){
|
||||||
|
this.$refs.cropper.getCropData(data=>{
|
||||||
|
this.imgData = data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getBlob(){
|
||||||
|
this.$refs.cropper.getCropBlob(blob=>{
|
||||||
|
this.imgData = URL.createObjectURL(blob)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getFile(){
|
||||||
|
this.$refs.cropper.getCropFile(file=>{
|
||||||
|
let aTag = document.createElement('a')
|
||||||
|
aTag.download = file.name;
|
||||||
|
aTag.href = URL.createObjectURL(file)
|
||||||
|
aTag.click()
|
||||||
|
}, 'fileName.jpg', 'image/jpeg')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
<p>自定义插槽</p>
|
<p>自定义插槽</p>
|
||||||
</div>
|
</div>
|
||||||
</sc-upload>
|
</sc-upload>
|
||||||
|
<sc-upload v-model="imgurl4" title="开启剪裁" :cropper="true" :compress="1" :aspectRatio="1/1"></sc-upload>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-card shadow="never" header="在验证表单中使用">
|
<el-card shadow="never" header="在验证表单中使用">
|
||||||
|
|
@ -51,6 +52,7 @@
|
||||||
imgurl: "img/avatar.jpg",
|
imgurl: "img/avatar.jpg",
|
||||||
imgurl2: "",
|
imgurl2: "",
|
||||||
imgurl3: "",
|
imgurl3: "",
|
||||||
|
imgurl4: "",
|
||||||
form: {
|
form: {
|
||||||
img1: "",
|
img1: "",
|
||||||
img2: "",
|
img2: "",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue