diff --git a/components/FormBuilder/FormBuilder.d.ts b/components/FormBuilder/FormBuilder.d.ts index e17bf60..ba5714e 100644 --- a/components/FormBuilder/FormBuilder.d.ts +++ b/components/FormBuilder/FormBuilder.d.ts @@ -1,4 +1,4 @@ -import type { FormInstance, FormProps } from "antd/es/form"; +import type { FormProps } from "antd/es/form"; import type { Gutter } from "antd/es/grid/row"; import type { FC, ReactNode } from "react"; import type { FormOption, FormValues } from "./FormItemsRenderer"; @@ -6,7 +6,7 @@ import type { FormOption, FormValues } from "./FormItemsRenderer"; /** * FormBuilder 组件属性 */ -export interface FormBuilderProps extends Omit { +export interface FormBuilderProps extends FormProps { /** 表单初始值 */ values?: FormValues; /** 表单配置项数组 */ @@ -15,24 +15,22 @@ export interface FormBuilderProps extends Omit { gutter?: Gutter | [Gutter, Gutter]; /** 占据栅格列数,默认 12 */ span?: number | string; - /** 表单实例(通过 Form.useForm() 创建) */ - form?: FormInstance; /** 自动生成必填规则,默认 true */ useAutoGenerateRequired?: boolean; - /** 表单提交回调 */ - onFinish?: (values: FormValues) => void; /** 是否显示操作按钮区域,默认 true */ showActionButtons?: boolean; /** 提交按钮文字,默认为"提交" */ submitButtonText?: string; - /** 重置按钮文字,默认为"重置" */ - resetButtonText?: string; + /** 取消按钮文字,默认为"取消" */ + cancelButtonText?: string; /** 是否显示提交按钮,默认 true */ showSubmitButton?: boolean; - /** 是否显示重置按钮,默认 true */ - showResetButton?: boolean; + /** 是否显示取消按钮,默认 true */ + showCancelButton?: boolean; /** 自定义操作按钮组 */ customActionButtons?: ReactNode; + /** 额外操作按钮组 */ + extraActionButtons?: ReactNode; } /** diff --git a/components/FormBuilder/FormBuilder.js b/components/FormBuilder/FormBuilder.js index 5ce9005..50f5730 100644 --- a/components/FormBuilder/FormBuilder.js +++ b/components/FormBuilder/FormBuilder.js @@ -11,23 +11,19 @@ const FormBuilder = (props) => { gutter = 24, span = 12, labelCol = { span: 4 }, - onFinish, useAutoGenerateRequired = true, showActionButtons = true, submitButtonText = "提交", - resetButtonText = "重置", + cancelButtonText = "取消", showSubmitButton = true, - showResetButton = true, + showCancelButton = true, customActionButtons, - form: externalForm, + extraActionButtons, ...restProps } = props; - const [internalForm] = Form.useForm(); - const form = externalForm || internalForm; - - const handleReset = () => { - form.resetFields(); + const handleCancel = () => { + window.history.back(); }; return ( @@ -35,9 +31,7 @@ const FormBuilder = (props) => { labelCol={labelCol} scrollToFirstError wrapperCol={{ span: 24 - labelCol.span }} - onFinish={onFinish} initialValues={values} - form={form} style={{ width: `calc(100% - ${gutter * 2}px)`, margin: `0 auto` }} {...restProps} > @@ -60,11 +54,12 @@ const FormBuilder = (props) => { {submitButtonText} )} - {showResetButton && ( - )} + {extraActionButtons} )} diff --git a/components/FormBuilder/FormItemsRenderer.js b/components/FormBuilder/FormItemsRenderer.js index 1174349..f5b97ce 100644 --- a/components/FormBuilder/FormItemsRenderer.js +++ b/components/FormBuilder/FormItemsRenderer.js @@ -21,13 +21,13 @@ const { RangePicker } = DatePicker; * 表单项渲染器组件 */ const FormItemsRenderer = ({ - options, - labelCol, - span = 12, - collapse = false, - useAutoGenerateRequired = true, - initialValues, - }) => { + options, + labelCol, + span = 12, + collapse = false, + useAutoGenerateRequired = true, + initialValues, +}) => { const form = Form.useFormInstance(); // 获取表单值,优先使用 initialValues @@ -298,38 +298,38 @@ const FormItemsRenderer = ({ option.customizeRender ? (renderFormControl(option)) : ( - - {() => { + + {() => { // 支持动态计算 hidden - const hidden = typeof option.hidden === "function" - ? option.hidden(getFormValues()) - : (option.hidden ?? false); + const hidden = typeof option.hidden === "function" + ? option.hidden(getFormValues()) + : (option.hidden ?? false); - if (hidden) - return null; + if (hidden) + return null; - return ( - - - {renderFormControl(option)} - - - ); - }} - - ) + return ( + + + {renderFormControl(option)} + + + ); + }} + + ) ); } @@ -348,17 +348,17 @@ const FormItemsRenderer = ({ option.customizeRender ? (renderFormControl(option)) : ( - - {renderFormControl(option)} - - ) + + {renderFormControl(option)} + + ) } ); diff --git a/components/ImportFile/index.js b/components/ImportFile/index.js index adcf67a..ea1199f 100644 --- a/components/ImportFile/index.js +++ b/components/ImportFile/index.js @@ -75,7 +75,7 @@ const ImportFile = (props) => { > {children && typeof children === "function" ? children({ form }) : children} - + diff --git a/components/Upload/index.d.ts b/components/Upload/index.d.ts index 82dbabc..2f561d1 100644 --- a/components/Upload/index.d.ts +++ b/components/Upload/index.d.ts @@ -11,12 +11,14 @@ export interface UploadProps extends Omit { ratio?: `${number}*${number}`; /** 是否显示提示,默认 true */ showTip?: boolean; - /** 文件大小限制(单位:MB),默认 0(不限制) */ + /** 文件大小限制(单位:MB) */ size?: number; /** 自定义提示内容 */ tipContent?: ReactNode; - /** listType 为 text 时上传按钮文本,默认 "点击选择文件上传" */ + /** listType 为 text 时上传按钮文本 */ uploadButtonText?: string; + /** 要上传的文件类型,默认为 image */ + fileType?: "image" | "video" | "document"; } /** @@ -26,3 +28,7 @@ export interface UploadProps extends Omit { declare const Upload: FC; export default Upload; + +// 视频:数量默认1个,且只支持mp4格式,单个文件大小默认100M +// 文件:数量默认4个,且只支持pdf、doc、docx格式 +// 图片:数量默认4个,且只支持jpg、jpeg、png格式 diff --git a/components/Upload/index.js b/components/Upload/index.js index a0c72a1..e3b21b4 100644 --- a/components/Upload/index.js +++ b/components/Upload/index.js @@ -1,4 +1,4 @@ -import { PlusOutlined, UploadOutlined } from "@ant-design/icons"; +import { PlusOutlined, UploadOutlined, VideoCameraAddOutlined } from "@ant-design/icons"; import { Upload as AntUpload, Button, message, Modal } from "antd"; import { useState } from "react"; @@ -10,15 +10,16 @@ const Upload = (props) => { value = [], onChange, onPreview, - maxCount = 1, - listType = "picture-card", - accept = ["picture-card", "picture-circle", "picture"].includes(listType) ? ".jpg,.jpeg,.png" : "", + maxCount: externalMaxCount, + listType: externalListType, + accept: externalAccept, ratio = "", showTip = true, multiple = true, - size = 0, + size: externalSize, tipContent, - uploadButtonText = "点击选择文件上传", + uploadButtonText: externalUploadButtonText, + fileType: externalFileType, formValues, ...restProps } = props; @@ -26,6 +27,90 @@ const Upload = (props) => { const [previewVisible, setPreviewVisible] = useState(false); const [previewImage, setPreviewImage] = useState(""); + // 预设的文件格式 + const imageAccept = ".jpg,.jpeg,.png"; + const documentAccept = ".pdf,.doc,.docx"; + const videoAccept = ".mp4"; + + // 根据accept自动判断文件类型 + const getAutoFileType = () => { + if (externalAccept) { + if (externalAccept === "*") + return "document"; + const acceptList = externalAccept.split(","); + if (acceptList.some(format => videoAccept.split(",").includes(format))) + return "video"; + if (acceptList.some(format => documentAccept.split(",").includes(format))) + return "document"; + if (acceptList.some(format => imageAccept.split(",").includes(format))) + return "image"; + return "document"; + } + return "image"; + }; + const fileType = externalFileType || getAutoFileType(); + + // 文件类型判断 + const isImageType = fileType === "image"; + const isVideoType = fileType === "video"; + const isDocumentType = fileType === "document"; + + // 获取listType + const getListType = () => { + if (externalListType) + return externalListType; + if (externalAccept === "*") + return "text"; + if (fileType === "image") + return "picture-card"; + return "text"; + }; + const listType = getListType(); + + // 获取文件格式 + const getAccept = () => { + if (externalAccept) + return externalAccept === "*" ? "" : externalAccept; + if (isImageType) + return imageAccept; + if (isVideoType) + return videoAccept; + if (isDocumentType) + return documentAccept; + return imageAccept; + }; + const accept = getAccept(); + + // 获取默认上传数量 + const getMaxCount = () => { + if (externalMaxCount) + return externalMaxCount; + if (isVideoType) + return 1; + if (isImageType) + return 4; + if (isDocumentType) + return 4; + return 1; + }; + const maxCount = getMaxCount(); + + // 获取默认文件大小 + const getSize = () => { + if (externalSize) + return externalSize; + if (isVideoType) + return 100; + return 0; + }; + const size = getSize(); + + // 上传按钮文字 + const uploadButtonText = externalUploadButtonText || (isVideoType ? "上传视频" : "上传附件"); + + // 文件格式提示 + const acceptTip = accept.replace(/\./g, "").split(",").join("、"); + // 生成提示信息 const getTipText = () => { if (tipContent) @@ -34,10 +119,7 @@ const Upload = (props) => { const tips = [ `最多上传${maxCount}个文件`, accept - ? `并且只能上传${accept - .replace(/\./g, "") - .split(",") - .join("、")}格式的文件` + ? `并且只能上传${acceptTip}格式的文件` : "可以上传任意格式的文件", size ? `文件大小不能超过${size}M` : "", ratio ? `只能上传${ratio}分辨率的图片` : "", @@ -62,7 +144,7 @@ const Upload = (props) => { // 验证文件格式 if (acceptList.length > 0 && !acceptList.includes(suffix)) { - message.warning(`只能上传${accept}格式的文件`); + message.warning(`只能上传${acceptTip}格式的文件`); return; } @@ -107,7 +189,7 @@ const Upload = (props) => { // 预览文件 const handlePreview = (file) => { - if (["picture-card", "picture-circle", "picture"].includes(listType)) { + if (isImageType) { setPreviewImage(file.url || file.thumbUrl); setPreviewVisible(true); } @@ -121,14 +203,19 @@ const Upload = (props) => { // 上传按钮 const uploadButton - = ["picture-card", "picture-circle", "picture"].includes(listType) + = isImageType ? (
) : ( - + ); return ( @@ -148,9 +235,9 @@ const Upload = (props) => { { showTip - ? (tipContent || getTipText()) && ( + ? (getTipText()) && (
- {tipContent || getTipText()} + {getTipText()}
) : null diff --git a/utils/index.d.ts b/utils/index.d.ts index 8d8a45f..7de4653 100644 --- a/utils/index.d.ts +++ b/utils/index.d.ts @@ -31,6 +31,24 @@ type DataType | "HTMLDocument" | string; // 允许其他可能的类型 +// 定义 getFileSuffix 函数可能返回的常见类型 +type FileSuffix + = | "jpg" + | "jpeg" + | "png" + | "mp4" + | "mp3" + | "pdf" + | "doc" + | "docx" + | "xls" + | "xlsx" + | "txt" + | "zip" + | "rar" + | "tar" + | string; // 允许其他可能的类型 + // 为 findCharIndex 函数定义接口类型 interface FindCharIndexOptions { /** 查找的字符串 */ @@ -186,7 +204,7 @@ export function paging(options: PagingOptions): T[]; /** * 获取文件后缀 */ -export function getFileSuffix(name: string): string; +export function getFileSuffix(name: string): FileSuffix; /** * 获取文件名称