From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目
---
src/components/Cropper/src/CopperModal.vue | 261 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 261 insertions(+), 0 deletions(-)
diff --git a/src/components/Cropper/src/CopperModal.vue b/src/components/Cropper/src/CopperModal.vue
new file mode 100644
index 0000000..d9a4e34
--- /dev/null
+++ b/src/components/Cropper/src/CopperModal.vue
@@ -0,0 +1,261 @@
+<template>
+ <div @click.stop>
+ <Dialog
+ v-model="dialogVisible"
+ :canFullscreen="false"
+ :title="t('cropper.modalTitle')"
+ maxHeight="380px"
+ width="800px"
+ >
+ <div :class="prefixCls">
+ <div :class="`${prefixCls}-left`">
+ <div :class="`${prefixCls}-cropper`">
+ <CropperImage
+ v-if="src"
+ :circled="circled"
+ :src="src"
+ height="300px"
+ @cropend="handleCropend"
+ @ready="handleReady"
+ />
+ </div>
+
+ <div :class="`${prefixCls}-toolbar`">
+ <el-upload :beforeUpload="handleBeforeUpload" :fileList="[]" accept="image/*">
+ <el-tooltip :content="t('cropper.selectImage')" placement="bottom">
+ <XButton preIcon="ant-design:upload-outlined" type="primary" />
+ </el-tooltip>
+ </el-upload>
+ <el-space>
+ <el-tooltip :content="t('cropper.btn_reset')" placement="bottom">
+ <XButton
+ :disabled="!src"
+ preIcon="ant-design:reload-outlined"
+ size="small"
+ type="primary"
+ @click="handlerToolbar('reset')"
+ />
+ </el-tooltip>
+ <el-tooltip :content="t('cropper.btn_rotate_left')" placement="bottom">
+ <XButton
+ :disabled="!src"
+ preIcon="ant-design:rotate-left-outlined"
+ size="small"
+ type="primary"
+ @click="handlerToolbar('rotate', -45)"
+ />
+ </el-tooltip>
+ <el-tooltip :content="t('cropper.btn_rotate_right')" placement="bottom">
+ <XButton
+ :disabled="!src"
+ preIcon="ant-design:rotate-right-outlined"
+ size="small"
+ type="primary"
+ @click="handlerToolbar('rotate', 45)"
+ />
+ </el-tooltip>
+ <el-tooltip :content="t('cropper.btn_scale_x')" placement="bottom">
+ <XButton
+ :disabled="!src"
+ preIcon="vaadin:arrows-long-h"
+ size="small"
+ type="primary"
+ @click="handlerToolbar('scaleX')"
+ />
+ </el-tooltip>
+ <el-tooltip :content="t('cropper.btn_scale_y')" placement="bottom">
+ <XButton
+ :disabled="!src"
+ preIcon="vaadin:arrows-long-v"
+ size="small"
+ type="primary"
+ @click="handlerToolbar('scaleY')"
+ />
+ </el-tooltip>
+ <el-tooltip :content="t('cropper.btn_zoom_in')" placement="bottom">
+ <XButton
+ :disabled="!src"
+ preIcon="ant-design:zoom-in-outlined"
+ size="small"
+ type="primary"
+ @click="handlerToolbar('zoom', 0.1)"
+ />
+ </el-tooltip>
+ <el-tooltip :content="t('cropper.btn_zoom_out')" placement="bottom">
+ <XButton
+ :disabled="!src"
+ preIcon="ant-design:zoom-out-outlined"
+ size="small"
+ type="primary"
+ @click="handlerToolbar('zoom', -0.1)"
+ />
+ </el-tooltip>
+ </el-space>
+ </div>
+ </div>
+ <div :class="`${prefixCls}-right`">
+ <div :class="`${prefixCls}-preview`">
+ <img v-if="previewSource" :alt="t('cropper.preview')" :src="previewSource" />
+ </div>
+ <template v-if="previewSource">
+ <div :class="`${prefixCls}-group`">
+ <el-avatar :src="previewSource" size="large" />
+ <el-avatar :size="48" :src="previewSource" />
+ <el-avatar :size="64" :src="previewSource" />
+ <el-avatar :size="80" :src="previewSource" />
+ </div>
+ </template>
+ </div>
+ </div>
+ <template #footer>
+ <el-button type="primary" @click="handleOk">{{ t('cropper.okText') }}</el-button>
+ </template>
+ </Dialog>
+ </div>
+</template>
+<script lang="ts" setup>
+import { useDesign } from '@/hooks/web/useDesign'
+import { dataURLtoBlob } from '@/utils/filt'
+import { useI18n } from 'vue-i18n'
+import type { CropendResult, Cropper } from './types'
+import { propTypes } from '@/utils/propTypes'
+import { CropperImage } from '@/components/Cropper'
+
+defineOptions({ name: 'CopperModal' })
+
+const props = defineProps({
+ srcValue: propTypes.string.def(''),
+ circled: propTypes.bool.def(true)
+})
+const emit = defineEmits(['uploadSuccess'])
+const { t } = useI18n()
+const { getPrefixCls } = useDesign()
+const prefixCls = getPrefixCls('cropper-am')
+
+const src = ref(props.srcValue)
+const previewSource = ref('')
+const cropper = ref<Cropper>()
+const dialogVisible = ref(false)
+let filename = ''
+let scaleX = 1
+let scaleY = 1
+
+// Block upload
+function handleBeforeUpload(file: File) {
+ const reader = new FileReader()
+ reader.readAsDataURL(file)
+ src.value = ''
+ previewSource.value = ''
+ reader.onload = function (e) {
+ src.value = (e.target?.result as string) ?? ''
+ filename = file.name
+ }
+ return false
+}
+
+function handleCropend({ imgBase64 }: CropendResult) {
+ previewSource.value = imgBase64
+}
+
+function handleReady(cropperInstance: Cropper) {
+ cropper.value = cropperInstance
+}
+
+function handlerToolbar(event: string, arg?: number) {
+ if (event === 'scaleX') {
+ scaleX = arg = scaleX === -1 ? 1 : -1
+ }
+ if (event === 'scaleY') {
+ scaleY = arg = scaleY === -1 ? 1 : -1
+ }
+ cropper?.value?.[event]?.(arg)
+}
+
+async function handleOk() {
+ const blob = dataURLtoBlob(previewSource.value)
+ emit('uploadSuccess', { source: previewSource.value, data: blob, filename: filename })
+}
+
+function openModal() {
+ dialogVisible.value = true
+}
+
+function closeModal() {
+ dialogVisible.value = false
+}
+
+defineExpose({ openModal, closeModal })
+</script>
+<style lang="scss">
+$prefix-cls: #{$namespace}-cropper-am;
+
+.#{$prefix-cls} {
+ display: flex;
+
+ &-left,
+ &-right {
+ height: 340px;
+ }
+
+ &-left {
+ width: 55%;
+ }
+
+ &-right {
+ width: 45%;
+ }
+
+ &-cropper {
+ height: 300px;
+ background: #eee;
+ background-image: linear-gradient(
+ 45deg,
+ rgb(0 0 0 / 25%) 25%,
+ transparent 0,
+ transparent 75%,
+ rgb(0 0 0 / 25%) 0
+ ),
+ linear-gradient(
+ 45deg,
+ rgb(0 0 0 / 25%) 25%,
+ transparent 0,
+ transparent 75%,
+ rgb(0 0 0 / 25%) 0
+ );
+ background-position:
+ 0 0,
+ 12px 12px;
+ background-size: 24px 24px;
+ }
+
+ &-toolbar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: 10px;
+ }
+
+ &-preview {
+ width: 220px;
+ height: 220px;
+ margin: 0 auto;
+ overflow: hidden;
+ border: 1px solid;
+ border-radius: 50%;
+
+ img {
+ width: 100%;
+ height: 100%;
+ }
+ }
+
+ &-group {
+ display: flex;
+ padding-top: 8px;
+ margin-top: 8px;
+ border-top: 1px solid;
+ justify-content: space-around;
+ align-items: center;
+ }
+}
+</style>
--
Gitblit v1.8.0