<template>
  <up-upload
    v-if="$slots.default"
    :fileList="state.fileShowList"
    :multiple="multiple"
    :uploadText="uploadText"
    :previewFullImage="previewFullImage"
    :maxSize="state.maxSize"
    :maxCount="maxCount"
    :deletable="deletable"
    :disabled="disabled"
    :accept="accept"
    :width="width"
    :height="height"
    @afterRead="afterRead"
    @delete="deletePic"
    @oversize="oversize"
  >
    <slot></slot
  ></up-upload>
  <up-upload
    v-else
    :fileList="state.fileShowList"
    :multiple="multiple"
    :uploadText="uploadText"
    :previewFullImage="previewFullImage"
    :maxSize="state.maxSize"
    :maxCount="maxCount"
    :deletable="deletable"
    :disabled="disabled"
    :accept="accept"
    :width="width"
    :height="height"
    @afterRead="afterRead"
    @delete="deletePic"
    @oversize="oversize"
  >
  </up-upload>
</template>
<script lang="ts" setup>
import { reactive, watch, watchEffect } from "vue";
interface Props {
  serverProxyApiUrl: string; // 上传文件后台服务器的请求接口地址
  fileServerUrl: string; //文件服务器访问地址
  fileServerType: string; // 存储类型(1、V3Web   2、V3Uni   3、服务器端  4、微信存储   5、阿里存储)
  path?: string; //文件上传相对地址,拼接在文件服务器后面
  http: any; // 请求对象
  uploadUrl?: string; //上传接口地址
  deleteUrl?: string; //删除接口地址

  modelValue: string | undefined; //回显传过来的源，可能为对象数组字符串、或空  "[{id,name,url,type},{}]"

  multiple?: boolean; //多文件上传(不隐藏上传按钮)
  maxKb?: number; //上传文件的大小限制,单位KB
  maxCount?: string | number; //最大选择图片的数量, 默认52
  deletable?: boolean; //是否显示删除图片的按钮, 默认true
  disabled?: boolean; //是否禁用(显示/隐藏)组件, 默认false
  uploadText?: string; //上传区域的提示文字
  previewFullImage?: boolean; //是否允许展示预览图, 默认true
  //uploadIcon?: string; //上传区域的图标，只能内置图标, 默认camera-fill
  //uploadIconColor?: string; //上传区域的图标的颜色, 默认#D3D4D6
  //useBeforeRead?: boolean; //是否启用(显示/隐藏)组件, 默认false
  accept?: "image" | "file" | "all" | "media" | "video" | undefined; //接受的文件类型 all | media | image | file | video, 默认all
  width?: string | number; //内部预览图片区域和选择图片按钮的区域宽度，单位rpx，不能是百分比，或者auto, 默认80
  height?: string | number; //内部预览图片区域和选择图片按钮的区域高度，单位rpx，不能是百分比，或者auto, 默认80
}

const props = withDefaults(defineProps<Props>(), {
  path: "upload/files",
  previewFullImage: true,
  multiple: true,
  deletable: true,
  accept: "image",
});

const emit = defineEmits(["update:modelValue"]);

const state = reactive({
  fileList: [] as any[], // 文件列表[{url:'xxx'}]
  fileShowList: [] as any[], // 显示的文件列表
  requestUrlFlag: "file", //上传服务器类型标志
  uploadUrl: "",
  deleteUrl: "",
  path: "",
  maxSize: Number.MAX_VALUE as number | string, //默认不限制大小
  fileServerUrl: "",
  serverProxyApiUrl: "",
});

// 定义一个接口，它描述了upload对象的结构
interface Upload {
  url: string;
  size: number;
  name: string;
}

// 新增图片
const afterRead = async (event: any) => {
  let eventFile: any = null;
  if (Array.isArray(event.file)) {
    eventFile = event.file[0];
  } else if (Object.prototype.toString.call(event.file) === "[object Object]") {
    eventFile = event.file;
  }
  const upload: Upload = {
    url: eventFile.url,
    size: eventFile.size,
    name: eventFile.name,
  };
  await doUpload(upload);
};

//上传操作
const doUpload = (upload: Upload) => {
  //{url: 'blob:http://localhost:5173/68a66020-e17a-416c-bc6e-7ccc2f5c21fc', size: 58708, name: '60005p.png'}
  let extName = "";
  if (upload.name) {
    extName = upload.name.substring(upload.name.lastIndexOf(".") + 1);
  } else if (upload.url.includes(".")) {
    extName = upload.url.substring(upload.url.lastIndexOf(".") + 1);
  }
  if (
    ![
      "jpg",
      "png",
      "jpeg",
      "JPG",
      "JPEG",
      "PNG",
      "docx",
      "doc",
      "xls",
      "xlsx",
      "pdf",
      "mpeg",
      "gif",
      "avi",
      "mp4",
      "3gp",
      "wmv",
      "txt",
    ].includes(extName)
  ) {
    uni.showToast({
      title: "文件格式不正确",
      duration: 2000,
    });
    return;
  }
  // formData.type：存放文件的服务器类型(1、V3Web   2、V3Uni   3、固定位置  4、微信存储   5、阿里存储)
  // formData.path：存到库中的路径,接在根路径(后台服务器从配置文件中取)后, path结尾没有/
  // url：后台接口地址
  //filePath: 文件资源路径
  uni.showLoading({
    title: "上传中",
    mask: true,
  });
  uni.uploadFile({
    url: state.serverProxyApiUrl + state.uploadUrl,
    filePath: upload.url,
    name: "file",
    formData: {
      type: props.fileServerType,
      path: state.path,
    },
    success: (res:any) => {
      /*
        {
            "statusCode": 200,
            "data": "{\"code\":\"200\",\"message\":null,\"fullMessage\":null,\"data\":{\"type\":\"1\",\"id\":\"upload/files/60005p（9499）.png\",\"name\":\"60005p.png\",\"url\":\"upload/files/60005p（9499）.png\"},\"position\":null}",
            "errMsg": "uploadFile:ok"
        }
      */
      if (res.errMsg == "uploadFile:ok") {
        uni.showToast({
          title: "上传成功",
        });
      }
      let _data: string = res.data;
      if (_data) {
        try {
          let __data = JSON.parse(_data);
          if (__data.code == "200") {
            state.fileList.push(__data.data);
            // 将新的文件列表抛出去
            emit("update:modelValue", JSON.stringify(state.fileList));
          }
        } catch (e) {
          uni.showToast({
            title: "上传失败",
          });
        }
      }
    },
    fail: (err) => {
      uni.showToast({
        title: "上传失败",
      });
    },
    complete: () => {
      uni.hideLoading();
    },
  });
};

