diff --git a/src/components/AnForm/components/Form.tsx b/src/components/AnForm/components/Form.tsx index cb76fad..8e4c819 100644 --- a/src/components/AnForm/components/Form.tsx +++ b/src/components/AnForm/components/Form.tsx @@ -1,4 +1,5 @@ -import { Form, FormInstance, FormItem } from "@arco-design/web-vue"; +import { Form, FormInstance } from "@arco-design/web-vue"; +import { useVModel } from "@vueuse/core"; import { PropType } from "vue"; import { FormContextKey } from "../core/useFormContext"; import { useFormItems } from "../core/useFormItems"; @@ -6,7 +7,6 @@ import { useFormModel } from "../core/useFormModel"; import { useFormRef } from "../core/useFormRef"; import { useFormSubmit } from "../core/useFormSubmit"; import { AnFormItem, IAnFormItem } from "./FormItem"; -import { useVModel } from "@vueuse/core"; /** * 表单组件 @@ -16,6 +16,12 @@ export const AnForm = defineComponent({ props: { /** * 表单数据 + * @example + * ```ts + * { + * id: undefined + * } + * ``` */ model: { type: Object as PropType, @@ -30,12 +36,24 @@ export const AnForm = defineComponent({ }, /** * 提交表单 + * @example + * ```ts + * (model) => { + * return api.user.addUser(model) + * } + * ``` */ submit: { type: [String, Function, Object] as PropType, }, /** * 传给Form组件的参数 + * @exmaple + * ```ts + * { + * layout: 'vertical' + * } + * ``` */ formProps: { type: Object as IAnFormProps, diff --git a/src/components/AnForm/components/FormItem.tsx b/src/components/AnForm/components/FormItem.tsx index a4c3cbc..c3c8bee 100644 --- a/src/components/AnForm/components/FormItem.tsx +++ b/src/components/AnForm/components/FormItem.tsx @@ -59,14 +59,12 @@ export const AnFormItem = defineComponent({ if (Slot) { return ; } - - const Setter = setterMap[props.item.setter as SetterType]?.render as any; + const Setter = setterMap[props.item.setter as SetterType]?.setter as any; if (!Setter) { return null; } - return ( - + {setterSlots.value} ); @@ -152,13 +150,19 @@ export type IAnFormItemBase = { /** * 字段名 * @description 字段名唯一,支持特殊语法 - * @required + * @example + * ```ts + * 'username' + * ``` */ field: string; /** * 标签 - * @example '昵称' + * @example + * ```ts + * '昵称' + * ``` */ label?: string; @@ -170,19 +174,29 @@ export type IAnFormItemBase = { /** * 是否可见 - * @example (model) => Boolean(model.id) + * @example + * ```ts + * (props) => Boolean(props.model.id) + * ``` */ visible?: IAnFormItemBoolFn; /** * 是否禁用 - * @example (model) => Boolean(model.id) + * @example + * ```ts + * (props) => Boolean(props.model.id) + * ``` */ disable?: IAnFormItemBoolFn; /** * 选项 * @description 适用于下拉框等组件 + * @example + * ```ts + * [{ label: '方式1', value: 1 }] + * ``` */ options?: IAnFormItemOption[] | ((args: IAnFormItemFnProps) => IAnFormItemOption[] | Promise); diff --git a/src/components/AnForm/components/FormModal.tsx b/src/components/AnForm/components/FormModal.tsx index 9790fa0..e49bf12 100644 --- a/src/components/AnForm/components/FormModal.tsx +++ b/src/components/AnForm/components/FormModal.tsx @@ -1,10 +1,10 @@ +import { useVisible } from "@/hooks/useVisible"; import { Button, ButtonInstance, Modal } from "@arco-design/web-vue"; import { PropType } from "vue"; -import { IAnFormItem } from "./FormItem"; -import { AnForm, IAnFormProps, IAnFormSubmit } from "./Form"; -import { useModalTrigger } from "../core/useModalTrigger"; import { useModalSubmit } from "../core/useModalSubmit"; -import { useVisible } from "@/hooks/useVisible"; +import { useModalTrigger } from "../core/useModalTrigger"; +import { AnForm, IAnFormProps, IAnFormSubmit } from "./Form"; +import { IAnFormItem } from "./FormItem"; /** * 表单组件 @@ -14,11 +14,11 @@ export const AnFormModal = defineComponent({ props: { /** * 弹窗标题 - * @default '添加' + * @default '新增' */ title: { type: [String, Function] as PropType, - default: "添加", + default: "新增", }, /** * 触发元素 diff --git a/src/components/AnForm/components/FormSetter.tsx b/src/components/AnForm/components/FormSetter.tsx index 1dbe946..c65883b 100644 --- a/src/components/AnForm/components/FormSetter.tsx +++ b/src/components/AnForm/components/FormSetter.tsx @@ -4,24 +4,42 @@ export type SetterMap = typeof setterMap; export type SetterType = keyof SetterMap; -export type SetterItem = { - [key in SetterType]: Partial< - Omit & { - /** - * 控件类型 - * @example 'input' - */ - setter: key; - /** - * 控件插槽 - */ - setterSlots: Recordable; - /** - * 控件参数 - */ - setterProps: Recordable; - } - >; -}[SetterType]; +export type SetterItemMap = { + [key in SetterType]: { + /** + * 控件类型 + * @example + * ```ts + * 'input' + * ``` + */ + setter?: key; + /** + * 控件参数 + * @example + * ```tsx + * { type: "password" } + * ``` + */ + setterProps?: SetterMap[key]['setterProps']; + /** + * 控件插槽 + * @example + * ```tsx + * { + * help: (props) => { + * return + * {props.item.label} + * + * } + * } + * ``` + */ + setterSlots?: SetterMap[key]['setterSlots']; + }; +}; + +export type SetterItem = SetterItemMap[SetterType]; export { setterMap }; + diff --git a/src/components/AnForm/core/useFormItems.ts b/src/components/AnForm/core/useFormItems.ts index 211d63d..7082bba 100644 --- a/src/components/AnForm/core/useFormItems.ts +++ b/src/components/AnForm/core/useFormItems.ts @@ -9,7 +9,7 @@ export function useFormItems(items: Ref, model: Ref) const getItemOptions = (field: string) => { const item = getItem(field); if (item) { - return (item.nodeProps as any)?.options; + return (item.setterProps as any)?.options; } }; diff --git a/src/components/AnForm/hooks/useForm.tsx b/src/components/AnForm/hooks/useForm.tsx index 3d56b14..9afeed6 100644 --- a/src/components/AnForm/hooks/useForm.tsx +++ b/src/components/AnForm/hooks/useForm.tsx @@ -1,9 +1,17 @@ -import { FormItem, useItems } from "./useItems"; import { AnForm, IAnForm } from "../components/Form"; +import { FormItem, useItems } from "./useItems"; export type FormUseOptions = Partial> & { /** * 表单项 + * @example + * ```ts + * [{ + * field: 'name', + * label: '昵称', + * setter: 'input' + * }] + * ``` */ items?: FormItem[]; }; diff --git a/src/components/AnForm/hooks/useItems.ts b/src/components/AnForm/hooks/useItems.ts index fcd4de1..4c0c9ba 100644 --- a/src/components/AnForm/hooks/useItems.ts +++ b/src/components/AnForm/hooks/useItems.ts @@ -1,39 +1,49 @@ -import { defaultsDeep, merge, omit } from "lodash-es"; -import { Rule, useRules } from "./useRules"; -import { IAnFormItem } from "../components/FormItem"; -import { setterMap } from "../components/FormSetter"; +import { defaultsDeep, merge, omit } from 'lodash-es'; +import { IAnFormItem, IAnFormItemBase } from '../components/FormItem'; +import { SetterItem, setterMap } from '../components/FormSetter'; +import { Rule, useRules } from './useRules'; /** * 表单项数据 */ -export type FormItem = Omit & { - /** - * 默认值 - * @example 1 - */ - value?: any; +export type FormItem = Omit & + SetterItem & { + /** + * 默认值 + * @example + * ```ts + * '1' + * ``` + */ + value?: any; - /** - * 是否必填 - * @default false - */ - required?: boolean; + /** + * 是否必填 + * @default + * ```ts + * false + * ``` + */ + required?: boolean; - /** - * 校验规则 - * @example ['email'] - */ - rules?: Rule[]; -}; + /** + * 校验规则 + * @example + * ```ts + * ['email'] + * ``` + */ + rules?: Rule[]; + }; const ITEM: Partial = { - setter: "input", + setter: 'input', itemProps: {}, }; const SUBMIT_ITEM: FormItem = { - field: "id", - setter: "submit", + field: 'id', + setter: 'submit', itemProps: { hideLabel: true, }, @@ -46,19 +56,19 @@ export function useItems(list: FormItem[], model: Recordable, submit: boolean) { for (const item of list) { let target: any = defaultsDeep({}, ITEM); - if (!item.setter || typeof item.setter === "string") { - const defaults = setterMap[item.setter ?? "input"]; + if (!item.setter || typeof item.setter === 'string') { + const defaults = setterMap[item.setter ?? 'input']; if (defaults) { defaultsDeep(target, defaults); } } - if (item.setter === "submit") { + if (item.setter === 'submit') { target = merge(target, SUBMIT_ITEM); hasSubmit = true; } - target = merge(target, omit(item, ["required", "rules"])); + target = merge(target, omit(item, ['required', 'rules'])); const rules = useRules(item); if (rules) { diff --git a/src/components/AnForm/setters/Cascader.tsx b/src/components/AnForm/setters/Cascader.tsx index d2e1696..266dd52 100644 --- a/src/components/AnForm/setters/Cascader.tsx +++ b/src/components/AnForm/setters/Cascader.tsx @@ -1,14 +1,17 @@ -import { Cascader, CascaderInstance } from "@arco-design/web-vue"; -import { initOptions } from "../utils/initOptions"; +import { Cascader, CascaderInstance } from '@arco-design/web-vue'; +import { initOptions } from '../utils/initOptions'; +import { defineSetter } from './util'; -type Props = CascaderInstance["$props"]; +type CascaderProps = CascaderInstance['$props']; -export default { - render: Cascader, - init: initOptions, - nodeProps: { - placeholder: "请选择", +type CascaderSlots = 'label' | 'prefix' | 'arrowIcon' | 'loadingIcon' | 'searchIcon' | 'empty' | 'option'; + +export default defineSetter({ + setter: Cascader, + setterProps: { + placeholder: '请选择', allowClear: true, - expandTrigger: "hover", - } as Props, -}; + expandTrigger: 'hover', + }, + onSetup: initOptions as any, +}); diff --git a/src/components/AnForm/setters/Custom.tsx b/src/components/AnForm/setters/Custom.tsx index 8db05e3..84d8bc7 100644 --- a/src/components/AnForm/setters/Custom.tsx +++ b/src/components/AnForm/setters/Custom.tsx @@ -1,6 +1,5 @@ -export default { - render: () => { - return "1"; - }, - nodeProps: {}, -}; +import { defineSetter } from './util'; + +export default defineSetter<{ a: number }, '11'>({ + setter: () => '1', +}); diff --git a/src/components/AnForm/setters/Date.tsx b/src/components/AnForm/setters/Date.tsx index 6fbdf83..4dacaac 100644 --- a/src/components/AnForm/setters/Date.tsx +++ b/src/components/AnForm/setters/Date.tsx @@ -1,11 +1,14 @@ -import { DatePicker, DatePickerInstance } from "@arco-design/web-vue"; +import { DatePicker, DatePickerInstance } from '@arco-design/web-vue'; +import { defineSetter } from './util'; -type Props = DatePickerInstance["$props"]; +type DateProps = DatePickerInstance['$props']; -export default { - render: DatePicker, - nodeProps: { - placeholder: "请选择", +type DateSlots = 'prefix' | 'suffixIcon' | 'iconNextDouble' | 'iconPrevDouble' | 'iconNext' | 'iconPrev' | 'cell' | 'extra'; + +export default defineSetter({ + setter: DatePicker, + setterProps: { + placeholder: '请选择', allowClear: true, - } as Props, -}; + } as any, +}); diff --git a/src/components/AnForm/setters/DateRange.tsx b/src/components/AnForm/setters/DateRange.tsx index a2226e1..4b8a90f 100644 --- a/src/components/AnForm/setters/DateRange.tsx +++ b/src/components/AnForm/setters/DateRange.tsx @@ -1,10 +1,13 @@ -import { RangePicker, RangePickerInstance } from "@arco-design/web-vue"; +import { RangePicker, RangePickerInstance } from '@arco-design/web-vue'; +import { defineSetter } from './util'; -type Props = RangePickerInstance["$props"]; +type RangeProps = RangePickerInstance['$props']; -export default { - render: RangePicker, - nodeProps: { +type RangeSlots = "1"; + +export default defineSetter({ + setter: RangePicker, + setterProps: { allowClear: true, - } as Props, -}; + }, +}); diff --git a/src/components/AnForm/setters/Input.tsx b/src/components/AnForm/setters/Input.tsx index 7df4095..2a990eb 100644 --- a/src/components/AnForm/setters/Input.tsx +++ b/src/components/AnForm/setters/Input.tsx @@ -1,11 +1,14 @@ -import { Input, InputInstance } from "@arco-design/web-vue"; +import { Input, InputInstance } from '@arco-design/web-vue'; +import { defineSetter } from './util'; -type Props = InputInstance["$props"]; +type InputProps = InputInstance['$props']; -export default { - render: Input, - nodeProps: { - placeholder: "请输入", +type InputSlots = "2"; + +export default defineSetter({ + setter: Input, + setterProps: { + placeholder: '请输入', allowClear: true, - } as Props, -}; + }, +}); diff --git a/src/components/AnForm/setters/Number.tsx b/src/components/AnForm/setters/Number.tsx index 4659f5f..afd0f2e 100644 --- a/src/components/AnForm/setters/Number.tsx +++ b/src/components/AnForm/setters/Number.tsx @@ -1,12 +1,15 @@ -import { InputInstance, InputNumber, InputNumberInstance } from "@arco-design/web-vue"; +import { InputInstance, InputNumber, InputNumberInstance } from '@arco-design/web-vue'; +import { defineSetter } from './util'; -type Props = InputInstance["$props"] & InputNumberInstance["$props"]; +type NumberProps = InputInstance['$props'] | InputNumberInstance['$props']; -export default { - render: InputNumber, - nodeProps: { - placeholder: "请输入", +type NumberSlots = "3"; + +export default defineSetter({ + setter: InputNumber, + setterProps: { + placeholder: '请输入', defaultValue: 0, allowClear: true, - } as Props, -}; + }, +}); diff --git a/src/components/AnForm/setters/Password.tsx b/src/components/AnForm/setters/Password.tsx index 301027d..de57ccb 100644 --- a/src/components/AnForm/setters/Password.tsx +++ b/src/components/AnForm/setters/Password.tsx @@ -1,10 +1,13 @@ -import { InputInstance, InputPassword, InputPasswordInstance } from "@arco-design/web-vue"; +import { InputInstance, InputPassword, InputPasswordInstance } from '@arco-design/web-vue'; +import { defineSetter } from './util'; -type Props = InputInstance["$props"] & InputPasswordInstance["$props"]; +type PasswordProps = InputInstance['$props'] & InputPasswordInstance['$props']; -export default { - render: InputPassword, - nodeProps: { - placeholder: "请输入", - } as Props, -}; +type PasswordSlots = "4"; + +export default defineSetter({ + setter: InputPassword, + setterProps: { + placeholder: '请输入', + }, +}); diff --git a/src/components/AnForm/setters/Search.tsx b/src/components/AnForm/setters/Search.tsx index 7cd7437..6da2933 100644 --- a/src/components/AnForm/setters/Search.tsx +++ b/src/components/AnForm/setters/Search.tsx @@ -1,11 +1,14 @@ -import { InputInstance, InputSearch, InputSearchInstance } from "@arco-design/web-vue"; +import { InputInstance, InputSearch, InputSearchInstance } from '@arco-design/web-vue'; +import { defineSetter } from './util'; -type Props = InputInstance["$props"] & InputSearchInstance["$props"]; +type SearchProps = InputInstance['$props'] & InputSearchInstance['$props']; -export default { - render: InputSearch, - nodeProps: { - placeholder: "请输入", +type SearchSlots = "5"; + +export default defineSetter({ + setter: InputSearch, + setterProps: { + placeholder: '请输入', allowClear: true, - } as Props, -}; + }, +}); diff --git a/src/components/AnForm/setters/Select.tsx b/src/components/AnForm/setters/Select.tsx index 9248a15..a5be68c 100644 --- a/src/components/AnForm/setters/Select.tsx +++ b/src/components/AnForm/setters/Select.tsx @@ -1,14 +1,18 @@ -import { Select, SelectInstance, SelectOptionData } from "@arco-design/web-vue"; -import { initOptions } from "../utils/initOptions"; +import { Select, SelectInstance } from '@arco-design/web-vue'; +import { initOptions } from '../utils/initOptions'; +import { defineSetter } from './util'; -export default { - render: Select, - init: initOptions, - nodeProps: { - placeholder: "请选择", +type SelectProps = SelectInstance['$props']; + +type SelectSlots = "6"; + +export default defineSetter({ + setter: Select, + onSetup: initOptions as any, + setterProps: { + placeholder: '请选择', allowClear: true, allowSearch: true, options: [], - } as SelectInstance["$props"], - options: [] as SelectOptionData[] | ((arg: any) => Recordable[] | Promise) | undefined, -}; + }, +}); diff --git a/src/components/AnForm/setters/Submit.tsx b/src/components/AnForm/setters/Submit.tsx index d4510f2..d51410a 100644 --- a/src/components/AnForm/setters/Submit.tsx +++ b/src/components/AnForm/setters/Submit.tsx @@ -1,8 +1,9 @@ import { Button } from "@arco-design/web-vue"; import { FormContextKey } from "../core/useFormContext"; +import { defineSetter } from "./util"; -export default { - render() { +export default defineSetter<{ a1?: number }, "10">({ + setter() { const { loading, submitForm, resetModel } = inject(FormContextKey)!; return ( <> @@ -15,5 +16,5 @@ export default { ); }, - nodeProps: {}, -}; + setterProps: {}, +}); diff --git a/src/components/AnForm/setters/Textarea.tsx b/src/components/AnForm/setters/Textarea.tsx index 0d29b38..3533199 100644 --- a/src/components/AnForm/setters/Textarea.tsx +++ b/src/components/AnForm/setters/Textarea.tsx @@ -1,11 +1,14 @@ -import { InputInstance, Textarea, TextareaInstance } from "@arco-design/web-vue"; +import { InputInstance, Textarea, TextareaInstance } from '@arco-design/web-vue'; +import { defineSetter } from './util'; -type Props = InputInstance["$props"] & TextareaInstance["$props"]; +type TextareaProps = InputInstance['$props'] & TextareaInstance['$props']; -export default { - render: Textarea, - nodeProps: { - placeholder: "请输入", +type TextareaSlots = "7"; + +export default defineSetter({ + setter: Textarea, + setterProps: { + placeholder: '请输入', allowClear: true, - } as Props, -}; + }, +}); diff --git a/src/components/AnForm/setters/Time.tsx b/src/components/AnForm/setters/Time.tsx index 7a9418f..b9a4813 100644 --- a/src/components/AnForm/setters/Time.tsx +++ b/src/components/AnForm/setters/Time.tsx @@ -1,10 +1,13 @@ -import { TimePicker, TimePickerInstance } from "@arco-design/web-vue"; +import { TimePicker, TimePickerInstance } from '@arco-design/web-vue'; +import { defineSetter } from './util'; -type Props = TimePickerInstance["$props"]; +type TimeProps = TimePickerInstance['$props']; -export default { - render: TimePicker, - nodeProps: { +type TimeSlots = "8"; + +export default defineSetter({ + setter: TimePicker, + setterProps: { allowClear: true, - } as Props, -}; + }, +}); diff --git a/src/components/AnForm/setters/TreeSelect.tsx b/src/components/AnForm/setters/TreeSelect.tsx index 1774c69..dea71ec 100644 --- a/src/components/AnForm/setters/TreeSelect.tsx +++ b/src/components/AnForm/setters/TreeSelect.tsx @@ -1,15 +1,17 @@ -import { TreeSelect, TreeSelectInstance } from "@arco-design/web-vue"; -import { initOptions } from "../utils/initOptions"; +import { TreeSelect, TreeSelectInstance } from '@arco-design/web-vue'; +import { initOptions } from '../utils/initOptions'; +import { defineSetter } from './util'; -type Props = TreeSelectInstance["$props"]; +type TreeSelectProps = TreeSelectInstance['$props']; -export default { - render: TreeSelect, - init: (arg: any) => initOptions(arg, "data"), - nodeProps: { - placeholder: "请选择", +type TreeSelectSlots = "9"; + +export default defineSetter({ + setter: TreeSelect, + onSetup: (arg: any) => initOptions(arg, 'data') as any, + setterProps: { + placeholder: '请选择', allowClear: true, allowSearch: true, - options: [], - } as Props, -}; + }, +}); diff --git a/src/components/AnForm/setters/index.ts b/src/components/AnForm/setters/index.ts index c2aca90..e08c10d 100644 --- a/src/components/AnForm/setters/index.ts +++ b/src/components/AnForm/setters/index.ts @@ -1,16 +1,16 @@ -import cascader from "./Cascader"; -import custom from "./Custom"; -import date from "./Date"; -import input from "./Input"; -import number from "./Number"; -import password from "./Password"; -import search from "./Search"; -import select from "./Select"; -import submit from "./Submit"; -import textarea from "./Textarea"; -import time from "./Time"; -import treeSelect from "./TreeSelect"; -import dateRange from "./DateRange"; +import cascader from './Cascader'; +import custom from './Custom'; +import date from './Date'; +import dateRange from './DateRange'; +import input from './Input'; +import number from './Number'; +import password from './Password'; +import search from './Search'; +import select from './Select'; +import submit from './Submit'; +import textarea from './Textarea'; +import time from './Time'; +import treeSelect from './TreeSelect'; export default { input, diff --git a/src/components/AnForm/setters/util.ts b/src/components/AnForm/setters/util.ts new file mode 100644 index 0000000..abbbbda --- /dev/null +++ b/src/components/AnForm/setters/util.ts @@ -0,0 +1,39 @@ +import { Component } from 'vue'; +import { IAnFormItemBase, IAnFormItemSlot } from '../components/FormItem'; + +export interface ItemSetter

{ + /** + * 输入组件 + */ + setter: Component; + + /** + * 输入组件参数 + */ + setterProps?: P; + + /** + * 空间插槽 + */ + setterSlots?: { + /** + * 控件插槽 + * @example + * ```tsx + * (props) => { + * return {props.item.label} + * } + * ``` + */ + [key in S]?: IAnFormItemSlot; + }; + + /** + * 初始化钩子 + */ + onSetup?: (model: Recordable, item: IAnFormItemBase, items: IAnFormItemBase[]) => void; +} + +export function defineSetter

(setter: ItemSetter) { + return setter; +} diff --git a/src/components/AnForm/utils/defineSetter.ts b/src/components/AnForm/utils/defineSetter.ts index 5212b62..caa62e7 100644 --- a/src/components/AnForm/utils/defineSetter.ts +++ b/src/components/AnForm/utils/defineSetter.ts @@ -1,27 +1,26 @@ -import { Component } from "vue"; -import { IAnFormItem, IAnFormItemBase } from "../components/FormItem"; +import { Component } from 'vue'; +import { IAnFormItemBase, IAnFormItemSlot } from '../components/FormItem'; -interface Setter any ? InstanceType["$props"] : any> { +export interface ItemSetter

{ /** * 输入组件 */ - component: T; + setter: Component; /** * 输入组件参数 */ - componentProps?: P; + setterProps?: P; + + /** + * 空间插槽 + */ + setterSlots?: { + [key in S]?: IAnFormItemSlot; + }; /** * 初始化钩子 - * @param model 表单数据 - * @param item 表单项 - * @param items 表单项列表 - * @returns */ onSetup?: (model: Recordable, item: IAnFormItemBase, items: IAnFormItemBase[]) => void; -} - -export function defineSetter(options: Setter): Setter { - return options; -} +} \ No newline at end of file diff --git a/src/pages/home/home.vue b/src/pages/home/home.vue index 6bcd057..8e724ed 100644 --- a/src/pages/home/home.vue +++ b/src/pages/home/home.vue @@ -28,7 +28,7 @@ const { component: UpForm, model: emodel } = useForm({ { field: 'xsa', label: '动态渲染', - setter: 'input', + setter: 'cascader', visible: props => props.model.id, }, { @@ -93,7 +93,7 @@ const { component: UpForm, model: emodel } = useForm({ }, }, ], - nodeProps: { + setterProps: { valueKey: 'value', }, },