# Form 表单组件

对 `antv` 的 form 组件进行封装，扩展一些常用的功能

> 如果文档内没有，可以尝试在在线示例内寻找

## 代码演示

### 基础表单

<CodePreview src="/doc-comp/form/basic" :height="1100">
<details>
<summary>展开查看</summary>

```vue
<template>
  <PageWrapper title="表单基础示例" contentFullHeight>
    <CollapseContainer title="基础示例">
      <BasicForm
        autoFocusFirstItem
        :labelWidth="200"
        :schemas="schemas"
        :actionColOptions="{ span: 24 }"
        @submit="handleSubmit"
        @reset="handleReset"
      >
        <template #selectA="{ model, field }">
          <a-select
            v-model:value="model[field]"
            :options="optionsA"
            mode="multiple"
            allowClear
            @change="valueSelectA = model[field]"
          />
        </template>
        <template #selectB="{ model, field }">
          <a-select
            v-model:value="model[field]"
            :options="optionsB"
            mode="multiple"
            allowClear
            @change="valueSelectB = model[field]"
          />
        </template>
        <template #localSearch="{ model, field }">
          <ApiSelect
            v-model:value="model[field]"
            :api="optionsListApi"
            showSearch
            optionFilterProp="label"
            resultField="list"
            labelField="name"
            valueField="id"
          />
        </template>
        <template #remoteSearch="{ model, field }">
          <ApiSelect
            v-model:value="model[field]"
            :api="optionsListApi"
            showSearch
            :filterOption="false"
            resultField="list"
            labelField="name"
            valueField="id"
            :params="searchParams"
            @search="onSearch"
          />
        </template>
      </BasicForm>
    </CollapseContainer>
  </PageWrapper>
</template>
<script lang="ts" setup>
  import { ApiSelect, BasicForm, FormSchema, PageWrapper, useMessage } from '@eciol/ant-ui';
  import { uploadApi } from '@eciol/api';
  import { useDebounceFn } from '@eciol/hooks';
  import { CollapseContainer } from '@eciol/share-ui';
  import { cloneDeep } from 'lodash-es';

  import { areaRecord } from '@/api/demo/cascader';
  import { optionsListApi } from '@/api/demo/select';
  import { treeOptionsListApi } from '@/api/demo/tree';

  const valueSelectA = ref<string[]>([]);
  const valueSelectB = ref<string[]>([]);
  const options = ref<Recordable[]>([]);
  for (let i = 1; i < 10; i++) options.value.push({ label: '选项' + i, value: `${i}` });

  const optionsA = computed(() => {
    return cloneDeep(unref(options)).map((op) => {
      op.disabled = unref(valueSelectB).indexOf(op.value) !== -1;
      return op;
    });
  });
  const optionsB = computed(() => {
    return cloneDeep(unref(options)).map((op) => {
      op.disabled = unref(valueSelectA).indexOf(op.value) !== -1;
      return op;
    });
  });

  const provincesOptions = [
    {
      id: 'guangdong',
      label: '广东省',
      value: '1',
      key: '1',
    },
    {
      id: 'jiangsu',
      label: '江苏省',
      value: '2',
      key: '2',
    },
  ];
  const citiesOptionsData = {
    guangdong: [
      {
        label: '珠海市',
        value: '1',
        key: '1',
      },
      {
        label: '深圳市',
        value: '2',
        key: '2',
      },
      {
        label: '广州市',
        value: '3',
        key: '3',
      },
    ],
    jiangsu: [
      {
        label: '南京市',
        value: '1',
        key: '1',
      },
      {
        label: '无锡市',
        value: '2',
        key: '2',
      },
      {
        label: '苏州市',
        value: '3',
        key: '3',
      },
    ],
  };

  const schemas: FormSchema[] = [
    {
      field: 'divider-basic',
      component: 'Divider',
      label: '基础字段',
      colProps: {
        span: 24,
      },
    },
    {
      field: 'field1',
      component: 'Input',
      label: '字段1',

      colProps: {
        span: 8,
      },
      // componentProps:{},
      // can func
      componentProps: ({ schema, formModel }) => {
        console.log('form:', schema);
        console.log('formModel:', formModel);
        return {
          placeholder: '自定义placeholder',
          onChange: (e: any) => {
            console.log(e);
          },
        };
      },
      renderComponentContent: () => {
        return {
          prefix: () => 'pSlot',
          suffix: () => 'sSlot',
        };
      },
    },
    {
      field: 'field2',
      component: 'Input',
      label: '带后缀',
      defaultValue: '111',
      colProps: {
        span: 8,
      },
      componentProps: {
        onChange: (e: any) => {
          console.log(e);
        },
      },
      suffix: '天',
    },
    {
      field: 'fieldsc',
      component: 'Upload',
      label: '上传',
      colProps: {
        span: 8,
      },
      rules: [{ required: true, message: '请选择上传文件' }],
      componentProps: {
        api: uploadApi,
      },
    },
    {
      field: 'field3',
      component: 'DatePicker',
      label: '字段3',
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field4',
      component: 'Select',
      label: '字段4',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
            key: '1',
          },
          {
            label: '选项2',
            value: '2',
            key: '2',
          },
        ],
      },
    },
    {
      field: 'field5',
      component: 'CheckboxGroup',
      label: '字段5',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
          },
          {
            label: '选项2',
            value: '2',
          },
        ],
      },
    },
    {
      field: 'field7',
      component: 'RadioGroup',
      label: '字段7',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
          },
          {
            label: '选项2',
            value: '2',
          },
        ],
      },
    },
    {
      field: 'field8',
      component: 'Checkbox',
      label: '字段8',
      colProps: {
        span: 8,
      },
      renderComponentContent: 'Check',
    },
    {
      field: 'field9',
      component: 'Switch',
      label: '字段9',
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field10',
      component: 'RadioButtonGroup',
      label: '字段10',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
          },
          {
            label: '选项2',
            value: '2',
          },
        ],
        onChange: (e, v) => {
          console.log('RadioButtonGroup====>:', e, v);
        },
      },
    },
    {
      field: 'field11',
      component: 'Cascader',
      label: '字段11',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [
          {
            value: 'zhejiang',
            label: 'Zhejiang',
            children: [
              {
                value: 'hangzhou',
                label: 'Hangzhou',
                children: [
                  {
                    value: 'xihu',
                    label: 'West Lake',
                  },
                ],
              },
            ],
          },
          {
            value: 'jiangsu',
            label: 'Jiangsu',
            children: [
              {
                value: 'nanjing',
                label: 'Nanjing',
                children: [
                  {
                    value: 'zhonghuamen',
                    label: 'Zhong Hua Men',
                  },
                ],
              },
            ],
          },
        ],
      },
    },
    {
      field: 'divider-api-select',
      component: 'Divider',
      label: '远程下拉演示',
      colProps: {
        span: 24,
      },
    },
    {
      field: 'field30',
      component: 'ApiSelect',
      label: '懒加载远程下拉',
      required: true,
      componentProps: {
        // more details see /src/components/Form/src/components/ApiSelect.vue
        api: optionsListApi,
        params: {
          id: 1,
        },

        resultField: 'list',
        // use name as label
        labelField: 'name',
        // use id as value
        valueField: 'id',
        // not request untill to select
        immediate: true,
        onChange: (e, v) => {
          console.log('ApiSelect====>:', e, v);
        },
        // atfer request callback
        onOptionsChange: (options) => {
          console.log('get options', options.length, options);
        },
      },
      colProps: {
        span: 8,
      },
      defaultValue: '0',
    },
    {
      field: 'field8',
      component: 'ApiCascader',
      label: '联动ApiCascader',
      required: true,
      colProps: {
        span: 8,
      },
      componentProps: {
        api: areaRecord,
        apiParamKey: 'parentCode',
        dataField: 'data',
        labelField: 'name',
        valueField: 'code',
        initFetchParams: {
          parentCode: '',
        },
        isLeaf: (record) => {
          return !(record.levelType < 3);
        },
        onChange: (e, ...v) => {
          console.log('ApiCascader====>:', e, v);
        },
      },
    },
    {
      field: 'field31',
      component: 'Input',
      label: '下拉本地搜索',
      helpMessage: ['ApiSelect组件', '远程数据源本地搜索', '只发起一次请求获取所有选项'],
      required: true,
      slot: 'localSearch',
      colProps: {
        span: 8,
      },
      defaultValue: '0',
    },
    {
      field: 'field32',
      component: 'Input',
      label: '下拉远程搜索',
      helpMessage: ['ApiSelect组件', '将关键词发送到接口进行远程搜索'],
      required: true,
      slot: 'remoteSearch',
      colProps: {
        span: 8,
      },
      defaultValue: '0',
    },
    {
      field: 'field33',
      component: 'ApiTreeSelect',
      label: '远程下拉树',
      helpMessage: ['ApiTreeSelect组件', '使用接口提供的数据生成选项'],
      required: true,
      componentProps: {
        api: treeOptionsListApi,
        resultField: 'list',
        onChange: (e, v) => {
          console.log('ApiTreeSelect====>:', e, v);
        },
      },
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field331',
      component: 'ApiTreeSelect',
      label: '远程懒加载下拉树',
      helpMessage: ['ApiTreeSelect组件', '使用接口提供的数据生成选项'],
      required: true,
      componentProps: {
        api: () => {
          return new Promise((resolve) => {
            resolve([
              {
                title: 'Parent Node',
                value: '0-0',
              },
            ]);
          });
        },
        async: true,
        onChange: (e, v) => {
          console.log('ApiTreeSelect====>:', e, v);
        },
        onLoadData: ({ treeData, resolve, treeNode }) => {
          console.log('treeNode====>:', treeNode);
          setTimeout(() => {
            const children: Recordable[] = [
              { title: `Child Node ${treeNode.eventKey}-0`, value: `${treeNode.eventKey}-0` },
              { title: `Child Node ${treeNode.eventKey}-1`, value: `${treeNode.eventKey}-1` },
            ];
            children.forEach((item) => {
              item.isLeaf = false;
              item.children = [];
            });
            treeNode.dataRef.children = children;
            treeData.value = [...treeData.value];
            resolve();
            return;
          }, 300);
        },
      },
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field34',
      component: 'ApiRadioGroup',
      label: '远程Radio',
      helpMessage: ['ApiRadioGroup组件', '使用接口提供的数据生成选项'],
      required: true,
      componentProps: {
        api: () =>
          Promise.resolve({
            list: [
              {
                name: '张三',
                id: '1',
              },
              {
                name: '李四',
                id: '2',
              },
            ],
          }),
        params: {
          count: 2,
        },
        resultField: 'list',
        // use name as label
        labelField: 'name',
        // use id as value
        valueField: 'id',
      },
      defaultValue: '1',
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field35',
      component: 'ApiRadioGroup',
      label: '远程Radio',
      helpMessage: ['ApiRadioGroup组件', '使用接口提供的数据生成选项'],
      required: true,
      componentProps: {
        api: () =>
          Promise.resolve({
            list: [
              {
                name: '张三',
                id: '1',
              },
              {
                name: '李四',
                id: '2',
              },
            ],
          }),
        params: {
          count: 2,
        },
        resultField: 'list',
        // use name as label
        labelField: 'name',
        // use id as value
        valueField: 'id',
        isBtn: true,
        onChange: (e, v) => {
          console.log('ApiRadioGroup====>:', e, v);
        },
      },
      colProps: {
        span: 8,
      },
    },
    // {
    //   field: 'field36',
    //   component: 'ApiTree',
    //   label: '远程Tree',
    //   helpMessage: ['ApiTree组件', '使用接口提供的数据生成选项'],
    //   required: true,
    //   componentProps: {
    //     api: treeOptionsListApi,
    //     params: {
    //       count: 2,
    //     },
    //     afterFetch: (v) => {
    //       //do something
    //       return v;
    //     },
    //     resultField: 'list',
    //   },
    //   colProps: {
    //     span: 8,
    //   },
    // },
    {
      field: 'divider-linked',
      component: 'Divider',
      label: '字段联动',
      colProps: {
        span: 24,
      },
    },
    {
      field: 'province',
      component: 'Select',
      label: '省份',
      colProps: {
        span: 8,
      },
      componentProps: ({ formModel, formActionType }) => {
        return {
          options: provincesOptions,
          placeholder: '省份与城市联动',
          onChange: (e: any) => {
            // console.log(e)
            let citiesOptions =
              e == 1
                ? citiesOptionsData[provincesOptions[0].id]
                : citiesOptionsData[provincesOptions[1].id];
            // console.log(citiesOptions)
            if (e === undefined) {
              citiesOptions = [];
            }
            formModel.city = undefined; //  reset city value
            const { updateSchema } = formActionType;
            updateSchema({
              field: 'city',
              componentProps: {
                options: citiesOptions,
              },
            });
          },
        };
      },
    },
    {
      field: 'city',
      component: 'Select',
      label: '城市',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [], // defalut []
        placeholder: '省份与城市联动',
      },
    },
    {
      field: 'divider-selects',
      component: 'Divider',
      label: '互斥多选',
      helpMessage: ['两个Select共用数据源', '但不可选择对方已选中的项目'],
      colProps: {
        span: 24,
      },
    },
    {
      field: 'selectA',
      component: 'Select',
      label: '互斥SelectA',
      slot: 'selectA',
      defaultValue: [],
      colProps: {
        span: 8,
      },
    },
    {
      field: 'selectB',
      component: 'Select',
      label: '互斥SelectB',
      slot: 'selectB',
      defaultValue: [],
      colProps: {
        span: 8,
      },
    },
    {
      field: 'divider-deconstruct',
      component: 'Divider',
      label: '字段解构',
      helpMessage: ['如果组件的值是 array 或者 object', '可以根据 ES6 的解构语法分别取值'],
      colProps: {
        span: 24,
      },
    },
    {
      field: '[startTime, endTime]',
      label: '时间范围',
      component: 'TimeRangePicker',
      componentProps: {
        format: 'HH:mm:ss',
        placeholder: ['开始时间', '结束时间'],
      },
    },
    {
      field: '[startDate, endDate]',
      label: '日期范围',
      component: 'RangePicker',
      componentProps: {
        format: 'YYYY-MM-DD',
        placeholder: ['开始日期', '结束日期'],
      },
    },
    {
      field: '[startDateTime, endDateTime]',
      label: '日期时间范围',
      component: 'RangePicker',
      componentProps: {
        format: 'YYYY-MM-DD HH:mm:ss',
        placeholder: ['开始日期、时间', '结束日期、时间'],
        showTime: { format: 'HH:mm:ss' },
      },
    },
    {
      field: 'divider-others',
      component: 'Divider',
      label: '其它',
      colProps: {
        span: 24,
      },
    },
    {
      field: 'field20',
      component: 'InputNumber',
      label: '字段20',
      required: true,
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field21',
      component: 'Slider',
      label: '字段21',
      componentProps: {
        min: 0,
        max: 100,
        range: true,
        marks: {
          20: '20°C',
          60: '60°C',
        },
      },
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field22',
      component: 'Rate',
      label: '字段22',
      defaultValue: 3,
      colProps: {
        span: 8,
      },
      componentProps: {
        disabled: false,
        allowHalf: true,
      },
    },
  ];

  const check = ref(null);
  const { createMessage } = useMessage();
  const keyword = ref<string>('');
  const searchParams = computed<Recordable>(() => {
    return { keyword: unref(keyword) };
  });

  function _onSearch(value: string) {
    keyword.value = value;
  }
  const onSearch = useDebounceFn(_onSearch, 300);
  const handleReset = () => {
    keyword.value = '';
  };
  const handleSubmit = (values: any) => {
    console.log('values', values);
    createMessage.success('click search,values:' + JSON.stringify(values));
  };
</script>
```