// 删除图片
const deletePic = async (event: any) => {
  let index = event.index;
  await props.http?.post(state.deleteUrl, {
    id: state.fileList[index].id,
    type: state.fileList[index].type,
  });
  state.fileList.splice(index, 1);
  if (state.fileList.length == 0) {
    emit("update:modelValue", "");
  } else {
    emit("update:modelValue", JSON.stringify(state.fileList));
  }
};

//获取文件的访问地址
const getFileUrl = (fileObj: any) => {
  if (["4", "5"].includes(fileObj.type)) {
    return fileObj.url;
  }
  // fileServerUrl 结尾保留一个/
  return processSlashes(props.fileServerUrl, 3) + fileObj.url;
};
//获取文件请求标志
function getRequestFlag(type: any = "1") {
  if (["1", "2", "3"].includes(type)) {
    return "file";
  } else if (["4", "5"].includes(type)) {
    return "oss";
  }
  return "file";
}
//超出大小提示
const oversize = () => {
  uni.showToast({
    title:
      "大小不能超过" +
      Math.floor(Number(state.maxSize) / 1024).toFixed(2) +
      "KB!",
    duration: 2000,
  });
};

function list2ShowList() {
  if (state.fileList.length == 0) {
    state.fileShowList = [];
    return;
  }
  state.fileShowList = state.fileList.map((item: any) => {
    return { url: getFileUrl(item) };
  });
}

/**
 * 处理字符串开头与结尾的斜杠/
 * @params str: 字符串
 * @params flag:
 * 1 开头保留一个/，不处理结尾 ;
 * 2 开头不保留，不处理结尾 ;
 * 3 结尾保留一个/，不处理开头 ;
 * 4 结尾不保留，不处理开头 ;
 * 5 开头结尾都保留一个/ ;
 * 6 开头结尾都不保留 ;
 * 7 开头保留，结尾不保留 ;
 * 8 开头不保留，结尾保留 ;
 *
 */
function processSlashes(str: string, flag: number): string {
  if (!str) return str;
  // 处理开头的斜杠
  if (flag === 1 || flag === 5 || flag === 7) {
    // 如果开头没有斜杠，则添加一个
    if (!str.startsWith("/")) {
      str = "/" + str;
    } else {
      // 如果开头有多个斜杠，则只保留一个
      str = str.replace(/^\/+/, "/");
    }
  } else if (flag === 2 || flag === 6 || flag === 8) {
    str = str.replace(/^\/+/, ""); // 不保留开头的斜杠
  }

  // 处理结尾的斜杠
  if (flag === 3 || flag === 5 || flag === 8) {
    // 如果结尾没有斜杠，则添加一个
    if (!str.endsWith("/")) {
      str = str + "/";
    } else {
      // 如果结尾有多个斜杠，则只保留一个
      str = str.replace(/\/+$/, "/");
    }
  } else if (flag === 4 || flag === 6 || flag === 7) {
    str = str.replace(/\/+$/, ""); // 不保留结尾的斜杠
  }
  return str;
}

function initData() {
  //文件服务器地址(末尾加一个/)
  state.serverProxyApiUrl = processSlashes(props.serverProxyApiUrl, 3);
  //文件路径(去掉首尾/)
  state.path = processSlashes(props.path, 6);
  // 最大文件大小单位KB
  if (props.maxKb && typeof props.maxKb === "number") {
    state.maxSize = props.maxKb * 1024;
  }

  if (props.modelValue && typeof props.modelValue === "string") {
    try {
      state.fileList = JSON.parse(props.modelValue);
      if (
        Object.prototype.toString.call(state.fileList) === "[object,Object]"
      ) {
        state.fileList = [state.fileList];
      }
      list2ShowList();
    } catch (e) {
      throw new Error("数据格式错误");
    }
  } else {
    list2ShowList();
  }
  if (props.fileServerType) {
    state.requestUrlFlag = getRequestFlag(props.fileServerType);
  }
  if (props.uploadUrl) {
    state.uploadUrl = props.uploadUrl;
  } else {
    state.uploadUrl = "rest/wbl/" + state.requestUrlFlag + "/upload";
  }
  if (props.deleteUrl) {
    state.deleteUrl = props.deleteUrl;
  } else {
    state.deleteUrl = "rest/wbl/" + state.requestUrlFlag + "/delete";
  }
}

//监听文件列表变化
watchEffect(() => {
  initData();
});
</script>

<style lang="scss" scoped></style>
