Skip to content

表单 Form

具有数据收集、校验和提交功能的表单,包含复选框、单选框、输入框、下拉选择框等元素。

基本用法

表单的基本用法。


代码事例
vue
<template>
  <fk-form :model="form" :style="{ width: '600px' }" @submit="handleSubmit">
    <fk-form-item field="name" tooltip="Please enter username" label="Username">
      <fk-input
        v-model="form.name"
        placeholder="please enter your username..."
      />
    </fk-form-item>
    <fk-form-item field="post" label="Post">
      <fk-input v-model="form.post" placeholder="please enter your post..." />
    </fk-form-item>
    <fk-form-item field="isRead">
      <fk-checkbox v-model="form.isRead"> I have read the manual </fk-checkbox>
    </fk-form-item>
    <fk-form-item>
      <fk-button type="primary" html-type="submit">Submit</fk-button>
    </fk-form-item>
  </fk-form>
  {{ form }}
</template>

<script>
import { reactive } from 'vue';

export default {
  setup() {
    const form = reactive({
      name: '',
      post: '',
      isRead: false,
    });
    const handleSubmit = (data) => {
      console.log(data);
    };

    return {
      form,
      handleSubmit,
    };
  },
};
</script>
代码事例
vue
<template>
    <fk-space direction="vertical" size="large">
      <fk-radio-group v-model="status" type="button">
        <fk-radio value="validating">validating</fk-radio>
        <fk-radio value="success">success</fk-radio>
        <fk-radio value="error">error</fk-radio>
        <fk-radio value="warning">warning</fk-radio>
      </fk-radio-group>
      <fk-radio-group v-model="size" type="button">
        <fk-radio value="mini">mini</fk-radio>
        <fk-radio value="small">small</fk-radio>
        <fk-radio value="medium">medium</fk-radio>
        <fk-radio value="large">large</fk-radio>
      </fk-radio-group>
    </fk-space>
    <fk-form
      :model="form"
      :style="{ width: '600px', marginTop: '20px' }"
      :size="size"
    >
      <fk-form-item
        field="name"
        label="Username"
        help="This is custom message"
        extra="This is extra text"
        :validate-status="status"
        feedback
      >
        <fk-input
          v-model="form.name"
          placeholder="please enter your username..."
        />
      </fk-form-item>
      <fk-form-item
        field="post"
        label="Post"
        help="This is custom message"
        extra="This is extra text"
        :validate-status="status"
        feedback
      >
        <fk-input-number
          v-model="form.post"
          placeholder="please enter your post..."
        />
      </fk-form-item>
      <fk-form-item
        field="tags"
        label="Tags"
        help="This is custom message"
        extra="This is extra text"
        :validate-status="status"
        feedback
      >
        <fk-input-tag
          v-model="form.tags"
          placeholder="please enter your post..."
        />
      </fk-form-item>
      <fk-form-item
        field="section"
        label="Section"
        :rules="[{ match: /section one/, message: 'must select one' }]"
        :validate-status="status"
        feedback
      >
        <fk-select v-model="form.section" placeholder="Please select ...">
          <fk-option value="section one">Section One</fk-option>
          <fk-option value="section two">Section Two</fk-option>
          <fk-option value="section three">Section Three</fk-option>
        </fk-select>
      </fk-form-item>
      <fk-form-item label="DateRange" :validate-status="status" feedback>
        <fk-range-picker/>
      </fk-form-item>

      <fk-form-item field="date" label="Date" :validate-status="status" feedback>
        <fk-date-picker/>
      </fk-form-item>

      <fk-form-item field="time" label="Time" :validate-status="status" feedback>
        <fk-time-picker/>
      </fk-form-item>
    </fk-form> 
</template>

<script>
import { reactive, ref } from 'vue';

export default {
  setup() {
    const status = ref('success');
    const size = ref('medium');
    const form = reactive({
      name: '',
      post: undefined,
      tags: ['tag1'],
      section: '',
    });

    return {
      status,
      size,
      form,
    };
  },
};
</script>

表单布局

表单支持三种布局方式: horizontal - 水平排列 (默认)vertical - 垂直排列, inline - 行内排列。


代码事例
vue
<template>
  <fk-space direction="vertical" size="large" :style="{width: '600px'}">
    <fk-radio-group v-model="layout" type="button">
      <fk-radio value="horizontal">horizontal</fk-radio>
      <fk-radio value="vertical">vertical</fk-radio>
      <fk-radio value="inline">inline</fk-radio>
    </fk-radio-group>
    <fk-form :model="form" :layout="layout">
      <fk-form-item field="name" label="Username">
        <fk-input v-model="form.name" placeholder="please enter your username..." />
      </fk-form-item>
      <fk-form-item field="post" label="Post">
        <fk-input v-model="form.post" placeholder="please enter your post..." />
      </fk-form-item>
      <fk-form-item field="isRead">
        <fk-checkbox v-model="form.isRead">
          I have read the manual
        </fk-checkbox>
      </fk-form-item>
      <fk-form-item>
        <fk-button>Submit</fk-button>
      </fk-form-item>
    </fk-form>
    <div>
      {{ form }}
    </div>
  </fk-space>