</details>
</CodePreview>

### useForm

<CodePreview src="/doc-comp/form/useForm" :height="400">
<details>
<summary>展开查看</summary>

```vue
<template>
  <PageWrapper title="UseForm操作示例">
    <a-button class="mb-4" type="primary" @click="showDrawer"> 更改设置 </a-button>

    <ADrawer v-model:open="open" title="更改设置" placement="right">
      <BasicForm ref="settingFormRef" @register="registerSetting" @submit="handleSubmitSetting">
        <template #other>
          <ASpace>
            <a-button
              @click="() => withClose({ resetButtonOptions: { disabled: true, text: '重置New' } })"
            >
              修改重置按钮
            </a-button>
            <a-button
              @click="() => withClose({ submitButtonOptions: { disabled: true, loading: true } })"
            >
              修改查询按钮
            </a-button>
            <a-button class="mr-2" @click="handleLoad"> 联动回显 </a-button>
          </ASpace>
        </template>
      </BasicForm>
      <template #extra>
        <ASpace>
          <a-button @click="resetSettings">重置设置</a-button>
          <a-button type="primary" @click="onSettings">应用</a-button>
        </ASpace>
      </template>
    </ADrawer>

    <CollapseContainer title="useForm示例">
      <BasicForm @register="register" @submit="handleSubmit" />
    </CollapseContainer>
  </PageWrapper>
</template>

<script lang="ts" setup>
  import { BasicForm, type FormProps, FormSchema, PageWrapper, useForm } from '@eciol/ant-ui';
  import { CollapseContainer } from '@eciol/share-ui';

  import { areaRecord } from '@/api/demo/cascader';

  const sizeList = [
    { value: 'large', label: 'large' },
    { value: 'middle', label: 'middle' },
    { value: 'small', label: 'small' },
    { value: 'default', label: 'defualt' },
  ];

  const layoutList = [
    { value: 'vertical', label: 'vertical' },
    { value: 'inline', label: 'inline' },
    { value: 'horizontal', label: 'horizontal' },
  ];

  const labelAlignList = [
    { value: 'left', label: 'left' },
    { value: 'right', label: 'right' },
  ];

  const schemas: FormSchema[] = [
    {
      field: 'field1',
      component: 'Input',
      label: '字段1',
      colProps: { span: 8 },
      componentProps: {
        placeholder: '自定义placeholder',
        onChange: (e: any) => {
          console.log(e);
        },
      },
    },
    {
      field: 'field2',
      component: 'Input',
      label: '字段2',
      colProps: { span: 8 },
    },
    {
      field: 'field3',
      component: 'DatePicker',
      label: '字段3',
      colProps: { span: 8 },
    },
    {
      field: 'fieldTime',
      component: 'RangePicker',
      label: '时间字段',
      colProps: { span: 8 },
    },
    {
      field: 'field4',
      component: 'Select',
      label: '字段4',
      colProps: { span: 8 },
      componentProps: {
        options: [
          { label: '选项1', value: '1', key: '1' },
          { label: '选项2', value: '2', key: '2' },
        ],
      },
    },
    {
      field: 'field5',
      component: 'CheckboxGroup',
      label: '字段5',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [
          { label: '选项1', value: '1' },
          { label: '选项2', value: '2' },
        ],
      },
    },
    {
      field: 'field7',
      component: 'RadioGroup',
      label: '字段7',
      colProps: { span: 8 },
      componentProps: {
        options: [
          { label: '选项1', value: '1' },
          { label: '选项2', value: '2' },
        ],
      },
    },
    {
      field: 'field8',
      component: 'ApiCascader',
      label: '联动',
      colProps: { span: 8 },
      componentProps: {
        api: areaRecord,
        apiParamKey: 'parentCode',
        dataField: 'data',
        labelField: 'name',
        valueField: 'code',
        initFetchParams: {
          parentCode: '',
        },
        isLeaf: (record) => {
          return !(record.levelType < 3);
        },
      },
    },
    {
      field: 'field9',
      component: 'ApiCascader',
      label: '联动回显',
      colProps: { span: 8 },
      componentProps: {
        api: areaRecord,
        apiParamKey: 'parentCode',
        dataField: 'data',
        labelField: 'name',
        valueField: 'code',
        initFetchParams: {
          parentCode: '',
        },
        isLeaf: (record) => {
          return !(record.levelType < 3);
        },
      },
    },
  ];
  const formSchemas: FormSchema[] = [
    {
      field: 'd1',
      component: 'Divider',
      label: '基础属性',
      colProps: { span: 24 },
      componentProps: {
        orientation: 'center',
      },
    },
    {
      field: 'name',
      defaultValue: 'useForm',
      component: 'Input',
      label: 'name',
      colProps: { span: 24 },
    },
    {
      field: 'layout',
      defaultValue: 'horizontal',
      component: 'RadioButtonGroup',
      label: 'layout',
      colProps: { span: 24 },
      componentProps: {
        options: layoutList,
      },
    },
    {
      field: 'labelAlign',
      defaultValue: 'right',
      component: 'RadioButtonGroup',
      label: 'labelAlign',
      colProps: { span: 24 },
      componentProps: {
        options: labelAlignList,
      },
    },
    {
      field: 'labelWidth',
      defaultValue: 120,
      component: 'InputNumber',
      label: 'labelWidth',
      colProps: { span: 24 },
    },
    {
      field: 'size',
      defaultValue: 'default',
      component: 'Select',
      label: 'size',
      colProps: { span: 24 },
      componentProps: {
        options: sizeList,
      },
    },
    {
      field: 'colon',
      defaultValue: false,
      component: 'Switch',
      label: 'colon',
      colProps: { span: 24 },
    },
    {
      field: 'disabled',
      defaultValue: false,
      component: 'Switch',
      label: 'disabled',
      colProps: { span: 24 },
    },
    {
      field: 'compact',
      defaultValue: false,
      component: 'Switch',
      label: 'compact',
      colProps: { span: 24 },
    },
    {
      field: 'autoSetPlaceHolder',
      defaultValue: true,
      component: 'Switch',
      label: 'autoSetPlaceHolder',
      colProps: { span: 24 },
    },
    {
      field: 'autoSubmitOnEnter',
      defaultValue: false,
      component: 'Switch',
      label: 'autoSubmitOnEnter',
      colProps: { span: 24 },
    },
    {
      field: 'showAdvancedButton',
      defaultValue: false,
      component: 'Switch',
      label: 'showAdvancedButton',
      colProps: { span: 24 },
    },

    {
      field: 'd2',
      component: 'Divider',
      label: '网格布局(rowProps)',
      colProps: { span: 24 },
      componentProps: {
        orientation: 'center',
      },
    },
    {
      field: 'rowProps.gutter.0',
      component: 'InputNumber',
      defaultValue: 0,
      label: 'Horizontal Gutter',
      colProps: { span: 24 },
      componentProps: {
        addonAfter: 'px',
      },
    },
    {
      field: 'rowProps.gutter.1',
      component: 'InputNumber',
      defaultValue: 0,
      label: 'Vertical Gutter',
      colProps: { span: 24 },
      componentProps: {
        addonAfter: 'px',
      },
    },
    {
      field: 'rowProps.align',
      defaultValue: 'top',
      component: 'Select',
      label: 'align',
      colProps: { span: 24 },
      componentProps: {
        options: [
          { value: 'stretch', label: 'stretch' },
          { value: 'bottom', label: 'bottom' },
          { value: 'top', label: 'top' },
          { value: 'middle', label: 'middle' },
        ],
      },
    },
    {
      field: 'rowProps.justify',
      defaultValue: 'start',
      component: 'Select',
      label: 'justify',
      colProps: { span: 24 },
      componentProps: {
        options: [
          { value: 'space-around', label: 'space-around' },
          { value: 'space-between', label: 'space-between' },
          { value: 'center', label: 'center' },
          { value: 'end', label: 'end' },
          { value: 'start', label: 'start' },
        ],
      },
    },
    {
      field: 'wrap',
      defaultValue: true,
      component: 'Switch',
      label: 'wrap',
      colProps: { span: 24 },
    },

    {
      field: 'd3',
      component: 'Divider',
      label: '操作按钮',
      colProps: { span: 24 },
      componentProps: {
        orientation: 'center',
      },
    },
    {
      field: 'showActionButtonGroup',
      defaultValue: true,
      component: 'Switch',
      label: 'showActionButtonGroup',
      colProps: { span: 24 },
      componentProps: ({ formActionType }) => {
        return {
          onChange: async (val: boolean) => {
            formActionType.updateSchema([
              { field: 'showResetButton', componentProps: { disabled: !val } },
              {
                field: 'showSubmitButton',
                componentProps: { disabled: !val },
              },
              {
                field: 'actionColOptions.span',
                componentProps: { disabled: !val },
              },
            ]);
          },
        };
      },
    },
    {
      field: 'showResetButton',
      defaultValue: true,
      component: 'Switch',
      label: 'showResetButton',
      colProps: { span: 24 },
    },
    {
      field: 'showSubmitButton',
      defaultValue: true,
      component: 'Switch',
      label: 'showSubmitButton',
      colProps: { span: 24 },
    },

    {
      field: 'd4',
      component: 'Divider',
      label: '操作按钮网格布局(actionColOptions)',
      colProps: { span: 24 },
      componentProps: {
        orientation: 'center',
      },
    },
    {
      field: 'actionColOptions.span',
      component: 'Slider',
      defaultValue: 24,
      label: 'span',
      colProps: { span: 24 },
      componentProps: { min: 0, max: 24 },
    },
    {
      field: 'd5',
      component: 'Divider',
      label: '其他事件',
      colProps: { span: 24 },
      componentProps: {
        orientation: 'center',
      },
    },
    {
      field: 'other',
      component: 'Input',
      label: '',
      colProps: { span: 24 },
      colSlot: 'other',
    },
  ];

  const open = ref<boolean>(false);
  const settingFormRef = ref();
  const [registerSetting] = useForm({
    size: 'small',
    schemas: formSchemas,
    compact: true,
    actionColOptions: { span: 24 },
    showActionButtonGroup: false,
  });
  const resetSettings = async () => {
    setProps({ resetButtonOptions: { disabled: false, text: '重置' } });
    setProps({ submitButtonOptions: { disabled: false, loading: false } });
    await setFieldsValue({ field9: [] });
    await settingFormRef.value?.resetFields();
  };
  const handleSubmitSetting = async (values: Recordable) => {
    console.log(values);
    await setProps(values);
    open.value = false;
  };
  const [register, { setProps, setFieldsValue, updateSchema }] = useForm({
    labelWidth: 120,
    schemas,
    actionColOptions: { span: 24 },
    fieldMapToTime: [['fieldTime', ['startTime', 'endTime'], 'YYYY-MM']],
  });
  async function handleLoad() {
    const promiseFn = function () {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve({
            field9: ['430000', '430100', '430102'],
            province: '湖南省',
            city: '长沙市',
            district: '岳麓区',
          });
        }, 1000);
      });
    };
    const item = await promiseFn();
    const { field9, province, city, district } = item as any;
    await updateSchema({
      field: 'field9',
      componentProps: {
        displayRenderArray: [province, city, district],
      },
    });
    await setFieldsValue({ field9 });
    open.value = false;
  }
  const showDrawer = () => {
    open.value = true;
  };
  const onSettings = () => {
    settingFormRef.value?.submit();
  };
  const withClose = (formProps: Partial<FormProps>) => {
    setProps(formProps);
    open.value = false;
  };

  const handleSubmit = (values: Recordable) => {
    console.log(values);
  };
</script>
```

