/* * @Date: 2021-01-05 14:24:23 * @LastEditors: wangqiang@feewee.cn * @LastEditTime: 2023-02-11 16:54:22 */ import React, { useEffect, useRef, useState } from "react"; import { message, Popover, Spin, Upload } from "antd"; import { LinkOutlined, EyeOutlined, DeleteOutlined, FileTwoTone, LoadingOutlined, } from "@ant-design/icons"; import { UploadProps } from "antd/lib/upload"; import { RcFile, UploadChangeParam, UploadFile, } from "antd/lib/upload/interface"; import getFidFile, { FeeweeFileAccept } from "@/utils/getFidFile"; import COS from "@/utils/COS"; import { getFileListDetailApi, confirmThirdFileApi, deleteFileApi, } from "./api"; import "./index.less"; import { IMGURL } from "@/utils"; // @ts-ignore interface Props extends UploadProps { /** * @description: 附件fid列表。 * @description: 如果在表单中使用,则需要再Form.Item 中声明valuePropName="fidList" */ fidList?: string[]; onChange?: (fidList: string[]) => void; /** 使用腾讯云对象存储 */ useCos?: boolean; /** 上传腾讯云-前缀文件夹名称 */ catalog?: string; /** 不允许修改文件项列表 fid 数组[] */ freezeItems?: string[]; limitSize?: number; // 限制文件大小 limitUnit?: "kb" | "mb" | "KB" | "MB"; // 限制大小单位, 默认MB children?: React.ReactNode; } /** * @description: 自定义封装的 FeeweeUploadAttachment 上传附件组件 * @description: 使用方式: * 1. 仅需要 传入 fidList 和 onChange 参数就可以使用基本用法; * 2. 其他的参数同 Upload 组件的参数 * 3. useCos catalog freezeItems 同 APP 组件作用一样; * 4. limitSize 和 limitUnit 是为了限制上传附件的大小 * @param {Props} props */ export default function FeeweeUploadAttachment({ useCos, catalog = "", limitSize = undefined, limitUnit = "MB", children, freezeItems = [], fidList, onChange, ...props }: Props) { const [loading, setLoading] = useState(false); const [fileList, setFileList] = useState([]); const removingFileFidList = useRef([]); useEffect(() => { if (fidList?.length) { setLoading(true); // const hide = message.loading("", 0); getFileListDetailApi(fidList) .then((res) => { setFileList( (res.data || []).map((item) => ({ name: item.aliasName || item.fileName || "", uid: item.fid || "", url: item.fileUri, status: "done", size: item.length, type: item.contentType, response: { data: item.fileUri, fid: item.fid }, })) ); }) .catch((error: any) => { }) .finally(() => { // hide(); setLoading(false); }); } }, [fidList]); if (!props.listType) { props.listType = "text"; } if (useCos) { props.customRequest = (data) => COS.cosUpload({ ...data, uploadPath: "fw_fds_file/" + catalog }); } else { props.action = "api/file/upload"; } const onPreview = (file: UploadFile) => { if (props.onPreview) { props.onPreview(file); return; } getFidFile(file?.response.data); }; const beforeUpload = (file: RcFile, filelist: RcFile[]) => { if (limitSize !== undefined && typeof limitSize === "number") { const limit = isExceededSize(file, limitSize, limitUnit); if (limit.success) { return true; } else { message.error(limit.msg); // @ts-ignore file.status = "size_limit"; // @ts-ignore file.errMsg = limit.msg; return false; } } return props.beforeUpload ? props.beforeUpload(file, filelist) : true; }; const allFileDone = (fileList: UploadFile[]) => (fileList || []).every((file) => file?.status !== "uploading"); const onUploadChange = async ({ file, fileList, }: UploadChangeParam) => { setFileList( fileList.map((_file) => ({ ..._file, // @ts-ignore status: _file?.status === "size_limit" ? "error" : _file?.status, })) ); if (file?.status === "done") { if (useCos) { try { const ext_array = file?.name.split("."); const { data } = await confirmThirdFileApi({ fileName: file?.name, ext: "." + ext_array[ext_array.length - 1], contentType: file?.type, fileUri: file?.response.data, length: file?.size, }); file.response.fid = data; if (allFileDone(fileList)) { onChange && onChange( fileList .filter( (file) => file?.status === "done" && file?.response.fid ) .map((file) => file?.response.fid) ); } } catch (error: any) { message.error(error.message || "确认文件失败", 2); } } else { file.response.fid = file?.response.data; if (allFileDone(fileList)) { onChange && onChange( fileList .filter((file) => file?.status === "done" && file?.response.fid) .map((file) => file?.response.fid) ); } } } }; const onFileRemove = async (file: UploadFile) => { if (removingFileFidList.current.includes(file?.response?.fid)) return false; if (file.status === "error" || file?.status === "uploading") { setFileList(fileList.filter((_file) => _file.uid !== file.uid)); return true; } const hide = message.loading("删除中...", 0); try { removingFileFidList.current.push(file?.response.fid); setLoading(true); // await deleteFileApi(file?.response.fid); // if (useCos) { // await deleteCOSFile(file?.response.data); // } onChange && onChange((fidList || []).filter((fid) => fid !== file?.response.fid)); return Promise.resolve(); } catch (error: any) { message.error(error.message || "删除文件失败", 2); return Promise.reject(); } finally { removingFileFidList.current = removingFileFidList.current.filter( (fid) => fid !== file?.response.fid ); setLoading(false); hide(); } }; return ( { const DOM = props.listType === "picture-card" ? ListTypeOfPictureCard : props.listType === "picture" ? ListTypeOfPicture : ListTypeOfText; return ( ); // return ( //
// {/* @ts-ignore */} // {originalNode} //
// ); }} > {props.disabled ? ( fileList.length ? null : ( 暂无附件 ) ) : ( children )}
); } interface ItemRenderProps { file: UploadFile; freezeItems?: string[]; remove: () => void; disabled?: boolean; } const ListTypeOfText = ({ file, freezeItems = [], remove, disabled, }: ItemRenderProps) => (
{file?.status === "uploading" ? (
{file?.name}({file?.size ? getFileSize(file?.size) : ""})
) : (
{file?.name}({file?.size ? getFileSize(file?.size) : ""}) {/* @ts-ignore */} {file?.status === "error" && file?.errMsg ? ( // @ts-ignore {file?.errMsg} ) : null} {!disabled ? ( ) : null}
)}
{file?.status === "uploading" ? (
) : null}
); const ListTypeOfPictureCard = ({ file, freezeItems = [], remove, disabled, }: ItemRenderProps) => (

文件信息:{file?.name}({file?.size ? getFileSize(file?.size) : ""})

{/* @ts-ignore */} {file?.errMsg ? (

{/* @ts-ignore */} 错误原因:{file?.errMsg}

) : null}
} >
{file?.status === "uploading" ? (
文件上传中
{file?.name}
) : ( {file?.type?.includes("image") ? ( {`${file?.name}(${file?.size ) : ( )} {file?.name}({file?.size ? getFileSize(file?.size) : ""}) )}
{file?.status === "uploading" ? (
) : ( {!disabled ? ( ) : null} )}
); const ListTypeOfPicture = ({ file, freezeItems = [], remove, disabled, }: ItemRenderProps) => (
{file?.status === "uploading" ? (
{file?.name}({file?.size ? getFileSize(file?.size) : ""})
) : ( {file?.type?.includes("image") ? ( {`${file?.name}(${file?.size ) : ( )} {file?.name}({file?.size ? getFileSize(file?.size) : ""}) {/* @ts-ignore */} {file?.status === "error" && file?.errMsg ? ( // @ts-ignore {file?.errMsg} ) : null} {!disabled ? ( ) : null} )}
{file?.status === "uploading" ? (
) : null}
); const deleteCOSFile = (filePath: string) => { return new Promise((resolve, reject) => { COS.cosDelete({ filePath }, resolve, reject); }); }; /** * @description: 计算限制大小,统一返回限制大小转换为KB * @param {number} limitSize * @param {'kb' | 'mb' | 'KB' | 'MB'} limitUnit * @return {number} */ function getLimitSize( limitSize: number, limitUnit: "kb" | "mb" | "KB" | "MB" ): number { if (limitUnit === "kb" || limitUnit === "KB") { return limitSize; } if (limitUnit === "mb" || limitUnit === "MB") { return limitSize * 1024; } return 0; } /** * @description: 判断是否没超过限制大小 * @param {RcFile} file * @param {number} limitSize * @param {*} limitUnit * @return {{ success: boolean, msg?: string }} */ function isExceededSize( file: RcFile, limitSize: number, limitUnit: "kb" | "mb" | "KB" | "MB" ): { success: boolean; msg?: string } { const _limitSize = getLimitSize(limitSize, limitUnit); const fileSizeToKB = file?.size / 1024; if (fileSizeToKB <= _limitSize) { return { success: true }; } else { return { success: false, msg: `文件【${file?.name}】大小不能超过${limitSize}${limitUnit}`, }; } } export function getFileSize(length: number): string { let fileSize: string; fileSize = (length / 1024).toFixed(2); if (+fileSize > 1024) { fileSize = (+fileSize / 1024).toFixed(2) + "MB"; } else { fileSize += "KB"; } return fileSize; }