Upload-Image

<script setup lang="ts">
import { ref, h, defineEmits, watch } from "vue"
import type { UploadFileInfo, UploadInst, UploadCustomRequestOptions } from 'naive-ui'
import { fetchCommonUpload } from "@/service/api";
import { $t } from "@/locales";
// import { toBase64 } from "@/utils/videoFrame";

/**
 * @description 图片上传
 * @param options 上传的文件
 * */
interface UploadEmits {
  (e: "success", value: string): void;
  (e: "check-validate"): void;
}
const emit = defineEmits<UploadEmits>();


type FileTypes =
  | "image/apng"
  | "image/bmp"
  | "image/gif"
  | "image/jpeg"
  | "image/pjpeg"
  | "image/png"
  | "image/svg+xml"
  | "image/tiff"
  | "image/webp"
  | "image/x-icon";

interface UploadFileProps {
  defaultUrl: string;
  filelength: number; // 文件数
  fileSize: number; // 文件大小,M
  fileType: FileTypes[];
  disabled: boolean;
  size: string;
  borderRadius: string;
}


// 接受父组件参数
const props = withDefaults(defineProps<UploadFileProps>(), {
  defaultUrl: '',
  disabled: false,
  filelength: 1,
  fileSize: 5,
  fileType: () => ["image/jpeg", "image/png", "image/gif"],
  size: "120px",
  borderRadius: "100%",
});


const fileList = ref<UploadFileInfo[]>([])

const uploadRef = ref<UploadInst | null>()

const fileUrl = ref<string>("");


watch(() => props.defaultUrl, (newValue) => {
  if (fileUrl.value === '') {
    fileUrl.value = newValue;
    console.log(fileUrl.value)
  }
});

async function handleUpload({
  file,
  data,
  headers,
  withCredentials,
  action,
  onFinish,
  onError,
  onProgress
}: UploadCustomRequestOptions) {
  // fileUrl.value = await toBase64(file.file as File)
  const res = await fetchCommonUpload(file.file as File);
  fileUrl.value = res.url
  emit("success", fileUrl.value)
}

function handleBeforeUpload(data: {
  file: UploadFileInfo
  fileList: UploadFileInfo[]
}) {
  const fileInfo = data.file
  const size = fileInfo.file?.size || props.fileSize
  // 检查文件大小
  if (size > props.fileSize * 1024 * 1024) {
    window.$message?.warning(`请上传不超过${props.fileSize}M的文件`);
    return false;
  }
  if (props.fileType.indexOf(fileInfo.type as FileTypes) < 0) {
    window.$message?.warning(`上传图片不符合所需的格式`);
    return false;
  }
  return true;
}

function handleRemove() {
  fileList.value = []
}

function handleView() {
  window.$dialog?.info({
    title: $t('common.preview'),
    content: () => {
      return h(
        "img",
        { src: fileUrl.value, class: 'w-100% object-cover' }
      )
    },
    maskClosable: true,
    showIcon: false
  });
}
</script>

<template>
  <n-upload ref="uploadRef" abstract v-model:file-list="fileList" :custom-request="handleUpload"
    :accept="props.fileType.join(',')" @before-upload="handleBeforeUpload" :disabled="props.disabled">
    <div :style="['width:' + props.size, 'height:' + props.size]"
      class="border-dotted border-1 bordre-#c0ccda border-rd-2  position-relative cursor-pointer overflow-hidden">
      <n-upload-trigger #="{ handleClick }" abstract class="size-full position-relative">
        <div class="size-full position-relative" @click="handleClick" v-if="fileUrl === ''">
          <icon-local-plus class="text-24px position-absolute absolute top-50% left-50% translate--50%" />
        </div>
        <div :style="['border-radius:' + props.borderRadius]"
          class="size-full mr-10px position-absolute overflow-hidden top-50% left-50% translate--50%" v-else>
          <img :src="fileUrl" class="size-full" />
          <div
            class="position-absolute top-0 size-full  z-index-10 opacity-0 hover:opacity-100 bg-#f9f9f999 transition-all-300 transition-ease-linear">
            <div class="position-absolute top-50% left-50% translate--50% w-80% flex flex-row justify-around">
              <icon-local-view class="text-24px" @click="handleView" />
              <icon-local-upload class="text-24px" @click="() => { handleRemove(); handleClick() }" />
              <!-- <icon-local-delete class="text-24px" @click="handleRemove" /> -->
            </div>
          </div>
        </div>
      </n-upload-trigger>
    </div>
  </n-upload>
</template>

<style scoped></style>