</details>
</CodePreview>

### RefForm

<CodePreview src="/doc-comp/form/refForm" :height="450">
<details>
<summary>展开查看</summary>

```vue
<template>
  <PageWrapper title="Ref操作示例">
    <div class="mb-4">
      <a-button class="mr-2" @click="setProps({ labelWidth: 150 })"> 更改labelWidth </a-button>
      <a-button class="mr-2" @click="setProps({ labelWidth: 120 })"> 还原labelWidth </a-button>
      <a-button class="mr-2" @click="setProps({ size: 'large' })"> 更改Size </a-button>
      <a-button class="mr-2" @click="setProps({ size: 'default' })"> 还原Size </a-button>
      <a-button class="mr-2" @click="setProps({ disabled: true })"> 禁用表单 </a-button>
      <a-button class="mr-2" @click="setProps({ disabled: false })"> 解除禁用 </a-button>
      <a-button class="mr-2" @click="setProps({ compact: true })"> 紧凑表单 </a-button>
      <a-button class="mr-2" @click="setProps({ compact: false })"> 还原正常间距 </a-button>
      <a-button class="mr-2" @click="setProps({ actionColOptions: { span: 8 } })">
        操作按钮位置
      </a-button>
    </div>
    <div class="mb-4">
      <a-button class="mr-2" @click="setProps({ showActionButtonGroup: false })">
        隐藏操作按钮
      </a-button>
      <a-button class="mr-2" @click="setProps({ showActionButtonGroup: true })">
        显示操作按钮
      </a-button>
      <a-button class="mr-2" @click="setProps({ showResetButton: false })"> 隐藏重置按钮 </a-button>
      <a-button class="mr-2" @click="setProps({ showResetButton: true })"> 显示重置按钮 </a-button>
      <a-button class="mr-2" @click="setProps({ showSubmitButton: false })">
        隐藏查询按钮
      </a-button>
      <a-button class="mr-2" @click="setProps({ showSubmitButton: true })"> 显示查询按钮 </a-button>
      <a-button
        class="mr-2"
        @click="
          setProps({
            resetButtonOptions: {
              disabled: true,
              text: '重置New',
            },
          })
        "
      >
        修改重置按钮
      </a-button>
      <a-button
        class="mr-2"
        @click="
          setProps({
            submitButtonOptions: {
              disabled: true,
              loading: true,
            },
          })
        "
      >
        修改查询按钮
      </a-button>
    </div>
    <CollapseContainer title="使用ref调用表单内部函数示例">
      <BasicForm
        ref="formElRef"
        :schemas="schemas"
        :labelWidth="100"
        :actionColOptions="{ span: 24 }"
        @submit="handleSubmit"
      />
    </CollapseContainer>
  </PageWrapper>
</template>
<script lang="ts" setup>
  import {
    BasicForm,
    FormActionType,
    FormProps,
    FormSchema,
    PageWrapper,
    useMessage,
  } from '@eciol/ant-ui';

  const schemas: FormSchema[] = [
    {
      field: 'field1',
      component: 'Input',
      label: '字段1',
      colProps: {
        span: 8,
      },
      componentProps: {
        placeholder: '自定义placeholder',
        onChange: (e: any) => {
          console.log(e);
        },
      },
    },
    {
      field: 'field2',
      component: 'Input',
      label: '字段2',
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field3',
      component: 'DatePicker',
      label: '字段3',
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field4',
      component: 'Select',
      label: '字段4',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
            key: '1',
          },
          {
            label: '选项2',
            value: '2',
            key: '2',
          },
        ],
      },
    },
    {
      field: 'field5',
      component: 'CheckboxGroup',
      label: '字段5',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
          },
          {
            label: '选项2',
            value: '2',
          },
        ],
      },
    },
    {
      field: 'field7',
      component: 'RadioGroup',
      label: '字段7',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
          },
          {
            label: '选项2',
            value: '2',
          },
        ],
      },
    },
  ];

  const formElRef = ref<Nullable<FormActionType>>(null);
  const { createMessage } = useMessage();
  const handleSubmit = (values: any) => {
    createMessage.success('click search,values:' + JSON.stringify(values));
  };
  function setProps(props: FormProps) {
    const formEl = formElRef.value;
    if (!formEl) return;
    formEl.setProps(props);
  }
</script>
```

