<template>
  <div class="upload-file">
    <el-upload
      ref="fileUploadRef"
      multiple
      :action="realUploadUrl"
      :before-upload="handleBeforeUpload"
      :file-list="fileList"
      :limit="limit"
      :on-error="handleUploadError"
      :on-exceed="handleExceed"
      :on-success="handleUploadSuccess"
      :show-file-list="showFileList"
      :on-preview="handlePreview"
      :headers="headers"
      class="upload-file-uploader"
      :list-type="isConstruction ? 'picture-card' : 'text'"
      :accept="accept"
      :drag="isDarg"
      :data="data"
      :auto-upload="autoUpload"
      :on-change="handleChange"
      :on-remove="handleRemove"
      :method="method"
      :http-request="customUpload"
    >
      <slot>
        <div>
          <!-- 上传按钮 -->
          <el-button v-if="!isConstruction && !isImportInfo && !isDarg" type="primary">选取文件</el-button>

          <!-- 上传提示 -->
          <el-icon v-if="isDarg" class="el-icon--upload"><upload-filled /></el-icon>
          <div v-if="showTip" class="el-upload__tip" @click.stop>
            请上传
            <template v-if="fileSize">
              大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
            </template>
            <template v-if="fileType">
              格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
            </template>
            的文件
          </div>
          <!-- 文件列表 -->
          <transition-group
            v-if="!isConstruction && !isImportInfo"
            class="upload-file-list el-upload-list el-upload-list--text"
            name="el-fade-in-linear"
            tag="ul"
            @click.stop
          >
            <li
              style="margin-top: 10px"
              v-for="(file, index) in fileList"
              :key="file.uid"
              class="el-upload-list__item ele-upload-list__item-content"
              v-if="autoUpload"
            >
              <el-link :href="`${file.url}`" :underline="false" target="_blank">
                <span class="el-icon-document"> {{ getFileName(file.name) }} </span>
              </el-link>
              <div class="ele-upload-list__item-content-action">
                <el-button type="danger" link @click="handleDelete(index)">删除</el-button>
              </div>
            </li>
          </transition-group>
        </div>
      </slot>

      <el-icon v-if="isConstruction">
        <Plus />
      </el-icon>
      <template #file="{ file }">
        <div class="pdf" v-if="isConstruction">
          <img src="@/assets/icons/svg/pdf.png" alt="" />
          <el-text class="w-148px text-center" truncated>
            <span>{{ file.name }}</span>
          </el-text>
          <div class="Shadow">
            <a :href="file.url" target="_blank">
              <el-icon class="mr">
                <View />
              </el-icon>
            </a>
            <a href="#">
              <el-icon @click="handleDelete((file as any).ossId, 'ossId')">
                <Delete />
              </el-icon>
            </a>
          </div>
        </div>
      </template>
    </el-upload>
  </div>
</template>

<script setup lang="ts">
import { propTypes } from '@/utils/propTypes';
import { delOss, listByIds } from '@/api/system/oss';
import { globalHeaders } from '@/utils/request';
import axios from 'axios';
const props = defineProps({
  modelValue: {
    type: [String, Object, Array],
    default: () => []
  },
  // 数量限制
  limit: propTypes.number.def(5),
  // 大小限制(MB)
  fileSize: propTypes.number.def(5),
  // 文件类型, 例如['png', 'jpg', 'jpeg']
  // fileType: propTypes.array.def(['doc', 'xls', 'ppt', 'txt', 'pdf', 'png', 'jpg', 'jpeg', 'zip']),
  fileType: propTypes.array.def(['pdf']),
  // 是否显示提示
  isShowTip: propTypes.bool.def(true),
  //是否为施工人员上传
  isConstruction: propTypes.bool.def(false),
  //是否为上传zip文件
  isImportInfo: propTypes.bool.def(false),
  //ip地址
  uploadUrl: propTypes.string.def('/resource/oss/upload'),
  //可拖拽上传
  isDarg: propTypes.bool.def(false),
  // 是否自动上传
  autoUpload: propTypes.bool.def(true),
  // 是否显示文件列表
  showFileList: propTypes.bool.def(false),
  // 默认显示的文件列表
  defaultFileList: {
    type: Array as any,
    default: () => []
  },
  // 其他参数
  data: propTypes.object.def({}),
  // 成功回调
  onUploadSuccess: {
    type: Function as PropType<(files: any[], res: any) => void>,
    default: undefined
  },
  // 上传方法
  method: propTypes.string.def('post'),

  // 失败回调
  onUploadError: {
    type: Function as PropType<(err: any, file: any, fileList: any) => void>,
    default: undefined
  },
  params: {
    type: Object,
    default: () => ({})
  }
});

const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const emit = defineEmits(['update:modelValue', 'handleChange', 'handleRemove']);

