Skip to content

Commit

Permalink
feat: add cropper-demo (#325)
Browse files Browse the repository at this point in the history
  • Loading branch information
DesignHhuang committed Jan 15, 2024
1 parent baad5a9 commit e1e56e3
Show file tree
Hide file tree
Showing 15 changed files with 844 additions and 9 deletions.
82 changes: 82 additions & 0 deletions apps/admin/src/pages/demo/Cropper.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { CropperImage, CropperAvatar } from '@vben/components'
import img from '@/assets/images/header.jpg'
const info = ref('')
const cropperImg = ref('')
const circleInfo = ref('')
const circleImg = ref('')
const avatar = ref('')
function handleCropend({ imgBase64, imgInfo }) {
info.value = imgInfo
cropperImg.value = imgBase64
}
function handleCircleCropend({ imgBase64, imgInfo }) {
circleInfo.value = imgInfo
circleImg.value = imgBase64
}
</script>

<template>
<VbenCard
title="图片裁剪示例"
content="需要开启测试接口服务才能进行上传测试!"
>
<VbenCard title="头像裁剪">
<CropperAvatar :value="avatar" />
</VbenCard>

<VbenCard title="矩形裁剪" class="my-4">
<div class="container p-4">
<div class="cropper-container mr-10">
<CropperImage
ref="refCropper"
:src="img"
@cropend="handleCropend"
style="width: 40vw"
/>
</div>
<img :src="cropperImg" class="croppered" v-if="cropperImg" alt="" />
</div>
<p v-if="cropperImg">裁剪后图片信息:{{ info }}</p>
</VbenCard>

<VbenCard title="圆形裁剪">
<div class="container p-4">
<div class="cropper-container mr-10">
<CropperImage
ref="refCropper"
:src="img"
@cropend="handleCircleCropend"
style="width: 40vw"
circled
/>
</div>
<img :src="circleImg" class="croppered" v-if="circleImg" />
</div>
<p v-if="circleImg">裁剪后图片信息:{{ circleInfo }}</p>
</VbenCard>
</VbenCard>
</template>

<style scoped>
.container {
display: flex;
align-items: center;
width: 100vw;
}
.cropper-container {
width: 40vw;
}
.croppered {
height: 360px;
}
p {
margin: 10px;
}
</style>
2 changes: 2 additions & 0 deletions packages/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export { default as CountDownInput } from './src/countdown-input/index.vue'
export { default as StrengthMeter } from './src/strength-meter/index.vue'
export { default as ClickOutside } from './src/click-outside/index.vue'
export { default as IconPicker } from './src/icon-picker/index.vue'
export { default as CropperImage } from './src/cropper/index.vue'
export { default as CropperAvatar } from './src/cropper/cropper-avatar.vue'

export { default as CollapseTransition } from './src/transition/collapse-transition.vue'
export { default as CustomTransition } from './src/transition/index'
Expand Down
3 changes: 2 additions & 1 deletion packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"@zxcvbn-ts/core": "^3.0.4",
"qrcode": "^1.5.3",
"vue": "3.3.6",
"vue-router": "^4.2.4"
"vue-router": "^4.2.4",
"cropperjs": "^1.6.1"
},
"devDependencies": {
"@iconify/json": "^2.2.115",
Expand Down
161 changes: 161 additions & 0 deletions packages/components/src/cropper/cropper-avatar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<script lang="ts" setup>
import {
computed,
CSSProperties,
unref,
ref,
watchEffect,
watch,
PropType,
} from 'vue'
import CropperModal from './cropper-modal.vue'
import { useI18n } from '@vben/locale'
import Icon from '../icon/index.vue'
defineOptions({ name: 'CropperAvatar' })
const props = defineProps({
width: { type: [String, Number], default: '200px' },
value: { type: String },
showBtn: { type: Boolean, default: true },
btnProps: { type: Object },
btnText: { type: String, default: '' },
uploadApi: {
type: Function as PropType<
({ file, name }: { file: Blob; name: string }) => Promise<void>
>,
},
size: { type: Number, default: 5 },
})
const emit = defineEmits(['update:value', 'change'])
const sourceValue = ref(props.value || '')
const prefixCls = 'cropper-avatar'
const { t } = useI18n()
const cropperModelRef = ref()
const openModal = () => {
unref(cropperModelRef).showModal = true
}
const closeModal = () => {
unref(cropperModelRef).showModal = false
}
const getClass = computed(() => [prefixCls])
const getWidth = computed(() => `${props.width}`.replace(/px/, '') + 'px')
const getIconWidth = computed(
() => parseInt(`${props.width}`.replace(/px/, '')) / 2 + 'px',
)
const getStyle = computed((): CSSProperties => ({ width: unref(getWidth) }))
const getImageWrapperStyle = computed(
(): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) }),
)
watchEffect(() => {
sourceValue.value = props.value || ''
})
watch(
() => sourceValue.value,
(v: string) => {
emit('update:value', v)
},
)
function handleUploadSuccess({ source, data }) {
sourceValue.value = source
emit('change', { source, data })
console.log(t('component.cropper.uploadSuccess'))
}
defineExpose({ openModal, closeModal })
</script>

<template>
<div :class="getClass" :style="getStyle">
<div
:class="`${prefixCls}-image-wrapper`"
:style="getImageWrapperStyle"
@click="openModal()"
>
<div :class="`${prefixCls}-image-mask`" :style="getImageWrapperStyle">
<Icon
icon="ant-design:cloud-upload-outlined"
:size="getIconWidth"
:style="getImageWrapperStyle"
color="#d6d6d6"
/>
</div>
<img :src="sourceValue" v-if="sourceValue" alt="avatar" />
</div>
<vben-button
:class="`${prefixCls}-upload-btn`"
@click="openModal"
v-if="showBtn"
v-bind="btnProps"
>
{{ btnText ? btnText : t('component.cropper.selectImage') }}
</vben-button>

<CropperModal
ref="cropperModelRef"
@upload-success="handleUploadSuccess"
:uploadApi="uploadApi"
:src="sourceValue"
:size="size"
@close="closeModal"
/>
</div>
</template>

<style lang="less" scoped>
@prefix-cls: ~'cropper-avatar';
.@{prefix-cls} {
display: inline-block;
text-align: center;
&-image-wrapper {
overflow: hidden;
border: 1px solid hsv(0, 0, 85%);
border-radius: 50%;
background: #fff;
cursor: pointer;
img {
width: 100%;
}
}
&-image-mask {
position: absolute;
width: inherit;
height: inherit;
transition: opacity 0.4s;
border: inherit;
border-radius: inherit;
opacity: 0;
background: rgb(0 0 0 / 40%);
cursor: pointer;
::v-deep(svg) {
margin: auto;
}
}
&-image-mask:hover {
opacity: 40;
}
&-upload-btn {
margin: 10px auto;
}
}
</style>
Loading

0 comments on commit e1e56e3

Please sign in to comment.