</details>
</CodePreview>

### 可收缩表单

<CodePreview src="/doc-comp/form/advancedForm" :height="500">
<details>
<summary>展开查看</summary>

```vue
<template>
  <PageWrapper title="可折叠表单示例">
    <CollapseContainer title="基础收缩示例">
      <BasicForm @register="register" />
    </CollapseContainer>

    <CollapseContainer title="超过3行自动收起，折叠时保留2行" class="mt-4">
      <BasicForm @register="register1" />
    </CollapseContainer>
  </PageWrapper>
</template>
<script lang="ts" setup>
  import { BasicForm, FormSchema, PageWrapper, useForm } from '@eciol/ant-ui';
  import { CollapseContainer } from '@eciol/share-ui';

  const getSchemas = (): FormSchema[] => {
    return [
      {
        field: 'field1',
        component: 'Input',
        label: '字段1',
        colProps: {
          span: 8,
        },
        componentProps: {
          placeholder: '自定义placeholder',
          onChange: (e: any) => {
            console.log(e);
          },
        },
      },
      {
        field: 'field2',
        component: 'Input',
        label: '字段2',
        colProps: {
          span: 8,
        },
      },
      {
        field: 'field3',
        component: 'DatePicker',
        label: '字段3',
        colProps: {
          span: 8,
        },
      },
      {
        field: 'field4',
        component: 'Select',
        label: '字段4',
        colProps: {
          span: 8,
        },
        componentProps: {
          options: [
            {
              label: '选项1',
              value: '1',
              key: '1',
            },
            {
              label: '选项2',
              value: '2',
              key: '2',
            },
          ],
        },
      },
      {
        field: 'field5',
        component: 'CheckboxGroup',
        label: '字段5',
        colProps: {
          span: 8,
        },
        componentProps: {
          options: [
            {
              label: '选项1',
              value: '1',
            },
            {
              label: '选项2',
              value: '2',
            },
          ],
        },
      },
      // {
      //   field: 'field7',
      //   component: 'RadioGroup',
      //   label: '字段7',
      //   colProps: {
      //     span: 8,
      //   },
      //   componentProps: {
      //     options: [
      //       {
      //         label: '选项1',
      //         value: '1',
      //       },
      //       {
      //         label: '选项2',
      //         value: '2',
      //       },
      //     ],
      //   },
      // },
    ];
  };

  function getAppendSchemas(): FormSchema[] {
    return [
      {
        field: 'field10',
        component: 'Input',
        label: '字段10',
        colProps: {
          span: 8,
        },
      },
      {
        field: 'field11',
        component: 'Input',
        label: '字段11',
        colProps: {
          span: 8,
        },
      },
      {
        field: 'field12',
        component: 'Input',
        label: '字段12',
        colProps: {
          span: 8,
        },
      },
      {
        field: 'field13',
        component: 'Input',
        label: '字段13',
        colProps: {
          span: 8,
        },
      },
    ];
  }
  const [register] = useForm({
    labelWidth: 120,
    schemas: getSchemas(),
    actionColOptions: {
      span: 24,
    },
    compact: true,
    showAdvancedButton: true,
  });
  const extraSchemas: FormSchema[] = [];
  for (let i = 14; i < 30; i++) {
    extraSchemas.push({
      field: 'field' + i,
      component: 'Input',
      label: '字段' + i,
      colProps: {
        span: 8,
      },
    });
  }
  const [register1] = useForm({
    labelWidth: 120,
    schemas: [
      ...getSchemas(),
      ...getAppendSchemas(),
      { field: '', component: 'Divider', label: '更多字段' },
      ...extraSchemas,
    ],
    actionColOptions: {
      span: 24,
    },
    compact: true,
    showAdvancedButton: true,
    alwaysShowLines: 2,
  });
</script>
```

</details>
</CodePreview>

### 表单验证

<CodePreview src="/doc-comp/form/ruleForm" :height="500">
<details>
<summary>展开查看</summary>

```vue
<template>
  <PageWrapper title="表单校验示例">
    <div class="mb-4">
      <a-button class="mr-2" @click="validateForm"> 手动校验表单 </a-button>
      <a-button class="mr-2" @click="resetValidate"> 清空校验信息 </a-button>
      <a-button class="mr-2" @click="getFormValues"> 获取表单值 </a-button>
      <a-button class="mr-2" @click="setFormValues"> 设置表单值 </a-button>
      <a-button class="mr-2" @click="resetFields"> 重置 </a-button>
    </div>
    <CollapseContainer title="表单校验">
      <BasicForm @register="register" @submit="handleSubmit" />
    </CollapseContainer>
  </PageWrapper>
</template>
<script lang="ts" setup>
  import { BasicForm, FormSchema, PageWrapper, useForm, useMessage } from '@eciol/ant-ui';

  import { isAccountExist } from '@/api/demo/system';

  const schemas: FormSchema[] = [
    {
      field: 'field1',
      component: 'Input',
      label: '字段1',
      colProps: {
        span: 8,
      },
      required: true,
    },
    {
      field: 'field2',
      component: 'Input',
      label: '字段2',
      colProps: {
        span: 8,
      },
      required: true,
    },
    {
      field: 'id',
      label: 'id',
      required: true,
      defaultValue: 0,
      component: 'InputNumber',
      show: false,
    },
    {
      field: 'field3',
      component: 'DatePicker',
      label: '字段3',
      colProps: {
        span: 8,
      },
      required: true,
    },
    {
      field: 'field33',
      component: 'DatePicker',
      label: '字段33',
      colProps: {
        span: 8,
      },
      componentProps: {
        valueFormat: 'YYYY-MM-DD',
      },
      rules: [{ required: true, type: 'string' }],
    },
    {
      field: 'field44',
      component: 'InputCountDown',
      label: '验证码',
      colProps: {
        span: 8,
      },
      required: true,
    },
    {
      field: 'field4',
      component: 'Select',
      label: '字段4',
      colProps: {
        span: 8,
      },
      componentProps: {
        mode: 'multiple',
        options: [
          {
            label: '选项1',
            value: '1',
            key: '1',
          },
          {
            label: '选项2',
            value: '2',
            key: '2',
          },
        ],
        onChange: (value) => {
          console.log(value, '123');
        },
      },
      rules: [
        {
          required: true,
          message: '请输入aa',
          type: 'array',
        },
      ],
    },
    {
      field: 'field441',
      component: 'Input',
      label: '自定义校验',
      colProps: {
        span: 8,
      },
      rules: [
        {
          required: true,
          // @ts-ignore
          validator: async (rule, value) => {
            if (!value) {
              /* eslint-disable-next-line */
              return Promise.reject('值不能为空');
            }
            if (value === '1') {
              /* eslint-disable-next-line */
              return Promise.reject('值不能为1');
            }
            return Promise.resolve();
          },
          trigger: 'change',
        },
      ],
    },
    {
      field: 'field5',
      component: 'CheckboxGroup',
      label: '字段5',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
          },
          {
            label: '选项2',
            value: '2',
          },
        ],
      },
      rules: [{ required: true }],
    },
    {
      field: 'field7',
      component: 'RadioGroup',
      label: '字段7',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
          },
          {
            label: '选项2',
            value: '2',
          },
        ],
      },
      rules: [{ required: true, message: '覆盖默认生成的校验信息' }],
    },
    {
      field: 'field8',
      component: 'Input',
      label: '后端异步验证',
      colProps: {
        span: 8,
      },
      helpMessage: ['本字段演示异步验证', '本地规则：必须填写', '后端规则：不能包含admin'],
      rules: [
        {
          required: true,
          message: '请输入数据',
        },
        {
          validator(_, value) {
            return new Promise((resolve, reject) => {
              isAccountExist(value)
                .then(() => resolve())
                .catch((err) => {
                  reject(err.message || '验证失败');
                });
            });
          },
        },
      ],
    },
  ];

  const { createMessage } = useMessage();
  const [register, { validateFields, clearValidate, getFieldsValue, resetFields, setFieldsValue }] =
    useForm({
      labelWidth: 120,
      schemas,
      actionColOptions: {
        span: 24,
      },
    });
  async function validateForm() {
    try {
      const res = await validateFields();
      console.log('passing', res);
    } catch (error) {
      console.log('not passing', error);
    }
  }
  async function resetValidate() {
    clearValidate();
  }
  function getFormValues() {
    const values = getFieldsValue();
    createMessage.success('values:' + JSON.stringify(values));
  }
  function setFormValues() {
    setFieldsValue({
      field1: 1111,
      field4: ['1'],
      field5: ['1'],
      field7: '1',
      field33: '2020-12-12',
      field3: '2020-12-12',
    });
  }

  const handleSubmit = (values: any) => {
    createMessage.success('click search,values:' + JSON.stringify(values));
  };
</script>
```

</details>
</CodePreview>

### 动态表单

<CodePreview src="/doc-comp/form/dynamicForm" :height="700">
<details>
<summary>展开查看</summary>

