index.vue 2.24 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>