</template>

<script>
import { reactive, ref } from 'vue';

export default {
  setup() {
    const layout = ref('horizontal')
    const form = reactive({
      name: '',
      post: '',
      isRead: false,
    })

    return {
      layout,
      form,
    }
  },
}
</script>

额外信息和帮助信息

可以使用 extra 添加额外信息。 如果需要在外部自定义校验信息,可以使用 help 属性或插槽。设置 help 时校验信息会被屏蔽。


代码事例
vue
<template>
  <fk-form :model="form" :style="{width:'600px'}">
    <fk-form-item field="name" label="Username" validate-trigger="input" required>
      <fk-input v-model="form.name" placeholder="please enter your username..." />
      <template #extra>
        <div>Used to login</div>
      </template>
    </fk-form-item>
    <fk-form-item field="post" label="Post" validate-trigger="input" required>
      <fk-input v-model="form.post" placeholder="please enter your post..." />
      <template #extra>
        <div>Used to login</div>
      </template>
      <template #help>
        <div>Custom valitae message</div>
      </template>
    </fk-form-item>
    <fk-form-item field="isRead">
      <fk-checkbox v-model="form.isRead">
        I have read the manual
      </fk-checkbox>
    </fk-form-item>
  </fk-form>
  {{ form }}
</template>

<script>
import { reactive } from 'vue';

export default {
  setup() {
    const form = reactive({
      name: '',
      post: '',
      isRead: false,
    })

    return {
      form,
    }
  },
}
</script>

嵌套数据

展示了多种表单项嵌套的方式。 表单项组件默认会将表单项状态和事件绑定到第一子组件,如果想要使用表单项进行布局设置,请设置 :merge-props="false" 以关闭绑定,或者使用函数指定需要绑定的数据。 如果使用 grid 组件进行布局,请设置 :content-flex="false" 关闭表单项内容的 flex 布局。


代码事例
vue
<template>
  <fk-form :model="form" :style="{width:'600px'}">
    <fk-form-item label="Username" :content-flex="false" :merge-props="false" extra="Show error message together">
      <fk-row :gutter="8">
        <fk-col :span="12">
          <fk-form-item
field="together.firstname" validate-trigger="input"
                       :rules="[{required:true,message:'firstname is required'}]" no-style>
            <fk-input v-model="form.together.firstname" placeholder="please enter your firstname..." />
          </fk-form-item>
        </fk-col>
        <fk-col :span="12">
          <fk-form-item
field="together.lastname" validate-trigger="input"
                       :rules="[{required:true,message:'lastname is required'}]" no-style>
            <fk-input v-model="form.together.lastname" placeholder="please enter your lastname..." />
          </fk-form-item>
        </fk-col>
      </fk-row>
    </fk-form-item>
    <fk-form-item label="Username" :content-flex="false" :merge-props="false">
      <fk-row :gutter="8">
        <fk-col :span="12">
          <fk-form-item
field="separate.firstname" validate-trigger="input"
                       extra="Show error message separate"
                       :rules="[{required:true,message:'firstname is required'}]" hide-label>
            <fk-input v-model="form.separate.firstname" placeholder="please enter your firstname..." />
          </fk-form-item>
        </fk-col>
        <fk-col :span="12">
          <fk-form-item
field="separate.lastname" validate-trigger="input"
                       :rules="[{required:true,message:'lastname is required'}]" hide-label>
            <fk-input v-model="form.separate.lastname" placeholder="please enter your lastname..." />
          </fk-form-item>
        </fk-col>
      </fk-row>
    </fk-form-item>
    <fk-form-item label="Posts" :content-flex="false" :merge-props="false">
      <fk-space direction="vertical" fill>
        <fk-form-item field="posts.post1" label="Post1">
          <fk-input v-model="form.posts.post1" placeholder="please enter your post..." />
        </fk-form-item>
        <fk-form-item field="posts.post2" label="Post2">
          <fk-input v-model="form.posts.post2" placeholder="please enter your post..." />
        </fk-form-item>
      </fk-space>
    </fk-form-item>
    <fk-form-item field="isRead">
      <fk-checkbox v-model="form.isRead">
        I have read the manual
      </fk-checkbox>
    </fk-form-item>
  </fk-form>
  {{ form }}