```vue
<template>
  <PageWrapper title="动态表单示例">
    <div class="mb-4">
      <a-button class="mr-2" @click="changeLabel3"> 更改字段3label </a-button>
      <a-button class="mr-2" @click="changeLabel34"> 同时更改字段3,4label </a-button>
      <a-button class="mr-2" @click="appendField"> 往字段3后面插入字段10 </a-button>
      <a-button class="mr-2" @click="deleteField"> 删除字段11 </a-button>
    </div>
    <CollapseContainer title="动态表单示例,动态根据表单内其他值改变">
      <BasicForm @register="register" />
    </CollapseContainer>

    <CollapseContainer class="mt-5" title="componentProps动态改变">
      <BasicForm @register="register1" />
    </CollapseContainer>
  </PageWrapper>
</template>
<script lang="ts" setup>
  import { BasicForm, FormSchema, PageWrapper, useForm } from '@eciol/ant-ui';
  import { CollapseContainer } from '@eciol/share-ui';

  const schemas: FormSchema[] = [
    {
      field: 'field1',
      component: 'Input',
      label: '字段1',
      colProps: {
        span: 8,
      },
      show: ({ values }) => {
        return !!values.field5;
      },
    },
    {
      field: 'field2',
      component: 'Input',
      label: '字段2',
      colProps: {
        span: 8,
      },
      ifShow: ({ values }) => {
        return !!values.field6;
      },
    },
    {
      field: 'field3',
      component: 'DatePicker',
      label: '字段3',
      colProps: {
        span: 8,
      },
      dynamicDisabled: ({ values }) => {
        return !!values.field7;
      },
    },
    {
      field: 'field4',
      component: 'Select',
      label: '字段4',
      colProps: {
        span: 8,
      },
      dynamicRules: ({ values }) => {
        return values.field8 ? [{ required: true, message: '字段4必填' }] : [];
      },
      componentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
            key: '1',
          },
          {
            label: '选项2',
            value: '2',
            key: '2',
          },
        ],
      },
    },
    {
      field: 'field11',
      component: 'DatePicker',
      label: '字段11',
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field5',
      component: 'Switch',
      label: '是否显示字段1(css控制)',
      colProps: {
        span: 8,
      },
      labelWidth: 200,
    },
    {
      field: 'field6',
      component: 'Switch',
      label: '是否显示字段2(dom控制)',
      colProps: {
        span: 8,
      },
      labelWidth: 200,
    },
    {
      field: 'field7',
      component: 'Switch',
      label: '是否禁用字段3',
      colProps: {
        span: 8,
      },
      labelWidth: 200,
    },
    {
      field: 'field8',
      component: 'Switch',
      label: '字段4是否必填',
      colProps: {
        span: 8,
      },
      labelWidth: 200,
    },
  ];

  const schemas1: FormSchema[] = [
    {
      field: 'f1',
      component: 'Input',
      label: 'F1',
      colProps: {
        span: 12,
      },
      labelWidth: 200,
      componentProps: ({ formModel }) => {
        return {
          placeholder: '同步f2的值为f1',
          onChange: (e: ChangeEvent) => {
            formModel.f2 = e.target.value;
          },
        };
      },
    },
    {
      field: 'f2',
      component: 'Input',
      label: 'F2',
      colProps: {
        span: 12,
      },
      labelWidth: 200,
      componentProps: { disabled: true },
    },
    {
      field: 'f3',
      component: 'Input',
      label: 'F3',
      colProps: {
        span: 12,
      },
      labelWidth: 200,
      componentProps: ({ formActionType }) => {
        return {
          placeholder: '值改变时执行查询,查看控制台',
          onChange: async () => {
            const { validate } = formActionType;
            // tableAction只适用于在表格内开启表单的例子
            // const { reload } = tableAction;
            const res = await validate();
            console.log(res);
          },
        };
      },
    },
  ];

  const [register, { setProps, updateSchema, appendSchemaByField, removeSchemaByField }] = useForm({
    labelWidth: 120,
    schemas,
    actionColOptions: {
      span: 24,
    },
  });
  const [register1] = useForm({
    labelWidth: 120,
    schemas: schemas1,
    actionColOptions: {
      span: 24,
    },
  });
  function changeLabel3() {
    updateSchema({
      field: 'field3',
      label: '字段3 New',
    });
  }
  function changeLabel34() {
    updateSchema([
      {
        field: 'field3',
        label: '字段3 New++',
      },
      {
        field: 'field4',
        label: '字段4 New++',
      },
    ]);
  }

  function appendField() {
    appendSchemaByField(
      {
        field: 'field10',
        label: '字段10',
        component: 'Input',
        colProps: {
          span: 8,
        },
      },
      'field3'
    );
  }
  function deleteField() {
    removeSchemaByField('field11');
  }
</script>
```

</details>
</CodePreview>

### 自定义组件

<CodePreview src="/doc-comp/form/customerForm" :height="400">
<details>
<summary>展开查看</summary>

```vue
<template>
  <PageWrapper title="自定义组件示例">
    <CollapseContainer title="自定义表单">
      <BasicForm class="local_form" @register="register" @submit="handleSubmit">
        <template #f3="{ model, field, disabled }">
          <a-input v-model:value="model[field]" :disabled="disabled" placeholder="自定义slot" />
        </template>
        <template #colSlot_field5="{ model, field, disabled }">
          <AFormItem :name="field" label="自定义colSlot" :rules="[{ required: true }]">
            <a-input
              v-model:value="model[field]"
              :disabled="disabled"
              placeholder="自定义colSlot"
            />
          </AFormItem>
        </template>
      </BasicForm>
    </CollapseContainer>
  </PageWrapper>
</template>
<script lang="tsx" setup>
  import { BasicForm, FormSchema, PageWrapper, useForm, useMessage } from '@eciol/ant-ui';
  import { CollapseContainer } from '@eciol/share-ui';
  import { FormItem, Input, Select } from 'ant-design-vue';
  import { h } from 'vue';

  const custom_typeKey2typeValueRules = (model) => {
    return [
      {
        required: true,
        validator: (rule, value, callback) => {
          if (!model.typeKey) return callback('请选择类型');
          if (!model.typeValue) return callback('请输入数据');
          callback();
        },
      },
    ];
  };
  const schemas: FormSchema[] = [
    {
      field: 'field1',
      component: 'Input',
      label: 'render方式',
      colProps: {
        span: 8,
      },
      dynamicDisabled: ({ values }) => {
        return !!values.field_disabled;
      },
      rules: [{ required: true }],
      render: ({ model, field }, { disabled }) => {
        return h(Input, {
          placeholder: '请输入',
          value: model[field],
          onChange: (e) => {
            model[field] = e.target.value;
          },
          disabled,
        });
      },
    },
    {
      field: 'field2',
      component: 'Input',
      label: 'render组件slot',
      colProps: {
        span: 8,
      },
      dynamicDisabled: ({ values }) => {
        return !!values.field_disabled;
      },
      rules: [{ required: true }],
      renderComponentContent: (_, { disabled }) => {
        return {
          suffix: () => (disabled ? 'suffix_disabled' : 'suffix_default'),
        };
      },
    },
    {
      field: 'field3',
      component: 'Input',
      label: '自定义Slot',
      slot: 'f3',
      colProps: {
        span: 8,
      },
      dynamicDisabled: ({ values }) => {
        return !!values.field_disabled;
      },
      rules: [{ required: true }],
    },
    {
      field: 'field4',
      component: 'Input',
      label: 'renderColContent渲染',
      /**!!!renderColContent 没有FormItem 包裹, 若想要 Form 提交需要带上数据须 <FormItem name={}></FormItem> 包裹： 示例如下*/
      renderColContent({ model, field }, { disabled }) {
        return (
          <FormItem name="field4" label="renderColContent渲染" rules={[{ required: true }]}>
            <Input placeholder="请输入" v-model:value={model[field]} disabled={disabled}></Input>
          </FormItem>
        );
      },
      colProps: {
        span: 8,
      },
      dynamicDisabled: ({ values }) => {
        return !!values.field_disabled;
      },
    },
    {
      field: 'field5',
      component: 'Input',
      label: '自定义colSlot',
      /**!!!renderColContent 没有FormItem 包裹, 若想要 Form 提交需要带上数据须 <FormItem name={}></FormItem> 包裹： 示例如下*/
      colSlot: 'colSlot_field5',
      colProps: {
        span: 8,
      },
      dynamicDisabled: ({ values }) => {
        return !!values.field_disabled;
      },
    },
    // 复合field 场景 自定义表单控件 一个控件包含多个表单录入 示例: 选择+输入
    {
      required: true,
      field: 'typeKey2',
      defaultValue: '测试类型',
      fields: ['typeValue2'],
      defaultValueObj: { typeValue2: '默认测试_文字' },
      component: 'Input',
      label: '复合field render',
      render({ model, field }, { disabled }) {
        return (
          <Input.Group compact>
            <Select
              disabled={disabled}
              style="width: 120px"
              allowClear
              v-model:value={model[field]}
            >
              <Select.Option value="测试类型">测试类型</Select.Option>
              <Select.Option value="测试名称">测试名称</Select.Option>
            </Select>
            <FormItem name="typeValue2" class="local_typeValue" rules={[{ required: true }]}>
              <Input placeholder="请输入" v-model:value={model['typeValue2']} disabled={disabled} />
            </FormItem>
          </Input.Group>
        );
      },
      colProps: {
        span: 8,
      },
      dynamicDisabled: ({ values }) => {
        return !!values.field_disabled;
      },
    },
    // 复合field 场景 自定义表单控件 一个控件包含多个表单录入 示例: 选择+输入
    {
      field: 'typeKey',
      defaultValue: '公司名称',
      fields: ['typeValue'],
      defaultValueObj: { typeValue: '默认文字' },
      component: 'Input',
      label: 'renderColContent渲染',
      /**!!!renderColContent 没有FormItem 包裹, 若想要 Form 提交需要带上数据须 <FormItem name={}></FormItem> 包裹： 示例如下*/
      renderColContent({ model, field }, { disabled }) {
        return (
          <FormItem
            name="typeKey"
            label="复合field renderColContent"
            rules={custom_typeKey2typeValueRules(model)}
          >
            <Input.Group compact>
              <Select
                allowClear
                disabled={disabled}
                style="width: 120px"
                v-model:value={model[field]}
              >
                <Select.Option value="公司名称">公司名称</Select.Option>
                <Select.Option value="产品名称">产品名称</Select.Option>
              </Select>
              <Input
                style="width: calc(100% - 120px); margin-left: -1px;"
                placeholder="请输入"
                v-model:value={model['typeValue']}
                disabled={disabled}
              />
            </Input.Group>
          </FormItem>
        );
      },
      colProps: {
        span: 16,
      },
      dynamicDisabled: ({ values }) => {
        return !!values.field_disabled;
      },
    },
    {
      field: 'field_disabled',
      component: 'Switch',
      label: '是否禁用 编辑字段',
      colProps: {
        span: 8,
      },
      labelWidth: 200,
    },
  ];

  const { createMessage } = useMessage();
  const [register, { setProps }] = useForm({
    labelWidth: 120,
    schemas,
    actionColOptions: {
      span: 24,
    },
  });
  const handleSubmit = (values: any) => {
    console.log('submit values', values);
    createMessage.success('click search,values:' + JSON.stringify(values));
  };
</script>
<style lang="less" scoped>
  :deep(.local_form) .local_typeValue {
    width: calc(100% - 120px);
    margin-bottom: 0;
    margin-left: -1px;
    border-right: 0;

    .ant-input {
      border-radius: 0 6px 6px 0;
    }
  }
</style>
```

