|
|
@ -22,14 +22,14 @@ |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div :class="`${prefixCls}-toolbar`"> |
|
|
<div :class="`${prefixCls}-toolbar`"> |
|
|
<Upload :fileList="[]" accept="image/*" :beforeUpload="handleBeforeUpload"> |
|
|
<Upload :fileList="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" /> |
|
|
<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 |
|
|
<Button |
|
|
type="primary" |
|
|
type="primary" |
|
|
preIcon="ant-design:reload-outlined" |
|
|
preIcon="ant-design:reload-outlined" |
|
|
size="small" |
|
|
size="small" |
|
|
@ -38,7 +38,7 @@ |
|
|
/> |
|
|
/> |
|
|
</Tooltip> |
|
|
</Tooltip> |
|
|
<Tooltip :title="t('component.cropper.btn_rotate_left')" placement="bottom"> |
|
|
<Tooltip :title="t('component.cropper.btn_rotate_left')" placement="bottom"> |
|
|
<a-button |
|
|
<Button |
|
|
type="primary" |
|
|
type="primary" |
|
|
preIcon="ant-design:rotate-left-outlined" |
|
|
preIcon="ant-design:rotate-left-outlined" |
|
|
size="small" |
|
|
size="small" |
|
|
@ -47,7 +47,7 @@ |
|
|
/> |
|
|
/> |
|
|
</Tooltip> |
|
|
</Tooltip> |
|
|
<Tooltip :title="t('component.cropper.btn_rotate_right')" placement="bottom"> |
|
|
<Tooltip :title="t('component.cropper.btn_rotate_right')" placement="bottom"> |
|
|
<a-button |
|
|
<Button |
|
|
type="primary" |
|
|
type="primary" |
|
|
preIcon="ant-design:rotate-right-outlined" |
|
|
preIcon="ant-design:rotate-right-outlined" |
|
|
size="small" |
|
|
size="small" |
|
|
@ -56,7 +56,7 @@ |
|
|
/> |
|
|
/> |
|
|
</Tooltip> |
|
|
</Tooltip> |
|
|
<Tooltip :title="t('component.cropper.btn_scale_x')" placement="bottom"> |
|
|
<Tooltip :title="t('component.cropper.btn_scale_x')" placement="bottom"> |
|
|
<a-button |
|
|
<Button |
|
|
type="primary" |
|
|
type="primary" |
|
|
preIcon="vaadin:arrows-long-h" |
|
|
preIcon="vaadin:arrows-long-h" |
|
|
size="small" |
|
|
size="small" |
|
|
@ -65,7 +65,7 @@ |
|
|
/> |
|
|
/> |
|
|
</Tooltip> |
|
|
</Tooltip> |
|
|
<Tooltip :title="t('component.cropper.btn_scale_y')" placement="bottom"> |
|
|
<Tooltip :title="t('component.cropper.btn_scale_y')" placement="bottom"> |
|
|
<a-button |
|
|
<Button |
|
|
type="primary" |
|
|
type="primary" |
|
|
preIcon="vaadin:arrows-long-v" |
|
|
preIcon="vaadin:arrows-long-v" |
|
|
size="small" |
|
|
size="small" |
|
|
@ -74,7 +74,7 @@ |
|
|
/> |
|
|
/> |
|
|
</Tooltip> |
|
|
</Tooltip> |
|
|
<Tooltip :title="t('component.cropper.btn_zoom_in')" placement="bottom"> |
|
|
<Tooltip :title="t('component.cropper.btn_zoom_in')" placement="bottom"> |
|
|
<a-button |
|
|
<Button |
|
|
type="primary" |
|
|
type="primary" |
|
|
preIcon="ant-design:zoom-in-outlined" |
|
|
preIcon="ant-design:zoom-in-outlined" |
|
|
size="small" |
|
|
size="small" |
|
|
@ -83,7 +83,7 @@ |
|
|
/> |
|
|
/> |
|
|
</Tooltip> |
|
|
</Tooltip> |
|
|
<Tooltip :title="t('component.cropper.btn_zoom_out')" placement="bottom"> |
|
|
<Tooltip :title="t('component.cropper.btn_zoom_out')" placement="bottom"> |
|
|
<a-button |
|
|
<Button |
|
|
type="primary" |
|
|
type="primary" |
|
|
preIcon="ant-design:zoom-out-outlined" |
|
|
preIcon="ant-design:zoom-out-outlined" |
|
|
size="small" |
|
|
size="small" |
|
|
@ -110,13 +110,15 @@ |
|
|
</div> |
|
|
</div> |
|
|
</BasicModal> |
|
|
</BasicModal> |
|
|
</template> |
|
|
</template> |
|
|
<script lang="ts"> |
|
|
<script lang="ts" setup> |
|
|
import type { CropendResult, Cropper } from './typing'; |
|
|
import type { CropendResult, Cropper } from './typing'; |
|
|
|
|
|
import type { UploadProps } from 'ant-design-vue'; |
|
|
|
|
|
|
|
|
import { defineComponent, ref } from 'vue'; |
|
|
import { ref } 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 { Button } from '/@/components/Button'; |
|
|
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'; |
|
|
@ -124,90 +126,70 @@ |
|
|
|
|
|
|
|
|
type apiFunParams = { file: Blob; name: string; filename: string }; |
|
|
type apiFunParams = { file: Blob; name: string; filename: string }; |
|
|
|
|
|
|
|
|
const props = { |
|
|
const emits = defineEmits(['uploadSuccess', 'register']); |
|
|
|
|
|
const props = defineProps({ |
|
|
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>>, |
|
|
}, |
|
|
}, |
|
|
}; |
|
|
}); |
|
|
|
|
|
let filename = ''; |
|
|
export default defineComponent({ |
|
|
const src = ref(''); |
|
|
name: 'CropperModal', |
|
|
const previewSource = ref(''); |
|
|
components: { BasicModal, Space, CropperImage, Upload, Avatar, Tooltip }, |
|
|
const cropper = ref<Cropper>(); |
|
|
props, |
|
|
const fileList = ref<UploadProps['fileList']>([]); |
|
|
emits: ['uploadSuccess', 'register'], |
|
|
let scaleX = 1; |
|
|
setup(props, { emit }) { |
|
|
let scaleY = 1; |
|
|
let filename = ''; |
|
|
|
|
|
const src = ref(''); |
|
|
|
|
|
const previewSource = ref(''); |
|
|
|
|
|
const cropper = ref<Cropper>(); |
|
|
|
|
|
let scaleX = 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.data }); |
|
|
emits('uploadSuccess', { source: previewSource.value, data: result.data }); |
|
|
closeModal(); |
|
|
closeModal(); |
|
|
} finally { |
|
|
} finally { |
|
|
setModalProps({ confirmLoading: false }); |
|
|
setModalProps({ confirmLoading: false }); |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
return { |
|
|
} |
|
|
t, |
|
|
|
|
|
prefixCls, |
|
|
|
|
|
src, |
|
|
|
|
|
register, |
|
|
|
|
|
previewSource, |
|
|
|
|
|
handleBeforeUpload, |
|
|
|
|
|
handleCropend, |
|
|
|
|
|
handleReady, |
|
|
|
|
|
handlerToolbar, |
|
|
|
|
|
handleOk, |
|
|
|
|
|
}; |
|
|
|
|
|
}, |
|
|
|
|
|
}); |
|
|
|
|
|
</script> |
|
|
</script> |
|
|
|
|
|
|
|
|
<style lang="less"> |
|
|
<style lang="less"> |
|
|
|