</template>

<script>
import { reactive } from 'vue';

export default {
  setup() {
    const form = reactive({
      together: {
        firstname: '',
        lastname: '',
      },
      separate: {
        firstname: '',
        lastname: '',
      },
      posts: {
        post1: '',
        post2: ''
      },
      isRead: false,
    })

    return {
      form,
    }
  },
}
</script>

栅格布局

展示了使用栅格布局的方式。可以使用 label-col-flex 属性指定标签的具体宽度。


代码事例
vue
<template>
  <fk-form :model="form">
    <fk-row :gutter="16">
      <fk-col :span="8">
        <fk-form-item field="value1" label="Value 1" label-col-flex="100px">
          <fk-input v-model="form.value1" placeholder="please enter..." />
        </fk-form-item>
      </fk-col>
      <fk-col :span="8">
        <fk-form-item field="value2" label="Value 2" label-col-flex="80px">
          <fk-input v-model="form.value2" placeholder="please enter..." />
        </fk-form-item>
      </fk-col>
      <fk-col :span="8">
        <fk-form-item field="value3" label="Value 3" label-col-flex="80px">
          <fk-input v-model="form.value3" placeholder="please enter..." />
        </fk-form-item>
      </fk-col>
    </fk-row>
    <fk-row :gutter="16">
      <fk-col :span="16">
        <fk-form-item field="value4" label="Value 4" label-col-flex="100px">
          <fk-input v-model="form.value4" placeholder="please enter..." />
        </fk-form-item>
      </fk-col>
      <fk-col :span="8">
        <fk-form-item field="value5" label="Value 5" label-col-flex="80px">
          <fk-input v-model="form.value5" placeholder="please enter..." />
        </fk-form-item>
      </fk-col>
    </fk-row>
  </fk-form>
  {{ form }}
</template>

<script>
import { reactive } from 'vue';

export default {
  setup() {
    const form = reactive({
      value1: '',
      value2: '',
      value3: '',
      value4: '',
      value5: '',
    })

    return {
      form,
    }
  },
}
</script>

自动标签宽度

设置 auto-label-width 开启自动标签宽度。仅在 layout="horizontal" 布局下生效。 * 目前仅在首次加载后生效。


代码事例
vue
<template>
  <fk-form :model="form" :style="{width:'600px'}" auto-label-width @submit="handleSubmit">
    <fk-form-item field="name" label="Username">
      <fk-input v-model="form.name" placeholder="please enter your username..." />
    </fk-form-item>
    <fk-form-item field="post" label="Post">
      <fk-input v-model="form.post" placeholder="please enter your post..." />
    </fk-form-item>
    <fk-form-item field="isRead">
      <fk-checkbox v-model="form.isRead">
        I have read the manual
      </fk-checkbox>
    </fk-form-item>
    <fk-form-item>
      <fk-button html-type="submit">Submit</fk-button>
    </fk-form-item>
  </fk-form>
  {{ form }}
</template>

<script>
import { reactive } from 'vue';

export default {
  setup() {
    const form = reactive({
      name: '',
      post: '',
      isRead: false,
    })
    const handleSubmit = (data) => {
      console.log(data)
    }

    return {
      form,
      handleSubmit
    }
  },
}
</script>

验证表单

展示了表单校验的使用方法。


代码事例
vue
<template>
  <fk-form ref="formRef" :size="form.size" :model="form" :style="{width:'600px'}" @submit="handleSubmit">
    <fk-form-item field="size" label="Form Size" >
      <fk-radio-group v-model="form.size" type="button">
        <fk-radio value="mini">Mini</fk-radio>
        <fk-radio value="small">Small</fk-radio>
        <fk-radio value="medium">Medium</fk-radio>
        <fk-radio value="large">Large</fk-radio>
      </fk-radio-group>
    </fk-form-item>
    <fk-form-item
field="name" label="Username"
                 :rules="[{required:true,message:'name is required'},{minLength:5,message:'must be greater than 5 characters'}]"
                 :validate-trigger="['change','input']"
    >
      <fk-input v-model="form.name" placeholder="please enter your username..." />
    </fk-form-item>
    <fk-form-item
field="age" label="Age"
                 :rules="[{required:true,message:'age is required'},{type:'number', max:200,message:'age is max than 200'}]"
    >
      <fk-input-number v-model="form.age" placeholder="please enter your age..." />
    </fk-form-item>
    <fk-form-item field="section" label="Section" :rules="[{match:/section one/,message:'must select one'}]">
      <fk-select v-model="form.section" placeholder="Please select ..." allow-clear>
        <fk-option value="section one">Section One</fk-option>
        <fk-option value="section two">Section Two</fk-option>
        <fk-option value="section three">Section Three</fk-option>
      </fk-select>
    </fk-form-item>
    <fk-form-item field="province" label="Province" :rules="[{required:true,message:'province is required'}]">
      <fk-cascader v-model="form.province" :options="options" placeholder="Please select ..." allow-clear />
    </fk-form-item>
    <fk-form-item