const number = ref(0);
const uploadList = ref<any[]>([]);
const baseUrl = import.meta.env.VITE_APP_BASE_API;
const uploadFileUrl = ref(baseUrl + props.uploadUrl); // 上传文件服务器地址
const headers = ref(globalHeaders());
const pendingFiles = ref<UploadFile[]>([]);

const realUploadUrl = computed(() => {
  const search = new URLSearchParams(props.params).toString();
  return search ? `${baseUrl}${props.uploadUrl}?${search}` : `${baseUrl}${props.uploadUrl}`;
});
const fileList = ref<any[]>([]);
const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize));

const fileUploadRef = ref<ElUploadInstance>();

const accept = computed(() => {
  return props.fileType.map((value) => `.${value}`).join(',');
});
watch(
  () => props.modelValue,
  async (val) => {
    if (props.isImportInfo) return;
    if (val) {
      let temp = 1;
      // 首先将值转为数组
      let list: any[] = [];
      if (Array.isArray(val)) {
        list = val;
      } else {
        const res = await listByIds(val as any);
        list = res.data.map((oss) => {
          console.log(oss);

          return {
            name: oss.originalName,
            url: oss.url,
            ossId: oss.ossId
          };
        });
      }
      // 然后将数组转为对象数组
      fileList.value = list.map((item) => {
        item = { name: item.name, url: item.url, ossId: item.ossId };
        item.uid = item.uid || new Date().getTime() + temp++;
        return item;
      });
    } else {
      fileList.value = [];
      return [];
    }
  },
  { deep: true, immediate: true }
);
watch(
  () => props.defaultFileList,
  () => {
    if (props.defaultFileList.length === 0) return;
    props.defaultFileList.forEach((item: any) => {
      fileList.value.push(item);
    });
  },
  { deep: true, immediate: true }
);
// 上传前校检格式和大小
const handleBeforeUpload = (file: any) => {
  if (!validateFile(file)) return false;
  proxy?.$modal.loading('正在上传文件，请稍候...');
  number.value++;
  return true;
};

//校检格式和大小
const validateFile = (file: File) => {
  const ext = file.name.split('.').pop()?.toLowerCase();
  if (props.fileType.length && !props.fileType.includes(ext!)) {
    proxy?.$modal.msgError(`文件格式不正确，请上传 ${props.fileType.join('/')} 格式文件!`);
    return false;
  }
  if (file.name.includes(',')) {
    proxy?.$modal.msgError('文件名不正确，不能包含英文逗号!');
    return false;
  }
  if (props.fileSize && file.size / 1024 / 1024 > props.fileSize) {
    proxy?.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
    return false;
  }
  return true;
};

// 文件个数超出
const handleExceed = () => {
  proxy?.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
};

// 上传失败
const handleUploadError = () => {
  proxy?.$modal.msgError('上传文件失败');
};

// 上传成功回调
interface UploadFileWithOssId extends UploadFile {
  ossId?: string;
}

const handleUploadSuccess = (res: any, file: UploadFileWithOssId) => {
  if (res.code === 200) {
    console.log('上传成功');
    // 上传成功，不管 data 是否为空
    uploadList.value.push({
      name: file.name,
      url: (res.data && res.data.url) || '',
      ossId: (res.data && res.data.ossId) || ''
    });
  } else {
    console.log('失败', res);

    number.value--;
    proxy?.$modal.closeLoading();
    proxy?.$modal.msgError(res.msg || '上传失败');
    fileUploadRef.value?.handleRemove(file);
    return;
  }

  uploadedSuccessfully(res);
};

const handleChange = (file: any, filelist: any) => {
  if (!props.autoUpload) {
    // 手动上传模式：在选中文件时拦截非法文件
    const isValid = validateFile(file.raw || file);
    if (!isValid) {
      fileUploadRef.value?.handleRemove(file); // 直接移除非法文件
      console.log(file, filelist, fileList.value);
      fileList.value = [...fileList.value]; // 触发列表更新
      return;
    }
  }
  // 记录 status = 'ready' 的文件
  if (file.status === 'ready') {
    pendingFiles.value.push(file);
    fileList.value = pendingFiles.value;
  }
  console.log(fileList.value);

  emit('handleChange', file, filelist);
};

// 删除文件
const handleRemove = (file: any, fileList: any) => {
  console.log(11);

  emit('handleRemove', file, fileList);
};

const handlePreview = (file: any) => {
  if (file.url) {
    window.open(file.url);
  }
};

// 删除文件
const handleDelete = async (index: string | number, type?: string) => {
  await proxy?.$modal.confirm('是否确认删除此文件？').finally();
  try {
    if (type === 'ossId') {
      delOss(index);
      fileList.value = fileList.value.filter((f) => f.ossId !== index);
    } else {
      let ossId = fileList.value[index].ossId;
      delOss(ossId);
      index = parseInt(index as string);
      fileList.value.splice(index, 1);
    }
  } finally {
    emit('handleRemove');
    emit('update:modelValue', listToString(fileList.value));
  }
};