</details>
</CodePreview>

### 表单增删示例

<CodePreview src="/doc-comp/form/appendForm" :height="400">
<details>
<summary>展开查看</summary>

```vue
<template>
  <PageWrapper title="表单增删示例">
    <CollapseContainer title="表单增删">
      <BasicForm @register="register" @submit="handleSubmit">
        <template #add="{ field }">
          <AButton v-if="Number(field) === 0" @click="add">+</AButton>
          <AButton v-if="Number(field) === 0" class="ml-2" @click="batchAdd">
            批量添加表单配置
          </AButton>
          <AButton v-if="field > 0" @click="del(field)">-</AButton>
        </template>
      </BasicForm>
    </CollapseContainer>
  </PageWrapper>
</template>
<script lang="ts" setup>
  import { BasicForm, PageWrapper, useForm } from '@eciol/ant-ui';
  import { CollapseContainer } from '@eciol/share-ui';

  const [register, { appendSchemaByField, removeSchemaByField, validate }] = useForm({
    schemas: [
      {
        field: 'field0a',
        component: 'Input',
        label: '字段0',
        required: true,
      },
      {
        field: 'field0b',
        component: 'Input',
        label: '字段0',
        required: true,
      },
      {
        field: '0',
        component: 'Input',
        label: ' ',
        slot: 'add',
      },
    ],
    labelWidth: 100,
    actionColOptions: { span: 24 },
    baseColProps: { span: 8 },
  });

  async function handleSubmit() {
    try {
      const data = await validate();
      console.log(data);
    } catch (e) {
      console.log(e);
    }
  }

  const n = ref(1);

  function add() {
    appendSchemaByField(
      {
        field: `field${n.value}a`,
        component: 'Input',
        label: '字段' + n.value,
        required: true,
      },
      ''
    );
    appendSchemaByField(
      {
        field: `field${n.value}b`,
        component: 'Input',
        label: '字段' + n.value,
        required: true,
      },
      ''
    );

    appendSchemaByField(
      {
        field: `${n.value}`,
        component: 'Input',
        label: ' ',
        slot: 'add',
      },
      ''
    );
    n.value++;
  }
  /**
   * @description: 批量添加
   */
  function batchAdd() {
    appendSchemaByField(
      [
        {
          field: `field${n.value}a`,
          component: 'Input',
          label: '字段' + n.value,
          required: true,
        },
        {
          field: `field${n.value}b`,
          component: 'Input',
          label: '字段' + n.value,
          required: true,
        },
        {
          field: `${n.value}`,
          component: 'Input',
          label: ' ',
          slot: 'add',
        },
      ],
      ''
    );
    n.value++;
  }

  function del(field) {
    removeSchemaByField([`field${field}a`, `field${field}b`, `${field}`]);
    n.value--;
  }
</script>
```

</details>
</CodePreview>

### 标签页+多级 field 表单

<CodePreview src="/doc-comp/form/tabsForm" :height="800">
<details>
<summary>展开查看</summary>

```vue
<template>
  <PageWrapper v-loading="loading" title="标签页+多级field表单">
    <div class="mb-4">
      <a-button class="mr-2" @click="handleReset"> 重置表单 </a-button>
      <a-button class="mr-2" @click="handleSetValues"> 设置默认值 </a-button>
      <a-button class="mr-2" type="primary" @click="handleSubmit"> 提交表单 </a-button>
    </div>
    <CollapseContainer title="标签页+多级field表单">
      <ATabs v-model:activeKey="activeKey">
        <ATabPane
          v-for="item in tabsFormSchema"
          :key="item.key"
          v-bind="omit(item, ['Form', 'key'])"
        >
          <BasicForm @register="item.Form[0]" />
        </ATabPane>
      </ATabs>
    </CollapseContainer>
  </PageWrapper>
</template>

<script lang="ts" setup>
  import {
    BasicForm,
    FormProps,
    FormSchema,
    PageWrapper,
    useForm,
    UseFormReturnType,
    useMessage,
  } from '@eciol/ant-ui';
  import { CollapseContainer } from '@eciol/share-ui';
  import { deepMerge } from '@eciol/shared';
  import { omit } from 'lodash-es';

  defineOptions({
    name: 'TabsFormDemo',
  });

  interface TabsFormType {
    key: string;
    tab: string;
    forceRender?: boolean;
    Form: UseFormReturnType;
  }

  const { createMessage } = useMessage();
  const activeKey = ref('tabs2');
  const loading = ref(false);
  const tabsFormSchema: TabsFormType[] = [];

  // 公共属性
  const baseFormConfig: Partial<FormProps> = {
    showActionButtonGroup: false,
    labelWidth: 100,
  };

  // 为每个字段模拟默认值, { tabs1: { field1: '', field2: '' }, tabs2: { field1: '' }, ... }
  const mockDefaultValue: Recordable = {};

  // 模拟5个标签页
  for (let i = 1; i <= 5; ++i) {
    const tabsKey = `tabs${i}`;

    // 每个标签页8个字段
    const schemas: FormSchema[] = [];
    const row: Recordable = {};

    for (let j = 1; j <= 8; ++j) {
      schemas.push({
        field: `${tabsKey}.field${j}`,
        label: `${tabsKey}-field${j}`,
        component: 'Input',
        colProps: { span: 24 },
      });
      row[`field${j}`] = `field: ${tabsKey}.field${j}, default value`;
    }

    mockDefaultValue[tabsKey] = row;

    tabsFormSchema.push({
      key: tabsKey,
      tab: tabsKey,
      forceRender: true,
      Form: useForm(Object.assign({ schemas }, baseFormConfig) as FormProps),
    });
  }

  async function handleReset() {
    for (const item of tabsFormSchema) {
      const { resetFields } = item.Form[1];
      await resetFields();
    }
  }

  async function handleSubmit() {
    let lastKey = '';
    loading.value = true;
    try {
      const values: Recordable = {};
      for (const item of tabsFormSchema) {
        lastKey = item.key;
        const { validate, getFieldsValue } = item.Form[1];
        await validate();
        // 表单已支持多级key
        deepMerge(values, getFieldsValue());
      }

      console.log('submit values: ', values);
      createMessage.success('提交成功！请打开控制台查看');
    } catch (e) {
      // 验证失败或出错，切换到对应标签页
      activeKey.value = lastKey;
      console.log(e);
    } finally {
      loading.value = false;
    }
  }

  async function handleSetValues() {
    console.log('默认值为: ', mockDefaultValue);
    for (const item of tabsFormSchema) {
      const { setFieldsValue } = item.Form[1];
      await setFieldsValue(mockDefaultValue);
    }
  }
</script>

<style scoped></style>
```

</details>
</CodePreview>

## Usage

### useForm 方式

下面是一个使用简单表单的示例，只有一个输入框

```vue
<template>
  <div class="m-4">
    <BasicForm
      :labelWidth="100"
      :schemas="schemas"
      :actionColOptions="{ span: 24 }"
      @submit="handleSubmit"
    />
  </div>
</template>
<script lang="ts" setup>
  import { BasicForm, FormSchema, useMessage } from '@eciol/ant-ui';
  import { CollapseContainer } from '@eciol/share-ui';
  const schemas: FormSchema[] = [
    {
      field: 'field',
      component: 'Input',
      label: '字段1',
      colProps: {
        span: 8,
      },
      defaultValue: '1',
      componentProps: {
        placeholder: '自定义placeholder',
        onChange: (e) => {
          console.log(e);
        },
      },
    },
  ];

  const { createMessage } = useMessage();

  const handleSubmit = (values: any) => {
    createMessage.success('click search,values:' + JSON.stringify(values));
  };
</script>
```

### template 方式

所有可调用函数见下方 `Methods` 说明

```vue
<template>
  <div class="m-4">
    <BasicForm
      :schemas="schemas"
      ref="formElRef"
      :labelWidth="100"
      @submit="handleSubmit"
      :actionColOptions="{ span: 24 }"
    />
  </div>
</template>
<script lang="ts" setup>
  import { BasicForm, FormSchema, FormActionType, FormProps } from '@eciol/ant-ui';
  import { CollapseContainer } from '@eciol/share-ui';
  const schemas: FormSchema[] = [];
  const formElRef = ref<Nullable<FormActionType>>(null);
  const setProps = (props: FormProps) => {
    const formEl = formElRef.value;
    if (!formEl) return;
    formEl.setProps(props);
  };
</script>
```

## useForm

form 组件还提供了 `useForm`，方便调用函数内部方法

### 示例

```vue
<template>
  <BasicForm @register="register" @submit="handleSubmit" />
</template>
<script lang="ts" setup>
  import { BasicForm, FormSchema, useForm, useMessage } from '@eciol/ant-ui';
  import { CollapseContainer } from '@eciol/share-ui';
  const schemas: FormSchema[] = [
    {
      field: 'field1',
      component: 'Input',
      label: '字段1',
      colProps: {
        span: 8,
      },
      componentProps: {
        placeholder: '自定义placeholder',
        onChange: (e: any) => {
          console.log(e);
        },
      },
    },
  ];

  const { createMessage } = useMessage();
  const [register, { setProps }] = useForm({
    labelWidth: 120,
    schemas,
    actionColOptions: {
      span: 24,
    },
  });
  const handleSubmit = (values: any) => {
    createMessage.success('click search,values:' + JSON.stringify(values));
  };
</script>
```

### 参数介绍

```ts
const [register, methods] = useForm(props);
```

**参数 props 内的值可以是 computed 或者 ref 类型**

**register**

register 用于注册 `useForm`，如果需要使用 `useForm` 提供的 api，必须将 register 传入组件的 `onRegister`