field="options" label="Options"
                 :rules="[{type:'array',minLength:2,message:'must select greater than two options'}]"
    >
      <fk-checkbox-group v-model="form.options">
        <fk-checkbox value="option one">Section One</fk-checkbox>
        <fk-checkbox value="option two">Option Two</fk-checkbox>
        <fk-checkbox value="option three">Option Three</fk-checkbox>
        <fk-checkbox value="option four">Option Four</fk-checkbox>
      </fk-checkbox-group>
    </fk-form-item>
    <fk-form-item field="date" label="Date">
      <fk-date-picker v-model="form.date" placeholder="Please select ..."/>
    </fk-form-item>
    <fk-form-item field="time" label="Time">
      <fk-time-picker v-model="form.time" placeholder="Please select ..."/>
    </fk-form-item>
    <fk-form-item field="radio" label="Radio" :rules="[{match:/one/,message:'must select one'}]">
      <fk-radio-group v-model="form.radio">
        <fk-radio value="radio one">Radio One</fk-radio>
        <fk-radio value="radio two">Radio Two</fk-radio>
      </fk-radio-group>
    </fk-form-item>
    <fk-form-item field="slider" label="Slider" :rules="[{type:'number', min:5,message:'slider is min than 5'}]">
      <fk-slider v-model="form.slider" :max="10" />
    </fk-form-item>
    <fk-form-item field="score" label="Score">
      <fk-rate v-model="form.score" allow-clear />
    </fk-form-item>
    <fk-form-item field="switch" label="Switch" :rules="[{type:'boolean', true:true,message:'must be true'}]">
      <fk-switch v-model="form.switch" />
    </fk-form-item>
    <fk-form-item field="multiSelect" label="Multiple Select">
      <fk-select v-model="form.multiSelect" placeholder="Please select ..." multiple>
        <fk-option value="section one">Section One</fk-option>
        <fk-option value="section two">Section Two</fk-option>
        <fk-option value="section three">Section Three</fk-option>
      </fk-select>
    </fk-form-item>
    <fk-form-item field="treeSelect" label="Tree Select">
      <fk-tree-select v-model="form.treeSelect" :data="treeData" placeholder="Please select ..."/>
    </fk-form-item>
    <fk-form-item>
      <fk-space>
        <fk-button html-type="submit">Submit</fk-button>
        <fk-button @click="$refs.formRef.resetFields()">Reset</fk-button>
      </fk-space>
    </fk-form-item>
  </fk-form>
  {{ form }}
</template>

<script>
import { reactive } from 'vue';

export default {
  setup() {
    const handleSubmit = ({values, errors}) => {
      console.log('values:', values, '\nerrors:', errors)
    }

    const form = reactive({
      size: 'medium',
      name: '',
      age: undefined,
      section: '',
      province: 'haidian',
      options: [],
      date: '',
      time: '',
      radio: 'radio one',
      slider: 5,
      score: 5,
      switch: false,
      multiSelect: ['section one'],
      treeSelect: ''
    });
    const options = [
      {
        value: 'beijing',
        label: 'Beijing',
        children: [
          {
            value: 'chaoyang',
            label: 'ChaoYang',
            children: [
              {
                value: 'datunli',
                label: 'Datunli',
              },
            ],
          },
          {
            value: 'haidian',
            label: 'Haidian',
          },
          {
            value: 'dongcheng',
            label: 'Dongcheng',
          },
          {
            value: 'xicheng',
            label: 'XiCheng',
          },
        ],
      },
      {
        value: 'shanghai',
        label: 'Shanghai',
        children: [
          {
            value: 'shanghaishi',
            label: 'Shanghai',
            children: [
              {
                value: 'huangpu',
                label: 'Huangpu',
              },
            ],
          },
        ],
      },
    ];
    const treeData = [
      {
        key: 'node1',
        title: 'Node1',
        children: [
          {
            key: 'node2',
            title: 'Node2',
          },
        ],
      },
      {
        key: 'node3',
        title: 'Node3',
        children: [
          {
            key: 'node4',
            title: 'Node4',
          },
          {
            key: 'node5',
            title: 'Node5',
          },
        ],
      },
    ]

    return {
      form,
      options,
      treeData,
      handleSubmit
    }
  },
}
</script>

验证表单2

展示了表单校验rules使用在fk-form上的使用方法,以及可以直接校验emailipurl


