| 1 |  | -<template> | 
| 2 |  | -  <div :class="getClass" :style="getStyle"> | 
| 3 |  | -    <div :class="`${prefixCls}-image-wrapper`" :style="getImageWrapperStyle" @click="openModal"> | 
| 4 |  | -      <div :class="`${prefixCls}-image-mask`" :style="getImageWrapperStyle"> | 
| 5 |  | -        <Icon | 
| 6 |  | -          icon="ant-design:cloud-upload-outlined" | 
| 7 |  | -          :size="getIconWidth" | 
| 8 |  | -          :style="getImageWrapperStyle" | 
| 9 |  | -          color="#d6d6d6" | 
| 10 |  | -        /> | 
| 11 |  | -      </div> | 
| 12 |  | -      <img :src="sourceValue" v-if="sourceValue" alt="avatar" /> | 
| 13 |  | -    </div> | 
| 14 |  | -    <a-button | 
| 15 |  | -      :class="`${prefixCls}-upload-btn`" | 
| 16 |  | -      @click="openModal" | 
| 17 |  | -      v-if="showBtn" | 
| 18 |  | -      v-bind="btnProps" | 
| 19 |  | -    > | 
| 20 |  | -      {{ btnText ? btnText : t('component.cropper.selectImage') }} | 
| 21 |  | -    </a-button> | 
| 22 |  | - | 
| 23 |  | -    <CopperModal | 
| 24 |  | -      @register="register" | 
| 25 |  | -      @uploadSuccess="handleUploadSuccess" | 
| 26 |  | -      :uploadApi="uploadApi" | 
| 27 |  | -      :src="sourceValue" | 
| 28 |  | -    /> | 
| 29 |  | -  </div> | 
| 30 |  | -</template> | 
| 31 |  | -<script lang="ts"> | 
| 32 |  | -  import { | 
| 33 |  | -    defineComponent, | 
| 34 |  | -    computed, | 
| 35 |  | -    CSSProperties, | 
| 36 |  | -    unref, | 
| 37 |  | -    ref, | 
| 38 |  | -    watchEffect, | 
| 39 |  | -    watch, | 
| 40 |  | -    PropType, | 
| 41 |  | -  } from 'vue'; | 
| 42 |  | -  import CopperModal from './CopperModal.vue'; | 
| 43 |  | -  import { useDesign } from '/@/hooks/web/useDesign'; | 
| 44 |  | -  import { useModal } from '/@/components/Modal'; | 
| 45 |  | -  import { useMessage } from '/@/hooks/web/useMessage'; | 
| 46 |  | -  import { useI18n } from '/@/hooks/web/useI18n'; | 
| 47 |  | -  import type { ButtonProps } from '/@/components/Button'; | 
| 48 |  | -  import Icon from '/@/components/Icon'; | 
| 49 |  | - | 
| 50 |  | -  const props = { | 
| 51 |  | -    width: { type: [String, Number], default: '200px' }, | 
| 52 |  | -    value: { type: String }, | 
| 53 |  | -    showBtn: { type: Boolean, default: true }, | 
| 54 |  | -    btnProps: { type: Object as PropType<ButtonProps> }, | 
| 55 |  | -    btnText: { type: String, default: '' }, | 
| 56 |  | -    uploadApi: { type: Function as PropType<({ file: Blob, name: string }) => Promise<void>> }, | 
| 57 |  | -  }; | 
| 58 |  | - | 
| 59 |  | -  export default defineComponent({ | 
| 60 |  | -    name: 'CropperAvatar', | 
| 61 |  | -    components: { CopperModal, Icon }, | 
| 62 |  | -    props, | 
| 63 |  | -    emits: ['update:value', 'change'], | 
| 64 |  | -    setup(props, { emit, expose }) { | 
| 65 |  | -      const sourceValue = ref(props.value || ''); | 
| 66 |  | -      const { prefixCls } = useDesign('cropper-avatar'); | 
| 67 |  | -      const [register, { openModal, closeModal }] = useModal(); | 
| 68 |  | -      const { createMessage } = useMessage(); | 
| 69 |  | -      const { t } = useI18n(); | 
| 70 |  | - | 
| 71 |  | -      const getClass = computed(() => [prefixCls]); | 
| 72 |  | - | 
| 73 |  | -      const getWidth = computed(() => `${props.width}`.replace(/px/, '') + 'px'); | 
| 74 |  | - | 
| 75 |  | -      const getIconWidth = computed(() => parseInt(`${props.width}`.replace(/px/, '')) / 2 + 'px'); | 
| 76 |  | - | 
| 77 |  | -      const getStyle = computed((): CSSProperties => ({ width: unref(getWidth) })); | 
| 78 |  | - | 
| 79 |  | -      const getImageWrapperStyle = computed( | 
| 80 |  | -        (): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) }) | 
| 81 |  | -      ); | 
| 82 |  | - | 
| 83 |  | -      watchEffect(() => { | 
| 84 |  | -        sourceValue.value = props.value || ''; | 
| 85 |  | -      }); | 
| 86 |  | - | 
| 87 |  | -      watch( | 
| 88 |  | -        () => sourceValue.value, | 
| 89 |  | -        (v: string) => { | 
| 90 |  | -          emit('update:value', v); | 
| 91 |  | -        } | 
| 92 |  | -      ); | 
| 93 |  | - | 
| 94 |  | -      function handleUploadSuccess(source) { | 
| 95 |  | -        sourceValue.value = source; | 
| 96 |  | -        emit('change', source); | 
| 97 |  | -        createMessage.success(t('component.cropper.uploadSuccess')); | 
| 98 |  | -      } | 
| 99 |  | - | 
| 100 |  | -      expose({ openModal: openModal.bind(null, true), closeModal }); | 
| 101 |  | - | 
| 102 |  | -      return { | 
| 103 |  | -        t, | 
| 104 |  | -        prefixCls, | 
| 105 |  | -        register, | 
| 106 |  | -        openModal, | 
| 107 |  | -        getIconWidth, | 
| 108 |  | -        sourceValue, | 
| 109 |  | -        getClass, | 
| 110 |  | -        getImageWrapperStyle, | 
| 111 |  | -        getStyle, | 
| 112 |  | -        handleUploadSuccess, | 
| 113 |  | -      }; | 
| 114 |  | -    }, | 
| 115 |  | -  }); | 
| 116 |  | -</script> | 
| 117 |  | - | 
| 118 |  | -<style lang="less" scoped> | 
| 119 |  | -  @prefix-cls: ~'@{namespace}-cropper-avatar'; | 
| 120 |  | - | 
| 121 |  | -  .@{prefix-cls} { | 
| 122 |  | -    display: inline-block; | 
| 123 |  | -    text-align: center; | 
| 124 |  | - | 
| 125 |  | -    &-image-wrapper { | 
| 126 |  | -      overflow: hidden; | 
| 127 |  | -      cursor: pointer; | 
| 128 |  | -      background: @component-background; | 
| 129 |  | -      border: 1px solid @border-color-base; | 
| 130 |  | -      border-radius: 50%; | 
| 131 |  | - | 
| 132 |  | -      img { | 
| 133 |  | -        width: 100%; | 
| 134 |  | -      } | 
| 135 |  | -    } | 
| 136 |  | - | 
| 137 |  | -    &-image-mask { | 
| 138 |  | -      opacity: 0; | 
| 139 |  | -      position: absolute; | 
| 140 |  | -      width: inherit; | 
| 141 |  | -      height: inherit; | 
| 142 |  | -      border-radius: inherit; | 
| 143 |  | -      border: inherit; | 
| 144 |  | -      background: rgba(0, 0, 0, 0.4); | 
| 145 |  | -      cursor: pointer; | 
| 146 |  | -      -webkit-transition: opacity 0.4s; | 
| 147 |  | -      transition: opacity 0.4s; | 
| 148 |  | - | 
| 149 |  | -      :deep(svg) { | 
| 150 |  | -        margin: auto; | 
| 151 |  | -      } | 
| 152 |  | -    } | 
| 153 |  | - | 
| 154 |  | -    &-image-mask:hover { | 
| 155 |  | -      opacity: 40; | 
| 156 |  | -    } | 
| 157 |  | - | 
| 158 |  | -    &-upload-btn { | 
| 159 |  | -      margin: 10px auto; | 
| 160 |  | -    } | 
| 161 |  | -  } | 
| 162 |  | -</style> | 
|  | 1 | +<template> | 
|  | 2 | +  <div :class="getClass" :style="getStyle"> | 
|  | 3 | +    <div :class="`${prefixCls}-image-wrapper`" :style="getImageWrapperStyle" @click="openModal"> | 
|  | 4 | +      <div :class="`${prefixCls}-image-mask`" :style="getImageWrapperStyle"> | 
|  | 5 | +        <Icon | 
|  | 6 | +          icon="ant-design:cloud-upload-outlined" | 
|  | 7 | +          :size="getIconWidth" | 
|  | 8 | +          :style="getImageWrapperStyle" | 
|  | 9 | +          color="#d6d6d6" | 
|  | 10 | +        /> | 
|  | 11 | +      </div> | 
|  | 12 | +      <img :src="sourceValue" v-if="sourceValue" alt="avatar" /> | 
|  | 13 | +    </div> | 
|  | 14 | +    <a-button | 
|  | 15 | +      :class="`${prefixCls}-upload-btn`" | 
|  | 16 | +      @click="openModal" | 
|  | 17 | +      v-if="showBtn" | 
|  | 18 | +      v-bind="btnProps" | 
|  | 19 | +    > | 
|  | 20 | +      {{ btnText ? btnText : t('component.cropper.selectImage') }} | 
|  | 21 | +    </a-button> | 
|  | 22 | + | 
|  | 23 | +    <CopperModal | 
|  | 24 | +      @register="register" | 
|  | 25 | +      @uploadSuccess="handleUploadSuccess" | 
|  | 26 | +      :uploadApi="uploadApi" | 
|  | 27 | +      :src="sourceValue" | 
|  | 28 | +    /> | 
|  | 29 | +  </div> | 
|  | 30 | +</template> | 
|  | 31 | +<script lang="ts"> | 
|  | 32 | +  import { | 
|  | 33 | +    defineComponent, | 
|  | 34 | +    computed, | 
|  | 35 | +    CSSProperties, | 
|  | 36 | +    unref, | 
|  | 37 | +    ref, | 
|  | 38 | +    watchEffect, | 
|  | 39 | +    watch, | 
|  | 40 | +    PropType, | 
|  | 41 | +  } from 'vue'; | 
|  | 42 | +  import CopperModal from './CopperModal.vue'; | 
|  | 43 | +  import { useDesign } from '/@/hooks/web/useDesign'; | 
|  | 44 | +  import { useModal } from '/@/components/Modal'; | 
|  | 45 | +  import { useMessage } from '/@/hooks/web/useMessage'; | 
|  | 46 | +  import { useI18n } from '/@/hooks/web/useI18n'; | 
|  | 47 | +  import type { ButtonProps } from '/@/components/Button'; | 
|  | 48 | +  import Icon from '/@/components/Icon'; | 
|  | 49 | + | 
|  | 50 | +  const props = { | 
|  | 51 | +    width: { type: [String, Number], default: '200px' }, | 
|  | 52 | +    value: { type: String }, | 
|  | 53 | +    showBtn: { type: Boolean, default: true }, | 
|  | 54 | +    btnProps: { type: Object as PropType<ButtonProps> }, | 
|  | 55 | +    btnText: { type: String, default: '' }, | 
|  | 56 | +    uploadApi: { type: Function as PropType<({ file: Blob, name: string }) => Promise<void>> }, | 
|  | 57 | +  }; | 
|  | 58 | + | 
|  | 59 | +  export default defineComponent({ | 
|  | 60 | +    name: 'CropperAvatar', | 
|  | 61 | +    components: { CopperModal, Icon }, | 
|  | 62 | +    props, | 
|  | 63 | +    emits: ['update:value', 'change'], | 
|  | 64 | +    setup(props, { emit, expose }) { | 
|  | 65 | +      const sourceValue = ref(props.value || ''); | 
|  | 66 | +      const { prefixCls } = useDesign('cropper-avatar'); | 
|  | 67 | +      const [register, { openModal, closeModal }] = useModal(); | 
|  | 68 | +      const { createMessage } = useMessage(); | 
|  | 69 | +      const { t } = useI18n(); | 
|  | 70 | + | 
|  | 71 | +      const getClass = computed(() => [prefixCls]); | 
|  | 72 | + | 
|  | 73 | +      const getWidth = computed(() => `${props.width}`.replace(/px/, '') + 'px'); | 
|  | 74 | + | 
|  | 75 | +      const getIconWidth = computed(() => parseInt(`${props.width}`.replace(/px/, '')) / 2 + 'px'); | 
|  | 76 | + | 
|  | 77 | +      const getStyle = computed((): CSSProperties => ({ width: unref(getWidth) })); | 
|  | 78 | + | 
|  | 79 | +      const getImageWrapperStyle = computed( | 
|  | 80 | +        (): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) }) | 
|  | 81 | +      ); | 
|  | 82 | + | 
|  | 83 | +      watchEffect(() => { | 
|  | 84 | +        sourceValue.value = props.value || ''; | 
|  | 85 | +      }); | 
|  | 86 | + | 
|  | 87 | +      watch( | 
|  | 88 | +        () => sourceValue.value, | 
|  | 89 | +        (v: string) => { | 
|  | 90 | +          emit('update:value', v); | 
|  | 91 | +        } | 
|  | 92 | +      ); | 
|  | 93 | + | 
|  | 94 | +      function handleUploadSuccess(source) { | 
|  | 95 | +        sourceValue.value = source.data; | 
|  | 96 | +        emit('change', source); | 
|  | 97 | +        createMessage.success(t('component.cropper.uploadSuccess')); | 
|  | 98 | +      } | 
|  | 99 | + | 
|  | 100 | +      expose({ openModal: openModal.bind(null, true), closeModal }); | 
|  | 101 | + | 
|  | 102 | +      return { | 
|  | 103 | +        t, | 
|  | 104 | +        prefixCls, | 
|  | 105 | +        register, | 
|  | 106 | +        openModal, | 
|  | 107 | +        getIconWidth, | 
|  | 108 | +        sourceValue, | 
|  | 109 | +        getClass, | 
|  | 110 | +        getImageWrapperStyle, | 
|  | 111 | +        getStyle, | 
|  | 112 | +        handleUploadSuccess, | 
|  | 113 | +      }; | 
|  | 114 | +    }, | 
|  | 115 | +  }); | 
|  | 116 | +</script> | 
|  | 117 | + | 
|  | 118 | +<style lang="less" scoped> | 
|  | 119 | +  @prefix-cls: ~'@{namespace}-cropper-avatar'; | 
|  | 120 | + | 
|  | 121 | +  .@{prefix-cls} { | 
|  | 122 | +    display: inline-block; | 
|  | 123 | +    text-align: center; | 
|  | 124 | + | 
|  | 125 | +    &-image-wrapper { | 
|  | 126 | +      overflow: hidden; | 
|  | 127 | +      cursor: pointer; | 
|  | 128 | +      background: @component-background; | 
|  | 129 | +      border: 1px solid @border-color-base; | 
|  | 130 | +      border-radius: 50%; | 
|  | 131 | + | 
|  | 132 | +      img { | 
|  | 133 | +        width: 100%; | 
|  | 134 | +      } | 
|  | 135 | +    } | 
|  | 136 | + | 
|  | 137 | +    &-image-mask { | 
|  | 138 | +      opacity: 0; | 
|  | 139 | +      position: absolute; | 
|  | 140 | +      width: inherit; | 
|  | 141 | +      height: inherit; | 
|  | 142 | +      border-radius: inherit; | 
|  | 143 | +      border: inherit; | 
|  | 144 | +      background: rgba(0, 0, 0, 0.4); | 
|  | 145 | +      cursor: pointer; | 
|  | 146 | +      -webkit-transition: opacity 0.4s; | 
|  | 147 | +      transition: opacity 0.4s; | 
|  | 148 | + | 
|  | 149 | +      :deep(svg) { | 
|  | 150 | +        margin: auto; | 
|  | 151 | +      } | 
|  | 152 | +    } | 
|  | 153 | + | 
|  | 154 | +    &-image-mask:hover { | 
|  | 155 | +      opacity: 40; | 
|  | 156 | +    } | 
|  | 157 | + | 
|  | 158 | +    &-upload-btn { | 
|  | 159 | +      margin: 10px auto; | 
|  | 160 | +    } | 
|  | 161 | +  } | 
|  | 162 | +</style> | 
... | ... |  |