123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- <template>
- <div class="image-edit-upload">
- <div :class="[`${prefixCls}-main`]" v-show="!edit">
- <div :class="elPrefixCls">
- <div
- :class="classes"
- @click="handleClick"
- @drop.prevent="onDrop"
- @dragover.prevent="dragOver = true"
- @dragleave.prevent="dragOver = false"
- >
- <input
- ref="input"
- type="file"
- :class="[`${elPrefixCls}-input`]"
- @change="handleChange"
- :accept="accept"
- />
- <div :class="[`${prefixCls}-cover`]" v-if="imgUrl">
- <img :src="imgUrl" alt="default" />
- </div>
- <div :class="[`${prefixCls}-dcover`]" v-else>
- <i class="el-icon-user-solid"></i><br />
- <p>点击或拖拽图片到此处</p>
- </div>
- </div>
- </div>
- </div>
- <div :class="[`${prefixCls}-edit`]" v-show="file.url && edit">
- <div :class="[`${prefixCls}-edit-box`]">
- <img ref="editImage" :src="file.url" />
- <transition name="fade" v-if="file && file.showProgress">
- <div class="img-progress">
- <el-progress
- :show-text="false"
- :stroke-width="4"
- :percentage="file.percentage"
- :status="
- file.status === 'finished' && file.showProgress
- ? 'success'
- : null
- "
- ></el-progress>
- </div>
- </transition>
- </div>
- <div :class="[`${prefixCls}-edit-btns`]">
- <el-button type="error" @click="clearEdit">取消</el-button>
- <el-button type="primary" @click="editSave">确认</el-button>
- </div>
- </div>
- <p
- :class="[
- `${prefixCls}-tips`,
- { 'cc-tips-success': res.success, 'cc-tips-error': !res.success }
- ]"
- v-if="res.msg"
- >
- {{ res.msg }}
- </p>
- </div>
- </template>
- <script>
- import ajax from "../utils/ajax";
- import Cropper from "cropperjs";
- import "cropperjs/dist/cropper.min.css";
- const elPrefixCls = "el-upload";
- const prefixCls = "cc-edit-upload";
- const ratios = {
- double: 1 / 2,
- square: 1,
- rectangle: 3 / 4
- };
- export default {
- name: "image-edit-upload",
- props: {
- action: {
- type: String,
- required: true
- },
- headers: {
- type: Object,
- default() {
- return {};
- }
- },
- defImage: {
- type: String
- },
- data: {
- type: Object
- },
- name: {
- type: String,
- default: "file"
- },
- withCredentials: {
- type: Boolean,
- default: false
- },
- format: {
- type: Array,
- default() {
- return ["jpg", "png"];
- }
- },
- accept: {
- type: String
- },
- maxSize: {
- type: Number,
- default() {
- return 2 * 1024;
- }
- },
- ratioType: {
- type: String,
- default: "rectangle",
- validator(val) {
- return ["square", "rectangle", "double"].indexOf(val) > -1;
- }
- },
- onProgress: {
- type: Function,
- default() {
- return {};
- }
- },
- onSuccess: {
- type: Function,
- default() {
- return {};
- }
- },
- onError: {
- type: Function,
- default() {
- return {};
- }
- },
- onRemove: {
- type: Function,
- default() {
- return {};
- }
- },
- onPreview: {
- type: Function,
- default() {
- return {};
- }
- },
- onExceededSize: {
- type: Function,
- default() {
- return {};
- }
- },
- onFormatError: {
- type: Function,
- default() {
- return {};
- }
- }
- },
- data() {
- return {
- file: {},
- type: "drag",
- edit: false,
- cropper: false,
- imgUrl: this.defImage,
- prefixCls,
- elPrefixCls,
- dragOver: false,
- tempIndex: 1,
- res: {
- success: true,
- msg: ""
- }
- };
- },
- computed: {
- classes() {
- return [
- `${elPrefixCls}`,
- {
- [`${elPrefixCls}-dragger`]: this.type === "drag",
- [`${elPrefixCls}-dragOver`]: this.type === "drag" && this.dragOver
- }
- ];
- },
- aspectRatio() {
- return ratios[this.ratioType];
- },
- limitSize() {
- const mn = this.maxSize / 1024;
- return mn >= 1 ? mn + "M" : this.maxSize + "Kb";
- }
- // accept() {
- // let formats = this.format.map(item => {
- // return "image/" + item;
- // });
- // return formats.join();
- // }
- },
- watch: {
- edit(value) {
- if (value) {
- this.$nextTick(function() {
- if (!this.$refs.editImage) {
- return;
- }
- this.cropper = new Cropper(this.$refs.editImage, {
- aspectRatio: this.aspectRatio,
- minCropBoxWidth: 120,
- minCropBoxHeight: 120 / this.aspectRatio,
- viewMode: 1
- });
- });
- } else {
- if (this.cropper) {
- this.cropper.destroy();
- this.cropper = false;
- }
- }
- },
- defImage(val) {
- this.imgUrl = val;
- }
- },
- methods: {
- isIe() {
- let userAgent = navigator.userAgent;
- let isIE =
- userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1;
- let isEdge = userAgent.indexOf("Edge") > -1 && !isIE;
- let isIE11 =
- userAgent.indexOf("Trident") > -1 && userAgent.indexOf("rv:11.0") > -1;
- return isEdge || isIE11 || isIE;
- },
- handleClick() {
- this.res = { success: true, msg: "" };
- this.$refs.input.click();
- },
- handleChange(e) {
- const files = e.target.files;
- if (!files) return;
- this.uploadFiles(files);
- this.$refs.input.value = null;
- },
- onDrop(e) {
- this.res = { success: true, msg: "" };
- this.dragOver = false;
- this.uploadFiles(e.dataTransfer.files);
- },
- uploadFiles(files) {
- let postFiles = Array.prototype.slice.call(files);
- if (postFiles.length === 0) return;
- if (this.checkValid(postFiles[0])) {
- this.openEdit(postFiles[0]);
- }
- },
- checkValid(file) {
- // check format
- if (this.format.length) {
- const _file_format = file.name
- .split(".")
- .pop()
- .toLocaleLowerCase();
- const checked = this.format.some(
- item => item.toLocaleLowerCase() === _file_format
- );
- if (!checked) {
- this.res = {
- success: false,
- msg: "只支持文件格式为" + this.format.join("/")
- };
- this.onFormatError(file);
- return false;
- }
- }
- // check maxSize
- if (this.maxSize) {
- if (file.size > this.maxSize * 1024) {
- this.res = {
- success: false,
- msg: "文件大小不能超过" + this.limitSize
- };
- this.onExceededSize(file);
- return false;
- }
- }
- this.res = {
- success: true,
- msg: ""
- };
- return true;
- },
- openEdit(file) {
- this.initFile(file);
- this.$nextTick(function() {
- this.edit = true;
- });
- },
- initFile(file) {
- this.file = {
- status: "uploading",
- name: file.name,
- size: file.size,
- type: file.type,
- url: this.createUrl(file),
- response: {},
- percentage: 0,
- showProgress: true
- };
- },
- createUrl(file) {
- let url = "";
- let URL = window.URL || window.webkitURL;
- if (URL && URL.createObjectURL) {
- url = URL.createObjectURL(file);
- }
- return url;
- },
- clearEdit() {
- this.edit = false;
- },
- editSave() {
- const { name, type } = this.file;
- const cropBoxSize = this.cropper.getCropBoxData();
- // 配置width,height之后会压缩原始图片
- const binStr = atob(
- this.cropper
- .getCroppedCanvas({
- width: cropBoxSize.width,
- height: cropBoxSize.height
- })
- .toDataURL(type)
- .split(",")[1]
- );
- let arr = new Uint8Array(binStr.length);
- for (let i = 0; i < binStr.length; i++) {
- arr[i] = binStr.charCodeAt(i);
- }
- let file;
- if (this.isIe()) {
- file = new Blob([arr], { type });
- } else {
- file = new File([arr], name, { type });
- }
- this.post(file);
- },
- post(file) {
- ajax({
- headers: this.headers,
- withCredentials: this.withCredentials,
- file: file,
- data: this.data,
- filename: this.name,
- action: this.action,
- onProgress: e => {
- this.handleProgress(e);
- },
- onSuccess: res => {
- this.edit = false;
- this.handleSuccess(res, file);
- },
- onError: (err, response) => {
- this.edit = false;
- this.handleError(err, response);
- }
- });
- },
- handleProgress(e) {
- this.onProgress(e, this.file);
- this.file.percentage = parseFloat(e.percent) || 0;
- },
- handleSuccess(res, file) {
- this.file.status = "finished";
- this.file.response = res;
- this.res = {
- success: true,
- msg: "图片上传成功!"
- };
- this.file.url = this.createUrl(file);
- this.imgUrl = this.file.url;
- this.onSuccess(res, this.file);
- setTimeout(() => {
- this.file.showProgress = false;
- }, 300);
- },
- handleError(err, response) {
- this.file.status = "fail";
- this.res = {
- success: false,
- msg: `图片上传失败:${response.msg || "请求错误"}`
- };
- this.onError(err, response, this.file);
- }
- }
- };
- </script>
|