代码事例
vue
<template>
  <fk-form ref="formRef" :rules="rules" :model="form" :style="{width:'600px'}" @submit="handleSubmit">
    <fk-form-item field="name" label="Username" validate-trigger="blur">
      <fk-input v-model="form.name" placeholder="please enter your username..." />
    </fk-form-item>
    <fk-form-item field="password" label="密码" validate-trigger="blur">
      <fk-input-password v-model="form.password" placeholder="please enter your password..." />
    </fk-form-item>
    <fk-form-item field="password2" label="确认密码" validate-trigger="blur">
      <fk-input-password v-model="form.password2" placeholder="please confirm your password..." />
    </fk-form-item>
    <fk-form-item field="email" label="email">
      <fk-input v-model="form.email" placeholder="please enter your email..." />
    </fk-form-item>
    <fk-form-item field="ip" label="IP">
      <fk-input v-model="form.ip" placeholder="please enter your ip..." />
    </fk-form-item>
    <fk-form-item field="url" label="URL">
      <fk-input v-model="form.url" placeholder="please enter your url..." />
    </fk-form-item>
    <fk-form-item field="match" label="match">
      <fk-input v-model="form.match" placeholder="please enter your match..." />
    </fk-form-item>
    <fk-form-item>
      <fk-space>
        <fk-button html-type="submit">Submit</fk-button>
        <fk-button @click="$refs.formRef.resetFields()">Reset</fk-button>
      </fk-space>
    </fk-form-item>
  </fk-form>
  {{ form }}
</template>

<script>
import { reactive } from 'vue';

export default {
  setup() {
    const handleSubmit = ({values, errors}) => {
      console.log('values:', values, '\nerrors:', errors)
    }

    const form = reactive({
      name: '',
      password: '',
      password2: '',
      email: '',
      ip: '192.168.2.1',
      url: '',
      match: ''
    });

    const rules = {
      name: [
        {
          required: true,
          message:'name is required',
        },
      ],
      password: [
        {
          required: true,
          message:'password is required',
        },
      ],
      password2: [
        {
          required: true,
          message:'password is required',
        },
        {
          validator: (value, cb) => {
            if (value !== form.password) {
              cb('two passwords do not match')
            } else {
              cb()
            }
          }
        }
      ],
      email: [
        {
          type: 'email',
          required: true,
        }
      ],
      ip: [
        {
          type: 'ip',
          required: true,
        }
      ],
      url: [
        {
          type: 'url',
          required: true,
        }
      ],
      match: [
        {
          required: true,
          validator: (value, cb) => {
            return new Promise((resolve) => {
              if (!value) {
                cb('Please enter match')
              }
              if (value !== 'match') {
                cb('match must be match!')
              }
              resolve()
            })
          }
        }
      ],
    }

    return {
      form,
      rules,
      handleSubmit
    }
  },
}
</script>

自定义表单校验状态

开启 feedback 可以让部分输入组件展示当前状态信息


代码事例
vue
<template>
  <fk-space direction="vertical" size="large">
    <fk-radio-group v-model="status" type="button">
      <fk-radio value="validating">validating</fk-radio>
      <fk-radio value="success">success</fk-radio>
      <fk-radio value="error">error</fk-radio>
      <fk-radio value="warning">warning</fk-radio>
    </fk-radio-group>
    <fk-radio-group v-model="size" type="button">
      <fk-radio value="mini">mini</fk-radio>
      <fk-radio value="small">small</fk-radio>
      <fk-radio value="medium">medium</fk-radio>
      <fk-radio value="large">large</fk-radio>
    </fk-radio-group>
  </fk-space>
  <fk-form
    :model="form"
    :style="{ width: '600px', marginTop: '20px' }"
    :size="size"
  >
    <fk-form-item
      field="name"
      label="Username"
      help="This is custom message"
      extra="This is extra text"
      :validate-status="status"
      feedback
    >
      <fk-input
        v-model="form.name"
        placeholder="please enter your username..."
      />
    </fk-form-item>
    <fk-form-item
      field="post"
      label="Post"
      help="This is custom message"
      extra="This is extra text"
      :validate-status="status"
      feedback
    >
      <fk-input-number
        v-model="form.post"
        placeholder="please enter your post..."
      />
    </fk-form-item>
    <fk-form-item
      field="tags"
      label="Tags"
      help="This is custom message"
      extra="This is extra text"
      :validate-status="status"
      feedback
    >
      <fk-input-tag
        v-model="form.tags"
        placeholder="please enter your post..."
      />
    </fk-form-item>
    <fk-form-item
      field="section"
      label="Section"
      :rules="[{ match: /section one/, message: 'must select one' }]"
      :validate-status="status"
      feedback
    >
      <fk-select v-model="form.section" placeholder="Please select ...">
        <fk-option value="section one">Section One</fk-option>
        <fk-option value="section two">Section Two</fk-option>
        <fk-option value="section three">Section Three</fk-option>
      </fk-select>
    </fk-form-item>
    <fk-form-item label="DateRange" :validate-status="status" feedback>
      <fk-range-picker/>
    </fk-form-item>

    <fk-form-item field="date" label="Date" :validate-status="status" feedback>
      <fk-date-picker/>
    </fk-form-item>

    <fk-form-item field="time" label="Time" :validate-status="status" feedback>
      <fk-time-picker/>
    </fk-form-item>
  </fk-form>