```vue
<template>
  <BasicForm @register="register" @submit="handleSubmit" />
</template>
<script lang="ts" setup>
  const [register] = useForm();
</script>
```

`Methods`见下方说明

### Methods

**getFieldsValue**

类型: `() => Recordable;`

说明: 获取表单值

**setFieldsValue**

类型: `<T>(values: T) => Promise<void>`

说明: 设置表单字段值

**resetFields**

类型: `()=> Promise<void>`

说明: 重置表单值

**validateFields**

类型: `(nameList?: NamePath[]) => Promise<any>`

说明: 校验指定表单项

**validate**

类型: `(nameList?: NamePath[]) => Promise<any>`

说明: 校验整个表单

**submit**

类型: `() => Promise<void>`

说明: 提交表单

**scrollToField**

类型: `(name: NamePath, options?: ScrollOptions) => Promise<void>`

说明: 滚动到对应字段位置

**clearValidate**

类型: `(name?: string | string[]) => Promise<void>`

说明: 清空校验

**setProps**

::: tip

设置表单的 props 可以直接在标签上传递，也可以使用 setProps，或者初始化直接写 useForm(props)

:::

类型: `(formProps: Partial<FormProps>) => Promise<void>`

说明: 设置表单 Props

**removeSchemaByFiled**

类型: `(field: string | string[]) => Promise<void>`

说明: 根据 field 删除 Schema

**appendSchemaByField**

类型: `( schema: FormSchema, prefixField: string | undefined, first?: boolean | undefined ) => Promise<void>`

说明: 插入到指定 filed 后面，如果没传指定 field，则插入到最后,当 first = true 时插入到第一个位置

**updateSchema**

类型: `(data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>`

说明: 更新表单的 schema, 只更新函数所传的参数

e.g

```ts
updateSchema({ field: 'filed', componentProps: { disabled: true } });
updateSchema([
  { field: 'filed', componentProps: { disabled: true } },
  { field: 'filed1', componentProps: { disabled: false } },
]);
```

## Props

::: tip 温馨提醒

