index.vue 4.28 KB
<script setup lang="ts">
/**
 * 单个图片上传公共组件
 * @module Pagination
 */
import { ref, reactive } from "vue";
// import * as store from "@/pinia/index";
import { useLoginStore } from '@/pinia/login';
import { ElMessage } from "element-plus";
import { Plus } from "@element-plus/icons-vue";
import type {
  ElUploadProgressEvent,
  ElFile,
} from "element-plus/es/components/upload/src/upload.type";
/**
 * @description 组件属性
 * @prop {string | null} tagName 标签
 * @prop {number | null} maxSize 允许上传的最大图片尺寸 default = 1 means 1MB
 * @prop {array<string>} formats 允许上传的图片格式
 * @prop {string | null} defaultUrl 默认图片
 * @prop {string | null} moduleName 上传接口需要传的module值
 * @prop {string | null} showType 组件类型,横图=horizontal(4:3),竖图=vertical(3:4),正图=square(1:1),小正图=mini(1:1),default=null=""=horizontal
 * @prop {string} tip 上传须知
 */
interface Props {
  tagName: string;
  maxSize?: number;
  formats?: string[];
  defaultUrl?: object;
  showType?: string;
  moduleName?: string;
  tip?: string;
}
const props = withDefaults(defineProps<Props>(), {
  formats: () => [
    "image/jpeg",
    "image/gif",
    "image/png",
    "image/x-icon",
    "image/svg+xml",
    "image/webp",
  ],
  maxSize: 1, 
  defaultUrl: {
    type: Object,
    default: {
      value: ''
    }
  },
  showType: "horizontal",
  moduleName: "test",
  tip: "支持16:9 的图片,建议最佳尺寸:750*422,大小不能超过500KB",
});
/**
 * @description 上传图片接口配置
 */
const uploadSettings = reactive({
  uploadUrl: `${import.meta.env.VITE_BASE_URL}upload/uploadPicSource?module=${
    props.moduleName
  }`,
  headers: {
    type: "formData",
    // "X-Token": store.family.loginStore().token,
    "X-Token": useLoginStore()?.getToken
  },
});
/**
 * @method handleAvatarSuccess
 * @description 图片上传成功
 * @param {object} res 接口返回信息
 */
const handleAvatarSuccess = (res: ElUploadProgressEvent) => {
  ElMessage.success("图片上传成功");
  props.defaultUrl.value = res?.data;
  emit("imageChange", res?.data, props.tagName);
};
/**
 * @method beforeAvatarUpload
 * @description 图片上传之前检查格式、最大尺寸
 * @param {ElFile} file 文件对象
 */
const beforeAvatarUpload = (file: ElFile) => {
  const isFormatAllowed = props.formats.includes(file.type);
  const isSizeAllowed = file.size / 1024 / 1024 < props.maxSize;
  if (!isFormatAllowed) {
    ElMessage.error("图片格式不支持");
  }
  if (!isSizeAllowed) {
    ElMessage.error("图片尺寸过大");
  }
  return isFormatAllowed && isSizeAllowed;
};
/**
 * 上传文件失败回调
 * @param { ElUploadProgressEvent } 回调返回对象
 */
const handlError = (err: ElUploadProgressEvent) => {
  console.log(err);
  ElMessage.error("上传失败,请重试");
};
const emit = defineEmits<{
  (e: "imageChange", url: string, tagName: string): void;
}>();
</script>

<template>
  <div class="uploader-container">
    <el-upload
      class="avatar-uploader"
      :with-credentials="true"
      :show-file-list="false"
      :action="uploadSettings.uploadUrl"
      :headers="uploadSettings.headers"
      name="multipartFile"
      :on-success="handleAvatarSuccess"
      :on-error="handlError"
      :before-upload="beforeAvatarUpload"
    >
      <img
        v-if="props.defaultUrl.value"
        :src="props.defaultUrl.value"
        class="avatar"
        :class="props.showType"
      />
      <el-icon v-else class="avatar-uploader-icon" :class="props.showType"
        ><plus
      /></el-icon>
    </el-upload>
    <div class="tip">{{ props.tip }}</div>
  </div>
</template>

<style scoped lang="scss">
.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
.avatar-uploader .el-upload:hover {
  border-color: #409eff;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  text-align: center;
  border: 1px dashed #d9d9d9;
}
.avatar {
  display: block;
}

.square {
  width: 178px;
  height: 178px;
  line-height: 178px;
}
.mini {
  width: 90px;
  height: 90px;
  line-height: 90px;
}
.horizontal {
  width: 180px;
  height: 135px;
  line-height: 135px;
}
.vertical {
  width: 135px;
  height: 180px;
  line-height: 180px;
}
</style>