</template>

<script>
import { reactive, ref } from 'vue';

export default {
  setup() {
    const status = ref('success');
    const size = ref('medium');
    const form = reactive({
      name: '',
      post: undefined,
      tags: ['tag1'],
      section: '',
    });

    return {
      status,
      size,
      form,
    };
  },
};
</script>

动态表单

通过数据动态控制表单内容。


代码事例
vue
<template>
  <fk-form :model="form" :style="{width:'600px'}">
    <fk-form-item field="name" label="Username">
      <fk-input v-model="form.name" placeholder="please enter your username..." />
    </fk-form-item>
    <fk-form-item v-for="(post,index) of form.posts" :key="index" :field="`posts[${index}].value`" :label="`Post-${index}`">
      <fk-input v-model="post.value" placeholder="please enter your post..." />
      <fk-button :style="{marginLeft:'10px'}" @click="handleDelete(index)">Delete</fk-button>
    </fk-form-item>
  </fk-form>
  <div>
    <fk-button @click="handleAdd">Add Post</fk-button>
  </div>
  {{ form }}
</template>

<script>
import { reactive } from 'vue';

export default {
  setup() {
    const form = reactive({
      name: '',
      posts: [{value: ''}]
    })
    const handleAdd = () => {
      form.posts.push({
        value: ''
      })
    };
    const handleDelete = (index) => {
      form.posts.splice(index, 1)
    }

    return {
      form,
      handleAdd,
      handleDelete
    }
  },
}
</script>

全局禁用

通过 disabled 属性可以禁用整个表单。


代码事例
vue
<template>
  <fk-form :model="form" :style="{width:'600px'}" disabled>
    <fk-form-item field="name" label="Username">
      <fk-input v-model="form.name" placeholder="please enter your username..." />
    </fk-form-item>
    <fk-form-item field="post" label="Post">
      <fk-input v-model="form.post" placeholder="please enter your post..." />
    </fk-form-item>
    <fk-form-item field="isRead">
      <fk-checkbox v-model="form.isRead">
        I have read the manual
      </fk-checkbox>
    </fk-form-item>
    <fk-form-item>
      <fk-button>Submit</fk-button>
    </fk-form-item>
  </fk-form>
  {{ form }}
</template>

<script>
import { reactive } from 'vue';

export default {
  setup() {
    const form = reactive({
      name: '',
      post: '',
      isRead: false,
    })

    return {
      form,
    }
  },
}
</script>

表单异步校验

通过异步的方法校验表单功能。


代码事例
vue
<template>
  <fk-form ref="formRef" :model="form" :style="{width:'600px'}">
    <fk-form-item field="name" label="Username" :rules="rules">
      <fk-input v-model="form.name" placeholder="please enter your username..." />
    </fk-form-item>
    <fk-form-item field="post" label="Post">
      <fk-input v-model="form.post" placeholder="please enter your post..." />
    </fk-form-item>
    <fk-form-item field="isRead">
      <fk-checkbox v-model="form.isRead">
        I have read the manual
      </fk-checkbox>
    </fk-form-item>
    <fk-form-item>
      <fk-button @click="handleClick">Set Status</fk-button>
    </fk-form-item>
  </fk-form>
  {{ form }}
</template>

<script>
import { reactive, ref } from 'vue';

export default {
  setup() {
    const formRef = ref()
    const form = reactive({
      name: '',
      post: '',
      isRead: false,
    })
    const rules = [{
      validator: (value, cb) => {
        return new Promise(resolve => {
          window.setTimeout(() => {
            if (value !== 'admin') {
              cb('name must be admin')
            }
            resolve()
          }, 2000)
        })
      }
    }];
    const handleClick = () => {
      formRef.value.setFields({
        name: {
          status: 'error',
          message: 'async name error'
        },
        post: {
          status: 'error',
          message: 'valid post'
        }
      })
    }

    return {
      formRef,
      form,
      rules,
      handleClick
    }
  },
}
</script>

自定义表单组件

通过 useFormItem 自定义表单组件。2.18.0 版本后可用。


