commit d7ccfda2751d2d2f71a4439f1617c25887b48bf5 Author: LiuJiaNan <15703339975@163.com> Date: Wed Oct 22 14:43:42 2025 +0800 init diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..271822f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5cd00a3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/src/test/ +/target/ +.idea + +/node_modules +*.local +package-lock.json diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..66949b7 --- /dev/null +++ b/.npmignore @@ -0,0 +1,42 @@ +# 开发相关文件 +.eslintrc.cjs +.eslintignore +.prettierrc.cjs +.gitignore +vite.config.js +vitest.config.js +tsconfig.json +jsconfig.json + +# 构建和缓存目录 +node_modules/ +dist/ +.vite/ +.cache/ + +# 开发工具配置 +.vscode/ +.idea/ +.editorconfig + +# 日志文件 +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# 测试相关 +coverage/ +.nyc_output/ +test/ +tests/ +__tests__/ +*.test.js +*.test.ts +*.spec.js +*.spec.ts + +# 其他 +.DS_Store +.env* +!.env.example diff --git a/README.md b/README.md new file mode 100644 index 0000000..46f8279 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# zy-react-library + +## 📦 安装 + +```bash +# yarn +yarn add zy-react-library +``` + +## 📄 更新日志 + +### v1.0.0 (2025-10-22) + +- 🎉 初始版本发布 \ No newline at end of file diff --git a/components/FormBuilder/FormBuilder.d.ts b/components/FormBuilder/FormBuilder.d.ts new file mode 100644 index 0000000..9b42144 --- /dev/null +++ b/components/FormBuilder/FormBuilder.d.ts @@ -0,0 +1,31 @@ +import type { FormInstance, FormProps } from "antd/es/form"; +import type { Gutter } from "antd/es/grid/row"; +import type { FC } from "react"; +import type { FormOption, FormValues } from "./FormItemsRenderer"; + +/** + * FormBuilder 组件属性 + */ +export interface FormBuilderProps extends Omit { + /** 表单初始值 */ + values?: FormValues; + /** 表单配置项数组 */ + options: FormOption[]; + /** 栅格间距,默认 24 */ + gutter?: Gutter | [Gutter, Gutter]; + /** 占据栅格列数,默认 12 */ + span?: number | string; + /** 表单实例(通过 Form.useForm() 创建) */ + form: FormInstance; + /** 自动生成必填规则,默认 true */ + useAutoGenerateRequired?: boolean; + /** 表单提交回调 */ + onFinish?: (values: any) => void; +} + +/** + * 表单构建器组件 + */ +declare const FormBuilder: FC; + +export default FormBuilder; diff --git a/components/FormBuilder/FormBuilder.js b/components/FormBuilder/FormBuilder.js new file mode 100644 index 0000000..5f46621 --- /dev/null +++ b/components/FormBuilder/FormBuilder.js @@ -0,0 +1,41 @@ +import { Form, Row } from "antd"; +import FormItemsRenderer from "./FormItemsRenderer"; + +/** + * 表单构建器组件 + */ +const FormBuilder = (props) => { + const { + values, + options, + gutter = 24, + span = 12, + labelCol = { span: 4 }, + onFinish, + useAutoGenerateRequired = true, + ...restProps + } = props; + + return ( +
+ + + +
+ ); +}; + +FormBuilder.displayName = "FormBuilder"; + +export default FormBuilder; diff --git a/components/FormBuilder/FormItemsRenderer.d.ts b/components/FormBuilder/FormItemsRenderer.d.ts new file mode 100644 index 0000000..3606097 --- /dev/null +++ b/components/FormBuilder/FormItemsRenderer.d.ts @@ -0,0 +1,107 @@ +import type { ColProps } from "antd/es/col"; +import type { FormItemProps, Rule } from "antd/es/form"; +import type { NamePath } from "rc-field-form/lib/interface"; +import type { FC, ReactNode } from "react"; +import type { FORM_ITEM_RENDER_ENUM } from "../../enum/formItemRender"; + +/** + * 表单项渲染类型 + */ +export type FormItemRenderType + = | (typeof FORM_ITEM_RENDER_ENUM)[keyof typeof FORM_ITEM_RENDER_ENUM] + | ((props: any) => ReactNode); + +/** + * 选项项数据类型 + */ +export interface OptionItem { + /** 值字段 */ + value?: any; + /** 标签字段 */ + label?: string; + /** 字典ID */ + dictionariesId?: any; + /** ID字段 */ + id?: any; + /** 名称字段 */ + name?: string; + [key: string]: any; +} + +/** + * 字段键配置 + */ +export interface itemsFieldConfig { + /** 值字段的键名,默认为 'value' */ + valueKey?: string; + /** 标签字段的键名,默认为 'label' */ + labelKey?: string; +} + +/** + * 表单值类型 + */ +export type FormValues = Record; + +/** + * 表单配置项 + */ +export interface FormOption { + /** 表单项字段名 */ + name?: string | string[]; + /** 表单项标签 */ + label?: ReactNode; + /** 渲染类型 */ + render?: FormItemRenderType; + /** 占据栅格列数,默认 12 */ + span?: number | string; + /** 是否必填,默认 true,支持函数动态计算 */ + required?: boolean | ((formValues: FormValues) => boolean); + /** 验证规则 */ + rules?: Rule | Rule[]; + /** 占位符文本,默认会根据传入的 render 类型自动判断(请选择、请输入)和 label 组合 */ + placeholder?: ReactNode; + /** 提示信息,传入将在 label 右侧生成图标展示 tooltip */ + tip?: ReactNode; + /** 是否隐藏,默认 false,支持函数动态计算 */ + hidden?: boolean | ((formValues: FormValues) => boolean); + /** 是否自定义渲染,默认 false,将不生成 Col 和 Form.Item( 仅生效 render、items、itemsField、componentProps ) */ + customizeRender?: boolean; + /** 选项数据(用于 select、radio、checkbox) */ + items?: OptionItem[]; + /** 字段键配置 */ + itemsField?: itemsFieldConfig; + /** 传递给表单控件的属性,支持函数动态计算 */ + componentProps?: Record | ((formValues: FormValues) => Record); + /** 传递给 Form.Item 的属性,支持函数动态计算 */ + formItemProps?: FormItemProps | ((formValues: FormValues) => FormItemProps); + /** label 栅格配置 */ + labelCol?: ColProps; + /** wrapper 栅格配置 */ + wrapperCol?: ColProps; + /** 是否应该更新(用于表单联动) */ + shouldUpdate?: boolean | ((prevValues: FormValues, nextValues: FormValues, info: { source?: string }) => boolean); + /** 依赖字段(用于表单联动) */ + dependencies?: NamePath[]; +} + +/** + * FormItemsRenderer 组件属性 + */ +export interface FormItemsRendererProps { + /** 表单配置项数组 */ + options: FormOption[]; + /** 默认栅格占据列数,默认 12 */ + span?: number; + /** 是否折叠(仅显示前3项),默认 false */ + collapse?: boolean; + /** 自动生成必填规则,默认 true */ + useAutoGenerateRequired?: boolean; +} + +/** + * 表单项渲染器组件 + */ +declare const FormItemsRenderer: FC; + +export default FormItemsRenderer; diff --git a/components/FormBuilder/FormItemsRenderer.js b/components/FormBuilder/FormItemsRenderer.js new file mode 100644 index 0000000..a2db67c --- /dev/null +++ b/components/FormBuilder/FormItemsRenderer.js @@ -0,0 +1,345 @@ +import { InfoCircleOutlined } from "@ant-design/icons"; +import { + Checkbox, + Col, + DatePicker, + Divider, + Form, + Input, + InputNumber, + Radio, + Select, + Tooltip, +} from "antd"; +import dayjs from "dayjs"; +import { FORM_ITEM_RENDER_ENUM } from "../../enum/formItemRender"; + +const { TextArea } = Input; +const { RangePicker } = DatePicker; + +/** + * 表单项渲染器组件 + */ +const FormItemsRenderer = ({ + options, + span = 12, + collapse = false, + useAutoGenerateRequired = true, +}) => { + const form = Form.useFormInstance(); + + // 获取传给组件的属性 + const getComponentProps = (option) => { + return typeof option.componentProps === "function" + ? option.componentProps(form.getFieldsValue()) + : (option.componentProps || {}); + }; + + // 获取传给formItem的属性 + const getFormItemProps = (option) => { + const formItemProps = typeof option.formItemProps === "function" + ? option.formItemProps(form.getFieldsValue()) + : (option.formItemProps || {}); + + // 为日期组件添加特殊处理 + if ([ + FORM_ITEM_RENDER_ENUM.DATE, + FORM_ITEM_RENDER_ENUM.DATE_MONTH, + FORM_ITEM_RENDER_ENUM.DATE_YEAR, + FORM_ITEM_RENDER_ENUM.DATETIME, + ].includes(option.render)) { + formItemProps.getValueFromEvent = (_, dateString) => dateString; + formItemProps.getValueProps = value => ({ value: value ? dayjs(value) : undefined }); + } + + // 为日期周组件添加特殊处理 + if ([ + FORM_ITEM_RENDER_ENUM.DATE_WEEK, + ].includes(option.render)) { + formItemProps.getValueFromEvent = (_, dateString) => dateString; + formItemProps.getValueProps = value => ({ value: value ? dayjs(value, "YYYY-wo") : undefined }); + } + + // 为日期范围组件添加特殊处理 + if ([ + FORM_ITEM_RENDER_ENUM.DATE_RANGE, + FORM_ITEM_RENDER_ENUM.DATETIME_RANGE, + ].includes(option.render)) { + formItemProps.getValueFromEvent = (_, dateString) => dateString; + formItemProps.getValueProps = value => ({ value: Array.isArray(value) ? value.map(v => v ? dayjs(v) : undefined) : undefined }); + } + + return formItemProps; + }; + + // 获取items里的value和label字段key + const getItemsFieldKey = (option) => { + return { + valueKey: option?.itemsField?.valueKey || "value", + labelKey: option?.itemsField?.labelKey || "label", + }; + }; + + // 获取验证规则 + const getRules = (option) => { + if (!useAutoGenerateRequired) + return option.rules ? (Array.isArray(option.rules) ? option.rules : [option.rules]) : []; + if (option.render === FORM_ITEM_RENDER_ENUM.DIVIDER) + return []; + + // 支持动态计算 required + const required = typeof option.required === "function" + ? option.required(form.getFieldsValue()) + : (option.required || true); + + if (required) { + const isBlurTrigger = !option.render || [ + FORM_ITEM_RENDER_ENUM.INPUT, + FORM_ITEM_RENDER_ENUM.TEXTAREA, + FORM_ITEM_RENDER_ENUM.INPUT_NUMBER, + FORM_ITEM_RENDER_ENUM.NUMBER, + ].includes(option.render); + + const rules = [ + { + required: true, + message: `${isBlurTrigger ? "请输入" : "请选择"}${option.label}`, + }, + ]; + + if (option.rules) { + if (Array.isArray(option.rules)) { + rules.push(...option.rules); + } + else { + rules.push(option.rules); + } + } + return rules; + } + + return option.rules ? (Array.isArray(option.rules) ? option.rules : [option.rules]) : []; + }; + + // 渲染表单控件 + const renderFormControl = (option) => { + const componentProps = getComponentProps(option); + const itemsFieldKey = getItemsFieldKey(option); + /** @type {string | Function} */ + const render = option.render || FORM_ITEM_RENDER_ENUM.INPUT; + const placeholder = option.placeholder || `请${render === FORM_ITEM_RENDER_ENUM.SELECT || render === FORM_ITEM_RENDER_ENUM.RADIO || render === FORM_ITEM_RENDER_ENUM.CHECKBOX ? "选择" : "输入"}${option.label}`; + + switch (render) { + case FORM_ITEM_RENDER_ENUM.INPUT: + return ; + + case FORM_ITEM_RENDER_ENUM.TEXTAREA: + return