fix: Cropper typo (#2891)

This commit is contained in:
Li Kui 2023-06-28 16:52:33 +08:00 committed by GitHub
parent 155ad45848
commit 27cb958c2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 287 additions and 287 deletions

View File

@ -20,7 +20,7 @@
{{ btnText ? btnText : t('component.cropper.selectImage') }} {{ btnText ? btnText : t('component.cropper.selectImage') }}
</a-button> </a-button>
<CopperModal <CropperModal
@register="register" @register="register"
@upload-success="handleUploadSuccess" @upload-success="handleUploadSuccess"
:uploadApi="uploadApi" :uploadApi="uploadApi"
@ -39,7 +39,7 @@
watch, watch,
PropType, PropType,
} from 'vue'; } from 'vue';
import CopperModal from './CopperModal.vue'; import CropperModal from './CropperModal.vue';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { useModal } from '/@/components/Modal'; import { useModal } from '/@/components/Modal';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
@ -58,7 +58,7 @@
export default defineComponent({ export default defineComponent({
name: 'CropperAvatar', name: 'CropperAvatar',
components: { CopperModal, Icon }, components: { CropperModal, Icon },
props, props,
emits: ['update:value', 'change'], emits: ['update:value', 'change'],
setup(props, { emit, expose }) { setup(props, { emit, expose }) {

View File

@ -1,284 +1,284 @@
<template> <template>
<BasicModal <BasicModal
v-bind="$attrs" v-bind="$attrs"
@register="register" @register="register"
:title="t('component.cropper.modalTitle')" :title="t('component.cropper.modalTitle')"
width="800px" width="800px"
:canFullscreen="false" :canFullscreen="false"
@ok="handleOk" @ok="handleOk"
:okText="t('component.cropper.okText')" :okText="t('component.cropper.okText')"
> >
<div :class="prefixCls"> <div :class="prefixCls">
<div :class="`${prefixCls}-left`"> <div :class="`${prefixCls}-left`">
<div :class="`${prefixCls}-cropper`"> <div :class="`${prefixCls}-cropper`">
<CropperImage <CropperImage
v-if="src" v-if="src"
:src="src" :src="src"
height="300px" height="300px"
:circled="circled" :circled="circled"
@cropend="handleCropend" @cropend="handleCropend"
@ready="handleReady" @ready="handleReady"
/> />
</div> </div>
<div :class="`${prefixCls}-toolbar`"> <div :class="`${prefixCls}-toolbar`">
<Upload :fileList="[]" accept="image/*" :beforeUpload="handleBeforeUpload"> <Upload :fileList="[]" accept="image/*" :beforeUpload="handleBeforeUpload">
<Tooltip :title="t('component.cropper.selectImage')" placement="bottom"> <Tooltip :title="t('component.cropper.selectImage')" placement="bottom">
<a-button size="small" preIcon="ant-design:upload-outlined" type="primary" /> <a-button size="small" preIcon="ant-design:upload-outlined" type="primary" />
</Tooltip> </Tooltip>
</Upload> </Upload>
<Space> <Space>
<Tooltip :title="t('component.cropper.btn_reset')" placement="bottom"> <Tooltip :title="t('component.cropper.btn_reset')" placement="bottom">
<a-button <a-button
type="primary" type="primary"
preIcon="ant-design:reload-outlined" preIcon="ant-design:reload-outlined"
size="small" size="small"
:disabled="!src" :disabled="!src"
@click="handlerToolbar('reset')" @click="handlerToolbar('reset')"
/> />
</Tooltip> </Tooltip>
<Tooltip :title="t('component.cropper.btn_rotate_left')" placement="bottom"> <Tooltip :title="t('component.cropper.btn_rotate_left')" placement="bottom">
<a-button <a-button
type="primary" type="primary"
preIcon="ant-design:rotate-left-outlined" preIcon="ant-design:rotate-left-outlined"
size="small" size="small"
:disabled="!src" :disabled="!src"
@click="handlerToolbar('rotate', -45)" @click="handlerToolbar('rotate', -45)"
/> />
</Tooltip> </Tooltip>
<Tooltip :title="t('component.cropper.btn_rotate_right')" placement="bottom"> <Tooltip :title="t('component.cropper.btn_rotate_right')" placement="bottom">
<a-button <a-button
type="primary" type="primary"
preIcon="ant-design:rotate-right-outlined" preIcon="ant-design:rotate-right-outlined"
size="small" size="small"
:disabled="!src" :disabled="!src"
@click="handlerToolbar('rotate', 45)" @click="handlerToolbar('rotate', 45)"
/> />
</Tooltip> </Tooltip>
<Tooltip :title="t('component.cropper.btn_scale_x')" placement="bottom"> <Tooltip :title="t('component.cropper.btn_scale_x')" placement="bottom">
<a-button <a-button
type="primary" type="primary"
preIcon="vaadin:arrows-long-h" preIcon="vaadin:arrows-long-h"
size="small" size="small"
:disabled="!src" :disabled="!src"
@click="handlerToolbar('scaleX')" @click="handlerToolbar('scaleX')"
/> />
</Tooltip> </Tooltip>
<Tooltip :title="t('component.cropper.btn_scale_y')" placement="bottom"> <Tooltip :title="t('component.cropper.btn_scale_y')" placement="bottom">
<a-button <a-button
type="primary" type="primary"
preIcon="vaadin:arrows-long-v" preIcon="vaadin:arrows-long-v"
size="small" size="small"
:disabled="!src" :disabled="!src"
@click="handlerToolbar('scaleY')" @click="handlerToolbar('scaleY')"
/> />
</Tooltip> </Tooltip>
<Tooltip :title="t('component.cropper.btn_zoom_in')" placement="bottom"> <Tooltip :title="t('component.cropper.btn_zoom_in')" placement="bottom">
<a-button <a-button
type="primary" type="primary"
preIcon="ant-design:zoom-in-outlined" preIcon="ant-design:zoom-in-outlined"
size="small" size="small"
:disabled="!src" :disabled="!src"
@click="handlerToolbar('zoom', 0.1)" @click="handlerToolbar('zoom', 0.1)"
/> />
</Tooltip> </Tooltip>
<Tooltip :title="t('component.cropper.btn_zoom_out')" placement="bottom"> <Tooltip :title="t('component.cropper.btn_zoom_out')" placement="bottom">
<a-button <a-button
type="primary" type="primary"
preIcon="ant-design:zoom-out-outlined" preIcon="ant-design:zoom-out-outlined"
size="small" size="small"
:disabled="!src" :disabled="!src"
@click="handlerToolbar('zoom', -0.1)" @click="handlerToolbar('zoom', -0.1)"
/> />
</Tooltip> </Tooltip>
</Space> </Space>
</div> </div>
</div> </div>
<div :class="`${prefixCls}-right`"> <div :class="`${prefixCls}-right`">
<div :class="`${prefixCls}-preview`"> <div :class="`${prefixCls}-preview`">
<img :src="previewSource" v-if="previewSource" :alt="t('component.cropper.preview')" /> <img :src="previewSource" v-if="previewSource" :alt="t('component.cropper.preview')" />
</div> </div>
<template v-if="previewSource"> <template v-if="previewSource">
<div :class="`${prefixCls}-group`"> <div :class="`${prefixCls}-group`">
<Avatar :src="previewSource" size="large" /> <Avatar :src="previewSource" size="large" />
<Avatar :src="previewSource" :size="48" /> <Avatar :src="previewSource" :size="48" />
<Avatar :src="previewSource" :size="64" /> <Avatar :src="previewSource" :size="64" />
<Avatar :src="previewSource" :size="80" /> <Avatar :src="previewSource" :size="80" />
</div> </div>
</template> </template>
</div> </div>
</div> </div>
</BasicModal> </BasicModal>
</template> </template>
<script lang="ts"> <script lang="ts">
import type { CropendResult, Cropper } from './typing'; import type { CropendResult, Cropper } from './typing';
import { defineComponent, ref, PropType } from 'vue'; import { defineComponent, ref, PropType } from 'vue';
import CropperImage from './Cropper.vue'; import CropperImage from './Cropper.vue';
import { Space, Upload, Avatar, Tooltip } from 'ant-design-vue'; import { Space, Upload, Avatar, Tooltip } from 'ant-design-vue';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { BasicModal, useModalInner } from '/@/components/Modal'; import { BasicModal, useModalInner } from '/@/components/Modal';
import { dataURLtoBlob } from '/@/utils/file/base64Conver'; import { dataURLtoBlob } from '/@/utils/file/base64Conver';
import { isFunction } from '/@/utils/is'; import { isFunction } from '/@/utils/is';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
type apiFunParams = { file: Blob; name: string; filename: string }; type apiFunParams = { file: Blob; name: string; filename: string };
const props = { const props = {
circled: { type: Boolean, default: true }, circled: { type: Boolean, default: true },
uploadApi: { uploadApi: {
type: Function as PropType<(params: apiFunParams) => Promise<any>>, type: Function as PropType<(params: apiFunParams) => Promise<any>>,
}, },
src: { type: String }, src: { type: String },
}; };
export default defineComponent({ export default defineComponent({
name: 'CropperModal', name: 'CropperModal',
components: { BasicModal, Space, CropperImage, Upload, Avatar, Tooltip }, components: { BasicModal, Space, CropperImage, Upload, Avatar, Tooltip },
props, props,
emits: ['uploadSuccess', 'register'], emits: ['uploadSuccess', 'register'],
setup(props, { emit }) { setup(props, { emit }) {
let filename = ''; let filename = '';
const src = ref(props.src || ''); const src = ref(props.src || '');
const previewSource = ref(''); const previewSource = ref('');
const cropper = ref<Cropper>(); const cropper = ref<Cropper>();
let scaleX = 1; let scaleX = 1;
let scaleY = 1; let scaleY = 1;
const { prefixCls } = useDesign('cropper-am'); const { prefixCls } = useDesign('cropper-am');
const [register, { closeModal, setModalProps }] = useModalInner(); const [register, { closeModal, setModalProps }] = useModalInner();
const { t } = useI18n(); const { t } = useI18n();
// Block upload // Block upload
function handleBeforeUpload(file: File) { function handleBeforeUpload(file: File) {
const reader = new FileReader(); const reader = new FileReader();
reader.readAsDataURL(file); reader.readAsDataURL(file);
src.value = ''; src.value = '';
previewSource.value = ''; previewSource.value = '';
reader.onload = function (e) { reader.onload = function (e) {
src.value = (e.target?.result as string) ?? ''; src.value = (e.target?.result as string) ?? '';
filename = file.name; filename = file.name;
}; };
return false; return false;
} }
function handleCropend({ imgBase64 }: CropendResult) { function handleCropend({ imgBase64 }: CropendResult) {
previewSource.value = imgBase64; previewSource.value = imgBase64;
} }
function handleReady(cropperInstance: Cropper) { function handleReady(cropperInstance: Cropper) {
cropper.value = cropperInstance; cropper.value = cropperInstance;
} }
function handlerToolbar(event: string, arg?: number) { function handlerToolbar(event: string, arg?: number) {
if (event === 'scaleX') { if (event === 'scaleX') {
scaleX = arg = scaleX === -1 ? 1 : -1; scaleX = arg = scaleX === -1 ? 1 : -1;
} }
if (event === 'scaleY') { if (event === 'scaleY') {
scaleY = arg = scaleY === -1 ? 1 : -1; scaleY = arg = scaleY === -1 ? 1 : -1;
} }
cropper?.value?.[event]?.(arg); cropper?.value?.[event]?.(arg);
} }
async function handleOk() { async function handleOk() {
const uploadApi = props.uploadApi; const uploadApi = props.uploadApi;
if (uploadApi && isFunction(uploadApi)) { if (uploadApi && isFunction(uploadApi)) {
const blob = dataURLtoBlob(previewSource.value); const blob = dataURLtoBlob(previewSource.value);
try { try {
setModalProps({ confirmLoading: true }); setModalProps({ confirmLoading: true });
const result = await uploadApi({ name: 'file', file: blob, filename }); const result = await uploadApi({ name: 'file', file: blob, filename });
emit('uploadSuccess', { source: previewSource.value, data: result.url }); emit('uploadSuccess', { source: previewSource.value, data: result.url });
closeModal(); closeModal();
} finally { } finally {
setModalProps({ confirmLoading: false }); setModalProps({ confirmLoading: false });
} }
} }
} }
return { return {
t, t,
prefixCls, prefixCls,
src, src,
register, register,
previewSource, previewSource,
handleBeforeUpload, handleBeforeUpload,
handleCropend, handleCropend,
handleReady, handleReady,
handlerToolbar, handlerToolbar,
handleOk, handleOk,
}; };
}, },
}); });
</script> </script>
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-cropper-am'; @prefix-cls: ~'@{namespace}-cropper-am';
.@{prefix-cls} { .@{prefix-cls} {
display: flex; display: flex;
&-left, &-left,
&-right { &-right {
height: 340px; height: 340px;
} }
&-left { &-left {
width: 55%; width: 55%;
} }
&-right { &-right {
width: 45%; width: 45%;
} }
&-cropper { &-cropper {
height: 300px; height: 300px;
background: #eee; background: #eee;
background-image: linear-gradient( background-image: linear-gradient(
45deg, 45deg,
rgb(0 0 0 / 25%) 25%, rgb(0 0 0 / 25%) 25%,
transparent 0, transparent 0,
transparent 75%, transparent 75%,
rgb(0 0 0 / 25%) 0 rgb(0 0 0 / 25%) 0
), ),
linear-gradient( linear-gradient(
45deg, 45deg,
rgb(0 0 0 / 25%) 25%, rgb(0 0 0 / 25%) 25%,
transparent 0, transparent 0,
transparent 75%, transparent 75%,
rgb(0 0 0 / 25%) 0 rgb(0 0 0 / 25%) 0
); );
background-position: 0 0, 12px 12px; background-position: 0 0, 12px 12px;
background-size: 24px 24px; background-size: 24px 24px;
} }
&-toolbar { &-toolbar {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
margin-top: 10px; margin-top: 10px;
} }
&-preview { &-preview {
width: 220px; width: 220px;
height: 220px; height: 220px;
margin: 0 auto; margin: 0 auto;
overflow: hidden; overflow: hidden;
border: 1px solid @border-color-base; border: 1px solid @border-color-base;
border-radius: 50%; border-radius: 50%;
img { img {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
} }
&-group { &-group {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-around; justify-content: space-around;
margin-top: 8px; margin-top: 8px;
padding-top: 8px; padding-top: 8px;
border-top: 1px solid @border-color-base; border-top: 1px solid @border-color-base;
} }
} }
</style> </style>