chore: 🔨 移除 vue-picture-cropper 插件(ts兼容问题)
This commit is contained in:
@@ -65,7 +65,6 @@
|
||||
"sortablejs": "^1.15.6",
|
||||
"vue": "^3.5.13",
|
||||
"vue-i18n": "9.9.1",
|
||||
"vue-picture-cropper": "^0.7.0",
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
/**
|
||||
* 图片压缩类
|
||||
* @param minSize
|
||||
* @param maxSize
|
||||
* @constructor
|
||||
*/
|
||||
export function PhotoCompress(minSize, maxSize) {
|
||||
var nextQ = 0.5; // 压缩比例
|
||||
var maxQ = 1;
|
||||
var minQ = 0;
|
||||
|
||||
/**
|
||||
* 将base64转换为文件
|
||||
* @param base64Codes base64编码
|
||||
* @param fileName 文件名称
|
||||
* @returns {*}
|
||||
*/
|
||||
PhotoCompress.prototype.dataUrlToFile = function (base64Codes, fileName = new Date().getTime()) {
|
||||
var arr = base64Codes.split(","),
|
||||
mime = arr[0].match(/:(.*?);/)[1],
|
||||
bStr = atob(arr[1]),
|
||||
n = bStr.length,
|
||||
u8arr = new Uint8Array(n);
|
||||
while (n--) {
|
||||
u8arr[n] = bStr.charCodeAt(n);
|
||||
}
|
||||
return new File([u8arr], fileName, { type: mime });
|
||||
};
|
||||
|
||||
/**
|
||||
* 图片压缩
|
||||
* @param file 文件
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
PhotoCompress.prototype.compress = function (file, callback) {
|
||||
var self = this;
|
||||
self.imgBase64(file, function (image, canvas) {
|
||||
var base64Codes = canvas.toDataURL(file.type, nextQ); // y压缩
|
||||
var compressFile = self.dataUrlToFile(base64Codes, file.name.split(".")[0]); // 转成file文件
|
||||
var compressFileSize = compressFile.size; // 压缩后文件大小 k
|
||||
// console.log("图片质量:" + nextQ);
|
||||
// console.log("压缩后文件大小:" + compressFileSize / 1024);
|
||||
if (compressFileSize > maxSize) {
|
||||
// 压缩后文件大于最大值
|
||||
maxQ = nextQ;
|
||||
nextQ = (nextQ + minQ) / 2; // 质量降低
|
||||
self.compress(file, callback);
|
||||
} else if (compressFileSize < minSize) {
|
||||
// 压缩以后文件小于最小值
|
||||
minQ = nextQ;
|
||||
nextQ = (nextQ + maxQ) / 2; // 质量提高
|
||||
self.compress(file, callback);
|
||||
} else {
|
||||
callback(compressFile);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 将图片转化为base64
|
||||
* @param file 文件
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
PhotoCompress.prototype.imgBase64 = function (file, callback) {
|
||||
// 看支持不支持FileReader
|
||||
if (!file || !window.FileReader) return;
|
||||
var image = new Image();
|
||||
// 绑定 load 事件处理器,加载完成后执行
|
||||
image.onload = function () {
|
||||
var canvas = document.createElement("canvas");
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
canvas.width = image.width * nextQ;
|
||||
canvas.height = image.height * nextQ;
|
||||
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
|
||||
callback(image, canvas);
|
||||
};
|
||||
if (/^image/.test(file.type)) {
|
||||
// 创建一个reader
|
||||
var reader = new FileReader();
|
||||
// 将图片将转成 base64 格式
|
||||
reader.readAsDataURL(file);
|
||||
// 读取成功后的回调
|
||||
reader.onload = function () {
|
||||
// self.imgUrls.push(this.result);
|
||||
// 设置src属性,浏览器会自动加载。
|
||||
// 记住必须先绑定事件,才能设置src属性,否则会出同步问题。
|
||||
image.src = this.result;
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="isShowModal"
|
||||
title="裁剪图片"
|
||||
width="50%"
|
||||
:close-on-press-escape="false"
|
||||
:close-on-click-modal="false"
|
||||
align-center
|
||||
@close="cancel"
|
||||
>
|
||||
<div class="modal-content">
|
||||
<VuePictureCropper
|
||||
:img="pic"
|
||||
:boxStyle="corpBoxStyle"
|
||||
:options="corpOptions"
|
||||
:presetMode="corpPresetMode"
|
||||
@ready="ready"
|
||||
/>
|
||||
</div>
|
||||
<div class="m-t-10 btns">
|
||||
<el-button class="default-btn" @click="cancel">取消</el-button>
|
||||
<el-button v-if="showClear" class="default-btn" @click="clear">关闭</el-button>
|
||||
<el-button v-if="showReset" class="default-btn" @click="reset">重置</el-button>
|
||||
<el-button class="default-btn primary" @click="getResult">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 组件文档:https://cropper.chengpeiquan.com/zh/quick-start.html
|
||||
import VuePictureCropper, { cropper } from "vue-picture-cropper";
|
||||
import { PhotoCompress } from "./compressUtil.js";
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
},
|
||||
image: {
|
||||
type: [String, File],
|
||||
},
|
||||
presetMode: {
|
||||
type: Object,
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
},
|
||||
boxStyle: {
|
||||
type: Object,
|
||||
},
|
||||
showClear: {
|
||||
// 是否显示 Clear 按钮
|
||||
type: Boolean,
|
||||
},
|
||||
showReset: {
|
||||
// 是否显示 Reset 按钮
|
||||
type: Boolean,
|
||||
},
|
||||
isCompress: {
|
||||
// 是否压缩裁剪后的图片大小
|
||||
type: Boolean,
|
||||
},
|
||||
compressSize: {
|
||||
// 压缩图片大小限制
|
||||
type: Object,
|
||||
default: {
|
||||
maxSize: 0.3 * 1024 * 1024, // 300k
|
||||
minSize: 0.03 * 1024 * 1024, // 30k
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/** 裁剪组件属性配置 */
|
||||
const corpOptions = computed(() => {
|
||||
return {
|
||||
viewMode: 1, // 裁剪框范围 1-只能在图片区域内活动
|
||||
dragMode: "move", // 可选值 crop(可绘制裁剪框) | move(仅移动)
|
||||
// aspectRatio: 1 / 1,// 裁剪框的宽高比
|
||||
cropBoxResizable: false, // 是否可以调整裁剪区的大小
|
||||
...props.options,
|
||||
};
|
||||
});
|
||||
/** 裁剪区域的样式 */
|
||||
const corpBoxStyle = computed(() => {
|
||||
return {
|
||||
width: "auto",
|
||||
height: "400px",
|
||||
backgroundColor: "#f8f8f8",
|
||||
margin: "auto",
|
||||
...props.boxStyle,
|
||||
};
|
||||
});
|
||||
|
||||
/** 预设模式配置 */
|
||||
const corpPresetMode = computed(() => {
|
||||
return {
|
||||
mode: "fixedSize",
|
||||
width: 200,
|
||||
height: 200,
|
||||
...props.presetMode,
|
||||
};
|
||||
});
|
||||
const emits = defineEmits(["close"]);
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(val) => {
|
||||
isShowModal.value = val;
|
||||
if (!val) {
|
||||
pic.value = "";
|
||||
result.dataURL = "";
|
||||
result.blobURL = "";
|
||||
reset();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.image,
|
||||
(val) => {
|
||||
pic.value = val;
|
||||
}
|
||||
);
|
||||
|
||||
const isShowModal = ref(false);
|
||||
const pic = ref("");
|
||||
const result = reactive({
|
||||
dataURL: "",
|
||||
blobURL: "",
|
||||
});
|
||||
|
||||
async function getResult() {
|
||||
if (!cropper) return;
|
||||
const base64 = cropper.getDataURL();
|
||||
const blob = await cropper.getBlob();
|
||||
if (!blob) return;
|
||||
|
||||
let file = await cropper.getFile({
|
||||
fileName: "locales.fileName",
|
||||
});
|
||||
|
||||
result.dataURL = base64;
|
||||
result.blobURL = URL.createObjectURL(blob);
|
||||
const { minSize, maxSize } = props.compressSize;
|
||||
// console.log(file.size, maxSize)
|
||||
// 是否限制文件大小
|
||||
if (props.isCompress && file.size > maxSize) {
|
||||
// console.log("compress")
|
||||
const photoCompress = new PhotoCompress(minSize, maxSize);
|
||||
photoCompress.compress(file, function (f) {
|
||||
const r = new FileReader();
|
||||
r.readAsDataURL(f);
|
||||
r.onload = function (e) {
|
||||
emits("close", { result, file: f });
|
||||
};
|
||||
});
|
||||
} else {
|
||||
// console.log("crop")
|
||||
emits("close", { result, file });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the crop box
|
||||
*/
|
||||
function clear() {
|
||||
if (!cropper) return;
|
||||
cropper.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the default cropping area
|
||||
*/
|
||||
function reset() {
|
||||
if (!cropper) return;
|
||||
cropper.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* The ready event passed to Cropper.js
|
||||
*/
|
||||
function ready() {
|
||||
// console.log('Cropper is ready.')
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
emits("close");
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.modal-wrap {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 99;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btns {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
@@ -1,211 +0,0 @@
|
||||
<template>
|
||||
<el-upload
|
||||
v-model:file-list="fileList"
|
||||
list-type="picture-card"
|
||||
:auto-upload="true"
|
||||
:class="fileList.length >= 1 || !props.showUploadBtn ? 'hide' : 'show'"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:data="props.data"
|
||||
:name="props.name"
|
||||
:accept="props.accept"
|
||||
:limit="1"
|
||||
>
|
||||
<i-ep-plus />
|
||||
{{ title }}
|
||||
<template #file="{ file }">
|
||||
<div style="width: 100%">
|
||||
<img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
|
||||
<span class="el-upload-list__item-actions">
|
||||
<span class="el-upload-list__item-preview" @click="previewImg(file)">
|
||||
<el-icon><zoom-in /></el-icon>
|
||||
</span>
|
||||
<span
|
||||
v-if="props.showDelBtn"
|
||||
class="el-upload-list__item-delete"
|
||||
@click="handleRemove(file)"
|
||||
>
|
||||
<el-icon><Delete /></el-icon>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
|
||||
<el-image-viewer
|
||||
v-if="viewVisible"
|
||||
:zoom-rate="1.2"
|
||||
:initialIndex="initialIndex"
|
||||
:url-list="[viewFile]"
|
||||
@close="closePreview"
|
||||
/>
|
||||
|
||||
<CorpperDialog
|
||||
:visible="isShowCorpper"
|
||||
:image="selectPic"
|
||||
:isCompress="true"
|
||||
:showReset="true"
|
||||
:presetMode="presetMode"
|
||||
@close="hideCorpper"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import CorpperDialog from "./corpperDialog.vue";
|
||||
|
||||
import { UploadRawFile, UploadUserFile, UploadFile, UploadProps } from "element-plus";
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* 文件路径
|
||||
*/
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: "上传图片",
|
||||
},
|
||||
|
||||
/**
|
||||
* 上传文件的参数名
|
||||
*/
|
||||
name: {
|
||||
type: String,
|
||||
default: "file",
|
||||
},
|
||||
/**
|
||||
* 文件上传数量限制
|
||||
*/
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
/**
|
||||
* 是否显示删除按钮
|
||||
*/
|
||||
showDelBtn: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
/**
|
||||
* 是否显示上传按钮
|
||||
*/
|
||||
showUploadBtn: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
/**
|
||||
* 单张图片最大大小
|
||||
*/
|
||||
uploadMaxSize: {
|
||||
type: Number,
|
||||
default: 2 * 1024 * 1024,
|
||||
},
|
||||
/**
|
||||
* 上传文件类型
|
||||
*/
|
||||
accept: {
|
||||
type: String,
|
||||
default: "image/*",
|
||||
},
|
||||
|
||||
presetMode: {
|
||||
type: Object,
|
||||
default: "{ width: 200, height: 200 }",
|
||||
},
|
||||
});
|
||||
|
||||
const selectPic = ref("");
|
||||
const isShowCorpper = ref(false);
|
||||
|
||||
const viewVisible = ref(false);
|
||||
const initialIndex = ref(0);
|
||||
|
||||
const fileList = ref([] as UploadUserFile[]);
|
||||
const viewFile = ref(props.modelValue as string);
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal: string) => {
|
||||
if (!newVal) {
|
||||
fileList.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
fileList.value = [{ url: newVal } as UploadUserFile];
|
||||
|
||||
viewFile.value = newVal;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
/**
|
||||
* 删除图片
|
||||
*/
|
||||
function handleRemove(removeFile: UploadFile) {
|
||||
const filePath = removeFile.url;
|
||||
if (filePath) {
|
||||
emit("update:modelValue", "");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 限制用户上传文件的格式和大小
|
||||
*/
|
||||
function handleBeforeUpload(file: UploadRawFile) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
selectPic.value = String(reader.result);
|
||||
isShowCorpper.value = true;
|
||||
};
|
||||
|
||||
if (file.size > props.uploadMaxSize) {
|
||||
ElMessage.warning("上传图片不能大于 " + Math.trunc(props.uploadMaxSize / 1024 / 1024) + "M");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预览图片
|
||||
*/
|
||||
const previewImg: UploadProps["onPreview"] = (uploadFile: UploadFile) => {
|
||||
viewFile.value = uploadFile.url!;
|
||||
initialIndex.value = 0;
|
||||
viewVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭预览
|
||||
*/
|
||||
const closePreview = () => {
|
||||
viewVisible.value = false;
|
||||
};
|
||||
|
||||
function hideCorpper(data: any) {
|
||||
isShowCorpper.value = false;
|
||||
if (data) {
|
||||
const {
|
||||
result: { dataURL },
|
||||
file,
|
||||
} = data;
|
||||
emit("update:modelValue", dataURL);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.hide {
|
||||
:deep(.el-upload--picture-card) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.show {
|
||||
:deep(.el-upload--picture-card) {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1
src/types/components.d.ts
vendored
1
src/types/components.d.ts
vendored
@@ -91,7 +91,6 @@ declare module "vue" {
|
||||
ThemeColorPicker: (typeof import("./../layout/components/Settings/components/ThemeColorPicker.vue"))["default"];
|
||||
SingleImageUpload: (typeof import("./../components/Upload/SingleImageUpload.vue"))["default"];
|
||||
WangEditor: (typeof import("./../components/WangEditor/index.vue"))["default"];
|
||||
ImgCorpper: (typeof import("./../components/ImgCorpper/index.vue"))["default"];
|
||||
}
|
||||
export interface ComponentCustomProperties {
|
||||
vLoading: (typeof import("element-plus/es"))["ElLoadingDirective"];
|
||||
|
||||
@@ -23,22 +23,6 @@
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="单图裁剪">
|
||||
<ImgCorpper
|
||||
v-model="cprpperValue"
|
||||
:presetMode="{ width: 295, height: 413 }"
|
||||
:title="'上传图片'"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="参数说明">
|
||||
<el-table :data="imageCprpperUploadArgData" border>
|
||||
<el-table-column prop="argsName" label="参数名称" width="300" />
|
||||
<el-table-column prop="type" label="参数类型" width="200" />
|
||||
<el-table-column prop="default" label="默认值" width="400" />
|
||||
<el-table-column prop="desc" label="描述" width="300" />
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="多图上传">
|
||||
<MultiImageUpload v-model="picUrls" />
|
||||
</el-form-item>
|
||||
|
||||
Reference in New Issue
Block a user