// 上传结束处理
const uploadedSuccessfully = (res: any) => {
  if (props.isImportInfo) {
    emit('update:modelValue', 'ok');
    fileUploadRef.value?.clearFiles();
    proxy?.$modal.closeLoading();
    proxy?.$modal.msgSuccess('导入成功');
    props.onUploadSuccess?.(fileList.value, res);
    return;
  }

  if (number.value > 0 && uploadList.value.length === number.value) {
    fileList.value = fileList.value.filter((f) => f.url !== undefined).concat(uploadList.value);
    uploadList.value = [];
    number.value = 0;

    emit('update:modelValue', listToString(fileList.value));
    proxy?.$modal.closeLoading();
  }
  props.onUploadSuccess?.(fileList.value, res);
};

// 获取文件名称
const getFileName = (name: string) => {
  // 如果是url那么取最后的名字 如果不是直接返回
  if (!name) return '';
  if (name.lastIndexOf('/') > -1) {
    return name.slice(name.lastIndexOf('/') + 1);
  } else {
    return name;
  }
};

// 对象转成指定字符串分隔
const listToString = (list: any[], separator?: string) => {
  let strs = '';
  separator = separator || ',';
  list.forEach((item) => {
    if (item.ossId) {
      strs += item.ossId + separator;
    }
  });
  return strs != '' ? strs.substring(0, strs.length - 1) : '';
};

// 改造后的 customUpload
const customUpload = async (options: any) => {
  if (props.autoUpload) {
    // 自动上传，单文件请求
    try {
      const formData = new FormData();
      formData.append('file', options.file);
      Object.entries(props.data).forEach(([k, v]) => {
        if (v !== null && v !== undefined) formData.append(k, v as any);
      });
      const res = await axios?.({
        url: realUploadUrl.value,
        method: props.method,
        data: formData,
        headers: { 'Content-Type': 'multipart/form-data', ...headers.value }
      });
      handleUploadSuccess(res.data, options.file);
    } catch (err) {
      console.log(err, 'err');

      handleUploadError();
    }
  } else {
    // 手动上传，不发请求，只缓存
    pendingFiles.value.push(options.file);
  }
};

// 改造后的 submitUpload
const submitUpload = async () => {
  if (props.autoUpload) {
    fileUploadRef.value?.submit();
    return;
  }
  if (!pendingFiles.value.length) {
    return 'noFile';
  }
  const validFiles = pendingFiles.value.filter((f: any) => validateFile(f.raw || f));
  if (!validFiles.length) {
    proxy?.$modal.msgError('没有符合条件的文件可上传');
    return;
  }
  try {
    proxy?.$modal.loading('正在上传文件，请稍候...');
    const formData = new FormData();
    pendingFiles.value.forEach((f) => {
      if (f.raw) formData.append('file', f.raw as File);
    });
    Object.entries(props.data).forEach(([k, v]) => {
      if (v !== null && v !== undefined) formData.append(k, v as any);
    });
    const res = await axios?.({
      url: realUploadUrl.value,
      method: props.method,
      data: formData,
      headers: { 'Content-Type': 'multipart/form-data', ...headers.value }
    });
    handleUploadSuccess(res.data, {} as any);
    pendingFiles.value = [];
    fileUploadRef.value?.clearFiles();
  } catch (err) {
    handleUploadError();
  } finally {
    proxy?.$modal.closeLoading();
  }
};

defineExpose({ submitUpload });
</script>

<style scoped lang="scss">
.pdf {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  border-radius: 6px;
  position: relative;
  width: 100%;

  img {
    width: 40%;
  }

  &:hover {
    .Shadow {
      opacity: 1;
    }
  }

  > span {
    width: 100%;
  }
}

.upload-file-list {
  margin: 0;

  .el-upload-list__item {
    border: 1px solid #e4e7ed;
    line-height: 2;
    margin-bottom: 0;
    position: relative;
  }
}

.upload-file-list .ele-upload-list__item-content {
  display: flex;
  justify-content: space-between;
  align-items: center;
  color: inherit;
}

.Shadow {
  align-items: center;
  background-color: rgba(0, 0, 0, 0.5);
  color: #fff;
  cursor: default;
  display: inline-flex;
  font-size: 20px;
  height: 100%;
  justify-content: center;
  left: 0;
  opacity: 0;
  position: absolute;
  top: 0;
  transition: opacity 0.3s;
  width: 100%;
  z-index: 1;
}

.ele-upload-list__item-content-action .el-link {
  margin-right: 10px;
}

.el-icon.avatar-uploader-icon {
  border: 1px dashed #cdd0d6;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: 0.3s;
}

.el-icon.avatar-uploader-icon:hover {
  border-color: #409eff;
}

.el-icon.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 200px;
  height: 178px;
  text-align: center;
}
</style>
