index.vue 2.93 KB
<template>
  <Upload
    v-bind="$attrs"
    :on-before-upload="onBeforeUpload"
    :custom-request="onUpload"
    :file-list="[]"
    :show-file-list="false"
    style="width: 100%"
  >
    <template #upload-button>
      <Input :model-value="modelValue" :readonly="true" :placeholder="placeholder">
        <template #suffix>
          <Progress v-if="loading" :percent="percent" size="mini" />
          <IconUpload v-else />
        </template>
      </Input>
    </template>
  </Upload>
</template>

<script lang="ts" setup>
import { ref, watch } from 'vue';
import { IconUpload } from '@arco-design/web-vue/es/icon';
import useLoading from '@/hooks/loading';
import useOss from '@/hooks/oss';
import { Input, Message, Progress, Upload, UploadRequest, useFormItem } from '@arco-design/web-vue';

type FileType = { name: string; url: string; size: number; type: string };

const props = defineProps({
  modelValue: { type: String, default: '' },
  prefix: { type: String, default: 'file' },
  limit: { type: Number, default: 0 },
  placeholder: { type: String, default: '请选择' },
});

const emits = defineEmits<{
  (e: 'update:modelValue', value: string): void;
  (e: 'update:loading', value: boolean): void;
  (e: 'success', value: FileType): void;
  (e: 'choose-file', value: File): void;
}>();
const { eventHandlers } = useFormItem();

const { loading, setLoading } = useLoading(false);
const { upload } = useOss();
const percent = ref<number>(0);

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

  // if (startsWith(file.type, 'audio/') || startsWith(file.type, 'video/')) {
  //   const audioElement = new Audio(URL.createObjectURL(file));
  //   audioElement.addEventListener('loadedmetadata', () => emits('update:duration', audioElement.duration * 1000));
  // }

  return Promise.resolve(file);
};

const onProgress = (p: number) => {
  percent.value = p;
};

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

  if (fileItem.file) {
    setLoading(true);
    // eslint-disable-next-line vue/custom-event-name-casing
    emits('choose-file', fileItem.file as File);
    upload(fileItem.file, props.prefix, onProgress)
      .then((res) => {
        emits('update:modelValue', res?.url || '');
        // emits('change', res?.url || '');
        eventHandlers.value?.onChange?.();
        fileItem.percent = 100;
        fileItem.url = res?.url || '';
        fileItem.status = 'done';
        emits('success', { name: fileItem.name, url: fileItem.url, size: fileItem.file.size, type: fileItem.file.type });
      })
      .finally(() => {
        setLoading(false);
      });
  }

  return {};
};

watch(
  () => loading.value,
  (value) => emits('update:loading', value)
);
</script>

<style lang="less" scoped>
::v-deep(.arco-input-append) {
  padding: 0;
}
</style>