代码事例
vue
<template>
  <fk-space style="margin-bottom: 20px;">
    <fk-switch v-model="disabled" />
    Disabled: {{disabled}}
  </fk-space>
  <Form :model="form" :disabled="disabled" :style="{width:'600px'}">
    <FormItem
field="name" label="Username"
              :rules="[{required:true,message:'name is required'},{minLength:5,message:'must be greater than 5 characters'}]">
      <MyInput v-model="form.name" placeholder="please enter your username..." />
    </FormItem>
  </Form>
</template>

<script lang="ts">
import { h, reactive, ref } from 'vue';
import { Form, FormItem, useFormItem } from '@erp/common';

const MyInput = {
  emits: ['update:modelValue'],
  setup(_, { emit }) {
    const { mergedDisabled, eventHandlers } = useFormItem();
    const handleInput = (ev) => {
      const { value } = ev.target;
      emit('update:modelValue', value)
      eventHandlers.value?.onChange?.(ev)
    }
    return () => h('input', { disabled: mergedDisabled.value, onInput: handleInput })
  }
}

export default {
  components: {
    Form,
    FormItem,
    MyInput
  },
  setup() {
    const disabled = ref(false);
    const form = reactive({
      name: ''
    })

    return {
      disabled,
      form
    }
  },
}
</script>

滚动到指定表单字段

展示了提交失败自动滚动到第一个错误字段和手动滚动对应字段的使用方法。


代码事例
vue
<template>
  <fk-space>
    <fk-button @click="formRef && formRef.validate()">Submit</fk-button>
    <fk-button @click="formRef && formRef.resetFields()">Reset</fk-button>
    <fk-button @click="formRef && formRef.scrollToField('name19')">Scroll to the last field</fk-button>
  </fk-space>
  <fk-form
    ref="formRef"
    style="width: 500px;height: 300px;margin-top:20px;padding-right: 16px;overflow: auto"
    :model="form"
    :scroll-to-first-error="true"
  >
    <template v-for="(fieldName, index) in fieldNames" :key="index">
      <fk-form-item
        :field="fieldName"
        :label="'user' + index"
        :rules="[
          { required: true, message: 'Name is required' },
        ]"
      >
        <fk-input v-model="form[fieldName]" />
      </fk-form-item>
    </template>
  </fk-form>
</template>

<script>
import { reactive, ref } from 'vue';

export default {
  setup() {
    const formRef = ref(null);
    const fieldCount = 20;
    const fieldNames = Array.from({ length: fieldCount }, (_, index) => `name${index}`);
    const form = reactive(Object.fromEntries(
      fieldNames.map((fieldName, index) => [fieldName, index === 7 ? '' : index.toString()])
    ));

    return {
      form,
      formRef,
      fieldCount,
      fieldNames
    };
  },
};
</script>

API

<form> Props

参数名描述类型默认值版本
model (必填)表单数据对象object-
layout表单的布局方式,包括水平、垂直、多列'horizontal' | 'vertical' | 'inline''horizontal'
size表单控件的尺寸'mini' | 'small' | 'medium' | 'large''medium'
label-col-props标签元素布局选项。参数同 <col> 组件一致object{ span: 5, offset: 0 }
wrapper-col-props表单控件布局选项。参数同 <col> 组件一致object{ span: 19, offset: 0 }
label-align标签的对齐方向'left' | 'right''right'
disabled是否禁用表单boolean-
rules表单项校验规则Record<string, FieldRule | FieldRule[]>-
auto-label-width是否开启自动标签宽度,仅在 layout="horizontal" 下生效。booleantrue1.0.0
id表单控件 id 的前缀string-
scroll-to-first-error验证失败后滚动到第一个错误字段booleanfalse1.0.0

<form> Events

事件名描述参数
submit表单提交时触发data: {values: Record<string, any>; errors: Record<string, ValidatedError> | undefined}
ev: Event
submit-success验证成功时触发values: Record<string, any>
ev: Event
submit-failed验证失败时触发data: {values: Record<string, any>; errors: Record<string, ValidatedError>}
ev: Event

<form> Methods

方法名描述参数返回值版本
validate校验全部表单数据callback: (errors: undefined | Record<string, ValidatedError>) => voidPromise<undefined | Record<string, ValidatedError>>
validateField校验部分表单数据field: string | string[]
callback: (errors: undefined | Record<string, ValidatedError>) => void
Promise<undefined | Record<string, ValidatedError>>
resetFields重置表单数据field: string | string[]-
clearValidate清除校验状态field: string | string[]-
setFields设置表单项的值和状态data: Record<string, FieldData>-
scrollToField滚动到指定表单项field: string-1.0.0

<form-item> Props