除以下参数外，官方文档内的 props 也都支持，具体可以参考 [antv form](https://www.antdv.com/components/form-cn/#Form)

:::

| 属性 | 类型 | 默认值 | 可选值 | 说明 | 版本 |
| --- | --- | --- | --- | --- | --- |
| schemas | `Schema[]` | - | - | 表单配置，见下方 `FormSchema` 配置 |  |
| submitOnReset | `boolean` | `false` | - | 重置时是否提交表单 |  |
| labelCol | `Partial<ColEx>` | - | - | 整个表单通用 LabelCol 配置 |  |
| wrapperCol | `Partial<ColEx>` | - | - | 整个表单通用 wrapperCol 配置 |  |
| baseColProps | `Partial<ColEx>` | - | - | 配置所有选子项的 ColProps，不需要逐个配置，子项也可单独配置优先与全局 |  |
| baseRowStyle | `object` | - | - | 配置所有 Row 的 style 样式 |  |
| labelWidth | `number , string` | - | - | 扩展 form 组件，增加 label 宽度，表单内所有组件适用，可以单独在某个项覆盖或者禁用 |  |
| labelAlign | `string` | - | `left`,`right` | label 布局 |  |
| mergeDynamicData | `object` | - | - | 额外传递到子组件的参数 values |  |
| autoFocusFirstItem | `boolean` | `false` | - | 是否聚焦第一个输入框，只在第一个表单项为 input 的时候作用 |  |
| compact | `boolean` | `false` | `true/false` | 紧凑类型表单，减少 margin-bottom |  |
| size | `string` | `default` | `'default' , 'small' , 'large'` | 向表单内所有组件传递 size 参数,自定义组件需自行实现 size 接收 |  |
| disabled | `boolean` | `false` | `true/false` | 向表单内所有组件传递 disabled 属性，自定义组件需自行实现 disabled 接收 |  |
| autoSetPlaceHolder | `boolean` | `true` | ` true/false` | 自动设置表单内组件的 placeholder，自定义组件需自行实现 |  |
| autoSubmitOnEnter | `boolean` | `false` | ` true/false` | 在 input 中输入时按回车自动提交 | 2.4.0 |
| rulesMessageJoinLabel | `boolean` | `false` | `true/false` | 如果表单项有校验，会自动生成校验信息，该参数控制是否将字段中文名字拼接到自动生成的信息后方 |  |
| showAdvancedButton | `boolean` | `false` | `true/false` | 是否显示收起展开按钮 |  |
| emptySpan | `number , Partial<ColEx>` | 0 | - | 空白行格,可以是数值或者 col 对象 数 |  |
| autoAdvancedLine | `number` | 3 | - | 如果 showAdvancedButton 为 true，超过指定行数行默认折叠 |  |
| alwaysShowLines | `number` | 1 | - | 折叠时始终保持显示的行数 | 2.7.1 |
| showActionButtonGroup | `boolean` | `true` | `true/false` | 是否显示操作按钮(重置/提交) |  |
| actionColOptions | `Partial<ColEx>` | - | - | 操作按钮外层 Col 组件配置，如果开启 showAdvancedButton，则不用设置，具体见下方 actionColOptions |  |
| showResetButton | `boolean` | `true` | - | 是否显示重置按钮 |  |
| resetButtonOptions | `object` |  | - | 重置按钮配置见下方 ActionButtonOption |  |
| showSubmitButton | `boolean` | `true` | - | 是否显示提交按钮 |  |
| submitButtonOptions | `object` |  | - | 确认按钮配置见下方 ActionButtonOption |  |
| resetFunc | ` () => Promise<void>` |  | - | 自定义重置按钮逻辑`() => Promise<void>;` |  |
| submitFunc | ` () => Promise<void>` |  | - | 自定义提交按钮逻辑`() => Promise<void>;` |  |
| fieldMapToTime | `[string, [string, string], string?][]` |  | 'timestamp' ,'timestampStartDay' ,momentjs 时间格式 | 用于将表单内时间区域的应设成 2 个字段,见下方说明 |  |

### ColEx

见[src/components/Form/src/types/index.ts](https://github.com/vbenjs/vue-vben-admin/tree/main/src/components/Form/src/types/index.ts)

### ActionButtonOption

[BasicButtonProps](https://github.com/vbenjs/vue-vben-admin/tree/main/src/components/Button/types.ts)

```ts
export interface ButtonProps extends BasicButtonProps {
  text?: string;
}
```

### fieldMapToTime

将表单内时间区域的值映射成 2 个字段

如果表单内有时间区间组件，获取到的值是一个数组，但是往往我们传递到后台需要是 2 个字段

```ts
useForm({
  fieldMapToTime: [
    // data为时间组件在表单内的字段，startTime，endTime为转化后的开始时间与结束时间
    // 'YYYY-MM-DD'为时间格式，参考moment
    ['datetime', ['startTime', 'endTime'], 'YYYY-MM-DD'],
    // 支持多个字段
    ['datetime1', ['startTime1', 'endTime1'], 'YYYY-MM-DD HH:mm:ss'],
  ],
});

// fieldMapToTime没写的时候表单获取到的值
{
  datetime: [Date(),Date()]
}
//  ['datetime', ['startTime', 'endTime'], 'YYYY-MM-DD'],等同于 dayjs(Date()).format('YYYY-MM-DD'). 之后
{
    startTime: '2020-08-12',
    endTime: '2020-08-15',
}

// ['datetime', ['startTime', 'endTime'], 'timestamp'],等同于 dayjs(Date()).unix(). 之后
{
    startTime: 1597190400,
    endTime: 1597449600,
}

// ['datetime', ['startTime', 'endTime'], 'timestampStartDay'],等同于 dayjs(Date()).startOf('day').unix(). 之后
{
    startTime: 1597190400,
    endTime: 1597449600,
}
```

### FormSchema

| 属性 | 类型 | 默认值 | 可选值 | 说明 |
| --- | --- | --- | --- | --- |
| field | `string` | - | - | 字段名 |
| label | `string` | - | - | 标签名 |
| subLabel | `string` | - | - | 二级标签名灰色 |
| suffix | `string , number , ((values: RenderCallbackParams) => string / number);` | - | - | 组件后面的内容 |
| changeEvent | `string` | - | - | 表单更新事件名称 |
| helpMessage | `string , string[]` | - | - | 标签名右侧温馨提示 |
| helpComponentProps | `HelpComponentProps` | - | - | 标签名右侧温馨提示组件 props,见下方 HelpComponentProps |
| labelWidth | `string , number` | - | - | 覆盖统一设置的 labelWidth |
| disabledLabelWidth | `boolean` | false | true/false | 禁用 form 全局设置的 labelWidth,自己手动设置 labelCol 和 wrapperCol |
| component | `string` | - | - | 组件类型，见下方 ComponentType |
| componentProps | `any,()=>{}` | - | - | 所渲染的组件的 props |
| rules | `ValidationRule[]` | - | - | 校验规则,见下方 ValidationRule |
| required | `boolean` | - | - | 简化 rules 配置，为 true 则转化成 [{required:true}]。`2.4.0`之前的版本只支持 string 类型的值 |
| rulesMessageJoinLabel | `boolean` | false | - | 校验信息是否加入 label |
| itemProps | `any` | - | - | 参考下方 FormItem |
| colProps | `ColEx` | - | - | 参考上方 actionColOptions |
| defaultValue | `object` | - | - | 所渲渲染组件的初始值 |
| render | `(renderCallbackParams: RenderCallbackParams) => VNode / VNode[] / string` | - | - | 自定义渲染组件 |
| renderColContent | `(renderCallbackParams: RenderCallbackParams) => VNode / VNode[] / string` | - | - | 自定义渲染组件（需要自行包含 formItem） |
| renderComponentContent | `(renderCallbackParams: RenderCallbackParams) => any / string` | - | - | 自定义渲染组内部的 slot |
| slot | `string` | - | - | 自定义 slot，渲染组件 |
| colSlot | `string` | - | - | 自定义 slot，渲染组件 （需要自行包含 formItem） |
| format | `string` | - | `toUtcDate` | 表单日期格式化为 UTC 时间 |
| show | ` boolean / ((renderCallbackParams: RenderCallbackParams) => boolean)` | - | - | 动态判断当前组件是否显示，css 控制，不会删除 dom |
| ifShow | ` boolean / ((renderCallbackParams: RenderCallbackParams) => boolean)` | - | - | 动态判断当前组件是否显示，js 控制，会删除 dom |
| dynamicDisabled | `boolean / ((renderCallbackParams: RenderCallbackParams) => boolean) ` | - | - | 动态判断当前组件是否禁用 |
| dynamicRules | `boolean / ((renderCallbackParams: RenderCallbackParams) => boolean)` | - | - | 动态判返当前组件你校验规则 |

**RenderCallbackParams**

```ts
export interface RenderCallbackParams {
  schema: FormSchema;
  values: any;
  model: any;
  field: string;
}
```

**componentProps**

- 当值为对象类型时,该对象将作为`component`所对应组件的的 props 传入组件

- 当值为一个函数时候

参数有 4 个

`schema`: 表单的整个 schemas

`formActionType`: 操作表单的函数。与 useForm 返回的操作函数一致

`formModel`: 表单的双向绑定对象，这个值是响应式的。所以可以方便处理很多操作

`tableAction`: 操作表格的函数，与 useTable 返回的操作函数一致。注意该参数只在表格内开启搜索表单的时候有值，其余情况为`null`,

```tsx
{
  // 简单例子，值改变的时候操作表格或者修改表单内其他元素的值
  component:'Input',
  componentProps: ({ schema, tableAction, formActionType, formModel }) => {
    return {
      // xxxx props
      onChange:(e)=>{
        const {reload}=tableAction
        reload()
        // or
        formModel.xxx='123'
      }
    };
  };
}
```

**HelpComponentProps**

```ts
export interface HelpComponentProps {
  maxWidth: string;
  // 是否显示序号
  showIndex: boolean;
  // 文本列表
  text: any;
  // 颜色
  color: string;
  // 字体大小
  fontSize: string;
  icon: string;
  absolute: boolean;
  // 定位
  position: any;
}
```

**ComponentType**

schema 内组件的可选类型

```tsx
export type ComponentType =
  | 'Input'
  | 'InputGroup'
  | 'InputPassword'
  | 'InputSearch'
  | 'InputTextArea'
  | 'InputNumber'
  | 'InputCountDown'
  | 'Select'
  | 'ApiSelect'
  | 'TreeSelect'
  | 'ApiTree'
  | 'ApiTreeSelect'
  | 'RadioButtonGroup'
  | 'RadioGroup'
  | 'ApiRadioGroup'
  | 'Checkbox'
  | 'CheckboxGroup'
  | 'AutoComplete'
  | 'Cascader'
  | 'ApiCascader'
  | 'DatePicker'
  | 'MonthPicker'
  | 'RangePicker'
  | 'WeekPicker'
  | 'TimePicker'
  | 'TimeRangePicker'
  | 'Switch'
  | 'StrengthMeter'
  | 'Upload'
  | 'IconPicker'
  | 'Render'
  | 'Slider'
  | 'Rate'
  | 'Transfer'
  | 'ApiTransfer'
  | 'CropperAvatar'
  | 'Divider'
  | 'BasicTitle';
```

### Divider schema 说明

`Divider`类型用于在`schemas`中占位，将会渲染成一个分割线（始终占一整行的版面），可以用于较长表单的版面分隔。请只将 Divider 类型的 schema 当作一个分割线，而不是一个常规的表单字段。

- **`Divider`仅在`showAdvancedButton`为 false 时才会显示**（也就是说如果启用了表单收起和展开功能，`Divider`将不会显示）
- `Divider` 使用`schema`中的`label`以及`helpMessage`来渲染分割线中的提示内容
- `Divider` 可以使用`componentProps`来设置除`type`之外的 props
- `Divider` 不会渲染`AFormItem`，因此`schema`中除`label`、`componentProps`、`helpMessage`、`helpComponentProps`以外的属性不会被用到

## 自行添加需要的组件类型

在 `src/components/Form/src/componentMap.ts` 内，添加需要的组件，并在上方 **ComponentType** 添加相应的类型 key

### 方式 1

这种写法适用与适用频率较高的组件

```tsx
componentMap.set('componentName', 组件);

// ComponentType
export type ComponentType = xxxx | 'componentName';
```

### 方式 2

使用 **useComponentRegister** 进行注册

这种写法只能在当前页使用，页面销毁之后会从 componentMap 删除相应的组件

```tsx
import { useComponentRegister } from '@/components/form/index';

import { StrengthMeter } from '@/components/strength-meter/index';

useComponentRegister('StrengthMeter', StrengthMeter);
```

::: tip 提示

方式 2 出现的原因是为了减少打包体积，如果某个组件体积很大，用方式 1 的话可能会使首屏体积增加

:::

### render

自定义渲染内容

```vue
<template>
  <div class="m-4">
    <BasicForm @register="register" @submit="handleSubmit" />
  </div>
</template>
<script lang="ts" setup>
  import { defineComponent, h } from 'vue';
  import { BasicForm, FormSchema, useForm, useMessage } from '@eciol/ant-ui';
  import { Input } from 'ant-design-vue';
  const schemas: FormSchema[] = [
    {
      field: 'field1',
      component: 'Input',
      label: '字段1',
      colProps: {
        span: 8,
      },
      rules: [{ required: true }],
      render: ({ model, field }) => {
        return h(Input, {
          placeholder: '请输入',
          value: model[field],
          onChange: (e: ChangeEvent) => {
            model[field] = e.target.value;
          },
        });
      },
    },
    {
      field: 'field2',
      component: 'Input',
      label: '字段2',
      colProps: {
        span: 8,
      },
      rules: [{ required: true }],
      renderComponentContent: () => {
        return {
          suffix: () => 'suffix',
        };
      },
    },
  ];
  const { createMessage } = useMessage();
  const [register, { setProps }] = useForm({
    labelWidth: 120,
    schemas,
    actionColOptions: {
      span: 24,
    },
  });
  const handleSubmit = (values: any) => {
    createMessage.success('click search,values:' + JSON.stringify(values));
  };
</script>
```

### slot

自定义渲染内容

::: tip 提示

使用插槽自定义表单域时，请注意 antdv 有关 FormItem 的[相关说明](https://www.antdv.com/components/form-cn#API)。

:::

```vue
<template>
  <div class="m-4">
    <BasicForm @register="register">
      <template #customSlot="{ model, field }">
        <a-input v-model:value="model[field]" />
      </template>
    </BasicForm>
  </div>
</template>
<script lang="ts" setup>
  import { BasicForm, useForm } from '@eciol/ant-ui';
  const [register] = useForm({
    labelWidth: 100,
    actionColOptions: {
      span: 24,
    },
    schemas: [
      {
        field: 'field1',
        label: '字段1',
        slot: 'customSlot',
      },
    ],
  });
</script>
```

### ifShow/show/dynamicDisabled

自定义显示/禁用

```vue
<template>
  <div class="m-4">
    <BasicForm @register="register" />
  </div>
</template>
<script lang="ts" setup>
  import { BasicForm, FormSchema, useForm } from '@eciol/ant-ui';
  const schemas: FormSchema[] = [
    {
      field: 'field1',
      component: 'Input',
      label: '字段1',
      colProps: {
        span: 8,
      },
      show: ({ values }) => {
        return !!values.field5;
      },
    },
    {
      field: 'field2',
      component: 'Input',
      label: '字段2',
      colProps: {
        span: 8,
      },
      ifShow: ({ values }) => {
        return !!values.field6;
      },
    },
    {
      field: 'field3',
      component: 'DatePicker',
      label: '字段3',
      colProps: {
        span: 8,
      },
      dynamicDisabled: ({ values }) => {
        return !!values.field7;
      },
    },
  ];

  const [register, { setProps }] = useForm({
    labelWidth: 120,
    schemas,
    actionColOptions: {
      span: 24,
    },
  });
</script>
```

---

见 [antv form](https://www.antdv.com/components/form-cn/#%E6%A0%A1%E9%AA%8C%E8%A7%84%E5%88%99)

## Slots

| 名称          | 说明         |
| ------------- | ------------ |
| formFooter    | 表单底部区域 |
| formHeader    | 表单顶部区域 |
| resetBefore   | 重置按钮前   |
| submitBefore  | 提交按钮前   |
| advanceBefore | 展开按钮前   |
| advanceAfter  | 展开按钮后   |

## ApiSelect

远程下拉加载组件，该组件可以用于学习参考如何自定义组件集成到 Form 组件内，将自定义组件交由 Form 去管理

### Usage

```ts
const schemas: FormSchema[] = [
  {
    field: 'field',
    component: 'ApiSelect',
    label: '字段',
  },
];
```

### Props

| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| numberToString | `boolean` | `false` | 是否将`number`值转化为`string` |
| api | `()=>Promise<{ label: string; value: string; disabled?: boolean }[]>` | - | 数据接口，接受一个 Promise 对象 |
| params | `object` | - | 接口参数。此属性改变时会自动重新加载接口数据 |
| resultField | `string` | - | 接口返回的字段，如果接口返回数组，可以不填。支持`x.x.x`格式 |
| labelField | `string` | `label` | 下拉数组项内`label`显示文本的字段，支持`x.x.x`格式 |
| valueField | `string` | `value` | 下拉数组项内`value`实际值的字段，支持`x.x.x`格式 |
| immediate | `boolean` | `true` | 是否立即请求接口，否则将在第一次点击时候触发请求 |

## ApiTreeSelect

远程下拉树加载组件，和`ApiSelect`类似，2.6.1 以上版本

### Props

| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| api | `()=>Promise<{ label: string; value: string; children?: any[] }[]>` | - | 数据接口，接受一个 Promise 对象 |
| params | `object` | - | 接口参数。此属性改变时会自动重新加载接口数据 |
| resultField | `string` | - | 接口返回的字段，如果接口返回数组，可以不填。支持`x.x.x`格式 |
| immediate | `boolean` | `true` | 是否立即请求接口。 |

## RadioButtonGroup

Radio Button 风格的选择按钮

### Usage

```ts
const schemas: FormSchema[] = [
  {
    field: 'field',
    component: 'RadioButtonGroup',
    label: '字段',
  },
];
```

### Props

| 属性    | 类型                                                     | 默认值 | 说明     |
| ------- | -------------------------------------------------------- | ------ | -------- |
| options | `{ label: string; value: string; disabled?: boolean }[]` | -      | 数据字段 |
