123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- <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>
- <a-button
- :class="`${prefixCls}-upload-btn`"
- @click="openModal"
- v-if="showBtn"
- v-bind="btnProps"
- >
- {{ btnText ? btnText : t('component.cropper.selectImage') }}
- </a-button>
- <CopperModal
- @register="register"
- @upload-success="handleUploadSuccess"
- :uploadApi="uploadApi"
- :src="sourceValue"
- />
- </div>
- </template>
- <script lang="ts">
- import {
- defineComponent,
- computed,
- CSSProperties,
- unref,
- ref,
- watchEffect,
- watch,
- PropType,
- } from 'vue';
- import CopperModal from './CopperModal.vue';
- import { useDesign } from '/@/hooks/web/useDesign';
- import { useModal } from '/@/components/Modal';
- import { useMessage } from '/@/hooks/web/useMessage';
- import { useI18n } from '/@/hooks/web/useI18n';
- import type { ButtonProps } from '/@/components/Button';
- import Icon from '/@/components/Icon';
- const props = {
- width: { type: [String, Number], default: '200px' },
- value: { type: String },
- showBtn: { type: Boolean, default: true },
- btnProps: { type: Object as PropType<ButtonProps> },
- btnText: { type: String, default: '' },
- uploadApi: { type: Function as PropType<({ file: Blob, name: string }) => Promise<void>> },
- };
- export default defineComponent({
- name: 'CropperAvatar',
- components: { CopperModal, Icon },
- props,
- emits: ['update:value', 'change'],
- setup(props, { emit, expose }) {
- const sourceValue = ref(props.value || '');
- const { prefixCls } = useDesign('cropper-avatar');
- const [register, { openModal, closeModal }] = useModal();
- const { createMessage } = useMessage();
- const { t } = useI18n();
- 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 });
- createMessage.success(t('component.cropper.uploadSuccess'));
- }
- expose({ openModal: openModal.bind(null, true), closeModal });
- return {
- t,
- prefixCls,
- register,
- openModal: openModal as any,
- getIconWidth,
- sourceValue,
- getClass,
- getImageWrapperStyle,
- getStyle,
- handleUploadSuccess,
- };
- },
- });
- </script>
- <style lang="less" scoped>
- @prefix-cls: ~'@{namespace}-cropper-avatar';
- .@{prefix-cls} {
- display: inline-block;
- text-align: center;
- &-image-wrapper {
- overflow: hidden;
- border: 1px solid @border-color-base;
- border-radius: 50%;
- background: @component-background;
- 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>
|