参数名描述类型默认值版本
field表单元素在数据对象中的path(数据项必填)string''
label标签的文本string-
tooltip提示内容string-1.0.0
show-colon是否显示冒号booleanfalse
no-style是否去除样式booleanfalse
disabled是否禁用boolean-
help帮助文案string-
extra额外显示的文案string-
required是否必须填写booleanfalse
asterisk-position可选择将星号置于 label 前/后'start' | 'end''start'1.0.0
rules表单项校验规则(优先级高于 form 的 rules)FieldRule | FieldRule[]-
validate-status校验状态'success' | 'warning' | 'error' | 'validating'-
validate-trigger触发校验的事件'change' | 'input' | 'focus' | 'blur''change'
label-col-props标签元素布局选项。参数同 <col> 组件一致object-
wrapper-col-props表单控件布局选项。参数同 <col> 组件一致object-
hide-label是否隐藏标签booleanfalse
hide-asterisk是否隐藏星号booleanfalse
label-col-style标签元素布局组件的 styleobject-1.0.0
wrapper-col-style表单控件布局组件的 styleobject-1.0.0
row-props表单项布局选项。参数同 <row> 组件一致object-1.0.0
row-class表单项布局组件的 classstring|array|object-1.0.0
content-class表单控件包裹层的 classstring|array|object-1.0.0
content-flex内容层是否开启 flex 布局booleantrue1.0.0
label-col-flex设置标签 Col 组件的 flex 属性。设置时表单 Col 组件的 flex 属性会被设置为 autonumber|string-1.0.0
feedback是否显示表单控件的反馈图标booleanfalse1.0.0
label-component表单项标签渲染的元素string'label'1.0.0
label-attrs表单项元素的属性object-1.0.0

<form-item> Slots

插槽名描述参数
label标签-
prefix前缀元素-
suffix后缀元素-
help帮助信息-
extra额外内容-

Type

FieldRule

参数名描述类型默认值
type校验的值的类型,默认为 'string''string' | 'number' | 'boolean' | 'array' | 'object' | 'email' | 'url' | 'ip'-
required是否必填booleanfalse
message校验失败时展示的信息string-
length校验长度(string, array)number-
maxLength最大长度(string)number-
minLength最小长度(string)number-
match匹配校验(string)RegExp-
uppercase大写(string)booleanfalse
lowercase小写(string)booleanfalse
min最小值(number)number-
max最大值(number)number-
equal校验数值(number)number-
positive正数(number)booleanfalse
negative负数(number)booleanfalse
true是否为 true(boolean)booleanfalse
false是否为 false(boolean)booleanfalse
includes数组中是否包含给定值(array)any[]-
deepEqual数组元素是否相等(array)any-
empty是否为空(object)booleanfalse
hasKeys对象是否包含给定属性(object)string[]-
validator自定义校验规则(value: FieldValue | undefined, callback: (error?: string) => void) => void-

FieldData

参数名描述类型默认值
value字段的值any-
status字段的状态ValidateStatus-
message字段的错误信息string-

ValidatedError

参数名描述类型默认值版本
label标签的文本string-1.0.0
field字段名string-
value字段值any-
type字段类型string-
isRequiredError是否为 required 错误booleanfalse
message错误信息string-

FormItemEventHandler

参数名描述类型默认值
onChangeonChange(ev?: Event) => void-
onInputonInput(ev?: Event) => void-
onFocusonFocus(ev?: Event) => void-
onBluronBlur(ev?: Event) => void-

useFormItem

ts
const useFormItem = (data: {
  size?: Ref<Size | undefined>;
  disabled?: Ref<boolean>;
  error?: Ref<boolean>;
}) => {
  mergedSize:Ref<Size>;
  mergedDisabled:Ref<boolean>;
  mergedError:Ref<boolean>;
  feedback:Ref<string>;
  eventHandlers:Ref<FormItemEventHandler>;
}

FAQ

关于 form-itemfield 属性

field 属性的值为获取当前 form-item 对应值的路径字符串。

例如传入 model 属性的数据结构为:

ts
const data = reactive({
  name:'xiaoming',
  people:[
    {
      id:'1111'
    },
    {
      // bind this value
      id:'2222'
    }
  ]
})

此时,如果想要指定当前 form-item 对应的值为 id: '2222',需要设置 field="people.2.id",值中的分隔符需要使用 .。数组分割也可以使用 [],例如 field="people[2].id"

关于在 label 插槽中使用可点击元素

表单组件的标题区域默认使用 label 元素包裹,会在点击时激活输入组件,如果在其中放入可以点击组件,会影响其正常功能。 此时可以使用 label-component 属性修改包裹元素为 span 解决这个问题。


基于 MIT 许可发布