index.vue 2.36 KB
<template>
  <Upload :accept="accept" :custom-request="onUpload" :on-before-upload="onBeforeUpload" :file-list="[]" :show-file-list="false">
    <template #upload-button>
      <Avatar :shape="shape" :size="size" trigger-type="mask">
        <div v-if="!modelValue" class="no-avatar">
          <icon-plus />
        </div>
        <img v-else :src="modelValue" style="width: 100%; height: 100%" alt="" />
        <template #trigger-icon>
          <icon-camera />
        </template>
      </Avatar>
    </template>
  </Upload>
</template>

<script lang="ts" setup>
  import useOss from '@/hooks/oss';
  import { IconCamera, IconPlus } from '@arco-design/web-vue/es/icon';
  import { Avatar, Message, Upload, UploadRequest, useFormItem } from '@arco-design/web-vue';
  import { computed, toRefs } from 'vue';

  const props = withDefaults(
    defineProps<{
      modelValue?: string;
      size?: number;
      shape?: 'circle' | 'square';
      accept?: string;
      limit?: number;
    }>(),
    {
      modelValue: '',
      size: 80,
      shape: 'circle',
      accept: 'image/*',
      limit: 5,
    }
  );

  const { shape } = toRefs(props);

  const borderRadius = computed(() => (shape.value === 'square' ? 'unset' : 'var(--border-radius-circle)'));

  const { eventHandlers } = useFormItem();

  const emits = defineEmits(['update:modelValue']);

  const { upload } = useOss();

  const onBeforeUpload = (file: File) => {
    if (file.size > props.limit * 1024 * 1024) {
      Message.warning(`${file.name} 文件超过${props.limit}MB,无法上传`);
      return Promise.resolve(false);
    }

    return Promise.resolve(file);
  };

  const onUpload = (option: any): UploadRequest => {
    const { fileItem } = option;

    if (fileItem.file) {
      upload(fileItem.file, 'image').then((res) => {
        emits('update:modelValue', res?.url || '');
        eventHandlers.value?.onChange?.();
      });
    }

    return {};
  };
</script>

<style lang="less" scoped>
  :deep(.arco-avatar-text) {
    width: 100%;
    height: 100%;
    overflow: hidden;
    display: inline-block;
    border-radius: v-bind(borderRadius);
  }

  .arco-avatar-image {
    transform: unset !important;
  }

  .no-avatar {
    top: 0;
    left: 0;
    display: flex;
    position: absolute;
    align-content: center;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
  }
</style>