feat: 添加图片裁剪组件
This commit is contained in:
parent
bb62983d6e
commit
b3e10b1781
|
@ -41,6 +41,7 @@
|
||||||
"v-clipboard3": "^0.1.4",
|
"v-clipboard3": "^0.1.4",
|
||||||
"vite-plugin-monaco-editor": "^1.1.0",
|
"vite-plugin-monaco-editor": "^1.1.0",
|
||||||
"vue": "^3.2.45",
|
"vue": "^3.2.45",
|
||||||
|
"vue-cropper": "^1.0.9",
|
||||||
"vue-json-viewer": "^3.0.4",
|
"vue-json-viewer": "^3.0.4",
|
||||||
"vue-router": "^4.1.6",
|
"vue-router": "^4.1.6",
|
||||||
"vue3-json-viewer": "^2.2.2",
|
"vue3-json-viewer": "^2.2.2",
|
||||||
|
|
|
@ -34,4 +34,6 @@ export const systemVersion = () => server.get<{edition?: string}>('/system/versi
|
||||||
* @param data
|
* @param data
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const queryDashboard = (data: Record<string, any>) => server.post(`/dashboard/_multi`, data)
|
export const queryDashboard = (data: Record<string, any>) => server.post(`/dashboard/_multi`, data)
|
||||||
|
|
||||||
|
export const fileUpload = (data: any) => server.post('/file/static', data)
|
|
@ -0,0 +1,67 @@
|
||||||
|
<template>
|
||||||
|
<j-modal
|
||||||
|
title="图片编辑"
|
||||||
|
visible
|
||||||
|
:width="400"
|
||||||
|
@cancel="cancel"
|
||||||
|
@ok="ok"
|
||||||
|
:confirmLoading="loading"
|
||||||
|
>
|
||||||
|
<div style="height: 300px; width: 100%;">
|
||||||
|
<vue-cropper
|
||||||
|
ref="cropper"
|
||||||
|
:img="img"
|
||||||
|
:fixed-box="true"
|
||||||
|
:autoCrop="true"
|
||||||
|
:auto-crop-width="200"
|
||||||
|
:auto-crop-height="200"
|
||||||
|
outputType="jpg"
|
||||||
|
></vue-cropper>
|
||||||
|
</div>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="UploadCropper">
|
||||||
|
import 'vue-cropper/dist/index.css'
|
||||||
|
import { VueCropper } from 'vue-cropper';
|
||||||
|
import { fileUpload } from '@/api/comm';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
img: {
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['cancel', 'ok'])
|
||||||
|
|
||||||
|
const imgUrl = ref()
|
||||||
|
const cropper = ref()
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
const ok = () => {
|
||||||
|
cropper.value.getCropBlob(async (data: Blob) => {
|
||||||
|
console.log(data)
|
||||||
|
let formData = new FormData()
|
||||||
|
formData.append('file', data, new Date().getTime() + '.jpg')
|
||||||
|
|
||||||
|
imgUrl.value = data
|
||||||
|
loading.value = true
|
||||||
|
fileUpload(formData).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
emit('ok', res.result)
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancel = () => {
|
||||||
|
emit('cancel')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -16,12 +16,6 @@
|
||||||
>
|
>
|
||||||
<div class="upload-image-content" :style="props.style">
|
<div class="upload-image-content" :style="props.style">
|
||||||
<template v-if="imageUrl">
|
<template v-if="imageUrl">
|
||||||
<!-- <div class="upload-image"
|
|
||||||
:style="{
|
|
||||||
backgroundSize: props.backgroundSize,
|
|
||||||
backgroundImage: `url(${imageUrl})`
|
|
||||||
}"
|
|
||||||
></div> -->
|
|
||||||
<img :src="imageUrl" class="upload-image" />
|
<img :src="imageUrl" class="upload-image" />
|
||||||
<div class="upload-image-mask">点击修改</div>
|
<div class="upload-image-mask">点击修改</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -52,6 +46,12 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<ImageCropper
|
||||||
|
v-if="cropperVisible"
|
||||||
|
:img="cropperImg"
|
||||||
|
@cancel="cropperVisible = false"
|
||||||
|
@ok="saveImage"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup name='JProUpload'>
|
<script lang="ts" setup name='JProUpload'>
|
||||||
|
@ -59,8 +59,9 @@ import { UploadChangeParam, UploadProps } from 'ant-design-vue';
|
||||||
import { message } from 'jetlinks-ui-components';
|
import { message } from 'jetlinks-ui-components';
|
||||||
import { FILE_UPLOAD } from '@/api/comm';
|
import { FILE_UPLOAD } from '@/api/comm';
|
||||||
import { TOKEN_KEY } from '@/utils/variable';
|
import { TOKEN_KEY } from '@/utils/variable';
|
||||||
import { LocalStore } from '@/utils/comm';
|
import {getBase64, LocalStore} from '@/utils/comm';
|
||||||
import { CSSProperties } from 'vue';
|
import { CSSProperties } from 'vue';
|
||||||
|
import ImageCropper from './Cropper.vue'
|
||||||
|
|
||||||
type Emits = {
|
type Emits = {
|
||||||
(e: 'update:modelValue', data: string): void;
|
(e: 'update:modelValue', data: string): void;
|
||||||
|
@ -100,6 +101,9 @@ const loading = ref<boolean>(false);
|
||||||
const imageUrl = ref<string>(props?.modelValue || '');
|
const imageUrl = ref<string>(props?.modelValue || '');
|
||||||
const imageTypes = props.types ? props.types : ['image/jpeg', 'image/png'];
|
const imageTypes = props.types ? props.types : ['image/jpeg', 'image/png'];
|
||||||
|
|
||||||
|
const cropperImg = ref()
|
||||||
|
const cropperVisible = ref(false)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.modelValue,
|
() => props.modelValue,
|
||||||
(newValue) => {
|
(newValue) => {
|
||||||
|
@ -129,6 +133,7 @@ const handleChange = (info: UploadChangeParam) => {
|
||||||
|
|
||||||
const beforeUpload = (file: UploadProps['fileList'][number]) => {
|
const beforeUpload = (file: UploadProps['fileList'][number]) => {
|
||||||
const isType = imageTypes.includes(file.type);
|
const isType = imageTypes.includes(file.type);
|
||||||
|
const maxSize = props.size || 4
|
||||||
if (!isType) {
|
if (!isType) {
|
||||||
if (props.errorMessage) {
|
if (props.errorMessage) {
|
||||||
message.error(props.errorMessage);
|
message.error(props.errorMessage);
|
||||||
|
@ -137,12 +142,25 @@ const beforeUpload = (file: UploadProps['fileList'][number]) => {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const isSize = file.size / 1024 / 1024 < (props.size || 4);
|
const isSize = file.size / 1024 / 1024 < maxSize;
|
||||||
if (!isSize) {
|
if (!isSize) {
|
||||||
message.error(`图片大小必须小于${props.size || 4}M`);
|
message.error(`图片大小必须小于${maxSize}M`);
|
||||||
}
|
}
|
||||||
return isType && isSize;
|
|
||||||
|
getBase64(file, (base64Url) => {
|
||||||
|
cropperImg.value = base64Url
|
||||||
|
cropperVisible.value = true
|
||||||
|
})
|
||||||
|
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const saveImage = (url: string) => {
|
||||||
|
cropperVisible.value = false
|
||||||
|
imageUrl.value = url
|
||||||
|
emit('update:modelValue', url);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -174,4 +174,14 @@ export const openKeysByTree = (data: any[], search: any, searchKey: string = 'id
|
||||||
|
|
||||||
findKey(filterTree)
|
findKey(filterTree)
|
||||||
return openKeys
|
return openKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getBase64 = (img: File, callback: (base64Url: string) => void) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(img);
|
||||||
|
|
||||||
|
reader.onload = (result: any) => {
|
||||||
|
console.log(result)
|
||||||
|
callback(result.target.result)
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -6945,6 +6945,11 @@ vite@^4.0.0:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
|
vue-cropper@^1.0.9:
|
||||||
|
version "1.0.9"
|
||||||
|
resolved "https://registry.jetlinks.cn/vue-cropper/-/vue-cropper-1.0.9.tgz#de402c57cadc221e9a2063399ff35bb04220ef22"
|
||||||
|
integrity sha512-JhQwxmjqmQohzI7sAp5O/Rfdxuw5HOEYkKjnp/De7iCi6c8Mv6M3N9HpMt9xgWCFchX3/DfXBv2axCZOCg3G8Q==
|
||||||
|
|
||||||
vue-demi@*:
|
vue-demi@*:
|
||||||
version "0.13.11"
|
version "0.13.11"
|
||||||
resolved "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz"
|
resolved "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz"
|
||||||
|
|
Loading…
Reference in New Issue