Skip to content

Form 表单

表单封装,通过配置生成表单。

TIP

如果表单项组件支持placeholderclearablefilterable等属性,会被默认配置

基础用法

  • 传入columns定义表单,modelValue为表单数据,options为数据配置项,component为表单项组件,field为字段名, fieldProps为组件属性配置,formItemProps为表单项装饰组件属性配置
  • FormItem组件属性(表单项装饰组件)属性配置在formItemProps字段中,有些字段为了方便使用,直接配置在column项中也可生效(如:labelrequiredmessagerule等)
  • 事件使用on+事件名
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElInput } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  time: [],
})

const options = {
  gender: [
    { label: '男', value: '1' },
    { label: '女', value: '2' },
  ],
}

const columns = [
  {
    component: ElInput,
    field: 'name',
    label: '姓名',
    onInput: (val: string) => {
      console.log(val, 'input event')
    },
    onChange: (val: string) => {
      console.log(val, 'change event')
    },
    required: true,
  },
  {
    component: 'select',
    field: 'gender',
    label: '性别',
    onChange: (val: string) => {
      console.log(val, 'change event')
    },
    onFocus: () => {
      console.log('focus event')
    },
  },
  {
    component: 'el-date-picker',
    field: 'time',
    label: '出生日期',
    fieldProps: {
      type: 'daterange',
      startPlaceholder: '开始日期',
      endPlaceholder: '结束日期',
    },
    onChange: (val: string[]) => {
      console.log(val, 'change event')
    },
  },
  {
    slot: 'button',
  },
]

function reset() {
  formRef.value.resetFields()
}

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid)
      console.log(formData.value, 'config.formData')

    else
      console.log('error')
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="80px"
    size="small"
  >
    <template #button>
      <div class="w-full flex">
        <el-button class="w-full" @click="reset">
          重置
        </el-button>
        <el-button type="primary" class="w-full" @click="submit">
          提交
        </el-button>
      </div>
    </template>
  </z-form>
</template>

列布局

表单传入column,支持1 ~ 3列布局,默认为1列布局。

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  age: '',
})

const options = {
  gender: [
    { label: '男', value: '1' },
    { label: '女', value: '2' },
  ],
}

const columns = [
  {
    component: 'input',
    field: 'name',
    label: '姓名',
    required: true,
  },
  {
    component: 'select',
    field: 'gender',
    label: '性别',
  },
  {
    component: 'input',
    label: '年龄',
    field: 'age',
  },
  {
    slot: 'button',
  },
]

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid) {
      ElMessage.success('success')
      console.log(formData.value, 'formData.value')
    }
    else {
      console.log('error')
    }
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="90px"
    size="small"
    :column="2"
  >
    <template #button>
      <el-button class="w-full" type="primary" @click="submit">
        提交
      </el-button>
    </template>
  </z-form>
</template>

自定义布局

如果列布局不满足要求,可以在column项中配置spanoffsetpullpushxssmmdlgxl实现自定义响应式布局

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  age: '',
})

const options = {
  gender: [
    { label: '男', value: '1' },
    { label: '女', value: '2' },
  ],
}

const columns = [
  {
    component: 'input',
    field: 'name',
    label: '姓名',
    required: true,
    span: 24,
  },
  {
    component: 'select',
    field: 'gender',
    label: '性别',
    span: 12,
  },
  {
    component: 'input',
    label: '年龄',
    field: 'age',
    span: 12,
  },
  {
    span: 24,
    slot: 'button',
  },
]

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid) {
      ElMessage.success('success')
      console.log(formData.value, 'formData.value')
    }
    else {
      console.log('error')
    }
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="90px"
    size="small"
  >
    <template #button>
      <el-button class="w-full" type="primary" @click="submit">
        提交
      </el-button>
    </template>
  </z-form>
</template>

提示

column传入tooltipextracolon,可以配置提示、额外信息和冒号。tooltipextra支持字符串和render函数配置。 表单传入colon,可以统一配置所有表单项冒号。

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { h, ref } from 'vue'
import { ElMessage } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  age: '',
})

const options = {
  gender: [
    { label: '男', value: '1' },
    { label: '女', value: '2' },
  ],
}

const columns = [
  {
    component: 'input',
    field: 'name',
    label: '姓名',
    tooltip: '这是姓名',
    extra: 'extraSlot',
    required: true,
  },
  {
    component: 'select',
    field: 'gender',
    label: '性别',
    colon: false,
    tooltip: () => h('span', {}, '性别提示'),
    required: true,
  },
  {
    component: 'input',
    label: '年龄',
    field: 'age',
    formItemProps: {
      tooltip: '这是年龄',
      extra: () => h('span', {}, '这是额外信息'),
      colon: false,
    },
  },
  {
    slot: 'button',
  },
]

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid) {
      ElMessage.success('success')
      console.log(formData.value, 'formData.value')
    }
    else {
      console.log('error')
    }
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="90px"
    size="small"
    :column="2"
    colon
  >
    <template #extraSlot>
      <span>提示提示提示提示提示提示提示提示提示提示提示提示提示提示提示提示提示提示提示</span>
    </template>
    <template #button>
      <el-button class="w-full" type="primary" @click="submit">
        提交
      </el-button>
    </template>
  </z-form>
</template>

tooltip支持传入render函数和插槽自定义。

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { h, ref } from 'vue'
import { ElMessage } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  age: '',
})

const options = {
  gender: [
    { label: '男', value: '1' },
    { label: '女', value: '2' },
  ],
}

const columns = [
  {
    component: 'input',
    field: 'name',
    label: '姓名',
    tooltip: {
      reference: () => h('span', {}, '?'),
      content: '姓名提示',
    },
    extra: 'extraSlot',
    required: true,
  },
  {
    component: 'select',
    field: 'gender',
    label: '性别',
    colon: false,
    tooltip: () => h('span', {}, '性别提示'),
    required: true,
  },
  {
    component: 'input',
    label: '年龄',
    field: 'age',
    formItemProps: {
      tooltip: {
        reference: 'tooltipSlot',
        content: () => h('span', {}, '自定义年龄提示'),
      },
      colon: false,
    },
  },
  {
    slot: 'button',
  },
]

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid) {
      ElMessage.success('success')
      console.log(formData.value, 'formData.value')
    }
    else {
      console.log('error')
    }
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="90px"
    size="small"
    :column="2"
    colon
  >
    <template #tooltipSlot>
      <el-icon class="cursor-pointer">
        <i-delete />
      </el-icon>
    </template>
    <template #button>
      <el-button class="w-full" type="primary" @click="submit">
        提交
      </el-button>
    </template>
  </z-form>
</template>

校验

  • columns表单项中添加required字段,或者formItemProps中设置required字段,即可设置必填,校验信息会根据label自动生成也可自定义。
  • form传入rules字段,可以定义表单校验规则。
  • columns表单项配置rules,可以定义当前表单项校验规则。
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  age: '',
  hobby: '',
})

const options = {
  gender: [
    { label: '男', value: '1' },
    { label: '女', value: '2' },
  ],
}

const rules = {
  age: [{ required: true, message: '年龄必填' }],
}

const columns = [
  {
    component: 'input',
    field: 'name',
    label: '姓名',
    required: true,
  },
  {
    component: 'select',
    field: 'gender',
    formItemProps: {
      required: true,
      label: '性别',
    },
  },
  {
    component: 'input',
    label: '年龄',
    field: 'age',
  },
  {
    component: 'input',
    label: '爱好',
    field: 'hobby',
    rules: {
      required: true,
      message: '请填写爱好',
    },
  },
  {
    slot: 'button',
  },
]

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid) {
      ElMessage.success('success')
      console.log(formData.value, 'formData.value')
    }
    else {
      console.log('error')
    }
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="90px"
    size="small"
    :rules="rules"
    :column="3"
  >
    <template #button>
      <el-button class="w-full" type="primary" @click="submit">
        提交
      </el-button>
    </template>
  </z-form>
</template>

联动

column表单项传入hide(支持布尔值或函数类型),可以配置表单项联动隐藏。

某些业务中需要使用v-show决定组件显隐,column表单项传入字段show(支持布尔值或函数类型)即可。

姓名输入框有值,会展示性别选择框,然后试试选中性别,年龄表单项会出现。

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  age: '',
})

const options = {
  gender: [
    { label: '男', value: '1' },
    { label: '女', value: '2' },
  ],
}

const columns = [
  {
    component: 'input',
    field: 'name',
    label: '姓名',
    required: true,
  },
  {
    component: 'select',
    field: 'gender',
    label: '性别',
    show: (formData: any) => formData.name,
  },
  {
    component: 'input',
    label: '年龄',
    hide: formData => formData.gender === '1' || !formData.gender,
    field: 'age',
  },
  {
    slot: 'button',
  },
]

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid) {
      ElMessage.success('success')
      console.log(formData.value, 'formData.value')
    }
    else {
      console.log('error')
    }
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="90px"
    size="small"
    :column="2"
  >
    <template #button>
      <el-button class="w-full" type="primary" @click="submit">
        提交
      </el-button>
    </template>
  </z-form>
</template>

表单项自定义

我们可以使用slotrender自定义表单项内容。请仍然传入field字段,否则重置无法清空数据(field字段不传,默认取slot字段)。

<script lang="ts" setup>
import { h, ref } from 'vue'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  age: '',
})

const options = {
  gender: [
    { label: '男', value: '1' },
    { label: '女', value: '2' },
  ],
}

const columns = [
  {
    component: 'input',
    field: 'name',
    label: '姓名',
    required: true,
  },
  {
    slot: 'gender',
    field: 'gender',
    label: '性别',
  },
  {
    label: '年龄',
    render: () => h('span', {}, '年龄内容'),
  },
]
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :columns="columns"
    label-width="90px"
    size="small"
  >
    <template #gender>
      <z-select v-model="formData.gender" :options="options.gender" clearable filterable placeholder="请选择性别" />
    </template>
  </z-form>
</template>

label、error自定义

labelerror支持传入字符串、render函数或拼接Slot的字符串

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { h, ref } from 'vue'
import { ElMessage } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  age: '',
})

const options = {
  gender: [
    { label: '男', value: '1' },
    { label: '女', value: '2' },
  ],
}

const columns = [
  {
    component: 'input',
    field: 'name',
    label: () => h('span', {}, '姓名'),
    error: 'error message',
    required: true,
  },
  {
    component: 'select',
    field: 'gender',
    label: 'labelSlot',
    required: true,
    error: h('span', {}, 'errorSlot'),
  },
  {
    component: 'input',
    field: 'age',
    label: '年龄',
    required: true,
    error: 'errorSlot',
  },
  {
    slot: 'button',
  },
]

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid) {
      ElMessage.success('success')
      console.log(formData.value, 'formData.value')
    }
    else {
      console.log('error')
    }
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :columns="columns"
    :options="options"
    label-width="90px"
    size="small"
  >
    <template #labelSlot>
      <span>性别</span>
    </template>
    <template #errorSlot>
      <span>年龄必填</span>
    </template>
    <template #button>
      <el-button class="w-full" type="primary" @click="submit">
        提交
      </el-button>
    </template>
  </z-form>
</template>

表单项顺序

表单项传入order,支持自定义顺序,不传则默认按顺序放最后

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElInput } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  time: [],
})

const options = {
  gender: [
    { label: '男', value: '1' },
    { label: '女', value: '2' },
  ],
}

const columns = [
  {
    component: 'el-input',
    field: 'name',
    label: '地址',
  },
  {
    component: ElInput,
    field: 'name',
    label: '姓名',
    order: 2,
  },
  {
    component: 'select',
    field: 'gender',
    label: '性别',
    order: 3,
  },
  {
    component: 'el-date-picker',
    field: 'time',
    label: '出生日期',
    order: 1,
  },
  {
    slot: 'button',
  },
]

function reset() {
  formRef.value.resetFields()
}

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid)
      console.log(formData.value, 'config.formData')

    else
      console.log('error')
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="80px"
    size="small"
  >
    <template #button>
      <div class="w-full flex">
        <el-button class="w-full" @click="reset">
          重置
        </el-button>
        <el-button type="primary" class="w-full" @click="submit">
          提交
        </el-button>
      </div>
    </template>
  </z-form>
</template>

表格结合

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'

interface RowData {
  name: string
  address: string
}

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  time: [],
  tableData: [{ name: 'steven', address: '' }],
})

const options = {
  gender: [
    { label: '男', value: '1' },
    { label: '女', value: '2' },
  ],
}

const columns = computed(() => [
  {
    component: 'el-input',
    field: 'name',
    label: '姓名',
    onInput: (val: string) => {
      console.log(val, 'input event')
    },
    onChange: (val: string) => {
      console.log(val, 'change event')
    },
    required: true,
  },
  {
    'component': 'z-table',
    'field': 'tableData',
    'label': '表格',
    'onUpdate:data': (data: RowData[]) => {
      console.log(data, 'data')
      formData.value.tableData = data
    },
    'fieldProps': {
      data: formData.value.tableData,
      toolBar: false,
      columns: [
        {
          label: '名称',
          prop: 'name',
        },
        {
          component: 'el-input',
          label: '地址',
          prop: 'address',
        },
      ],
    },
  },
  {
    slot: 'button',
  },
])

function reset() {
  formRef.value.resetFields()
}

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid)
      console.log(formData.value, 'config.formData')

    else
      console.log('error')
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="80px"
    size="small"
  >
    <template #button>
      <div class="w-full flex">
        <el-button class="w-full" @click="reset">
          重置
        </el-button>
        <el-button type="primary" class="w-full" @click="submit">
          提交
        </el-button>
      </div>
    </template>
  </z-form>
</template>

z-form属性

属性名说明类型默认
modelValue表单数据对象object
type表单类型normal / group / collapse / array / stepnormal
rules表单验证规则object
columns表单项array
options表单选择项数据源object
column表单列数(便捷布局)number1
colon表单项冒号booleanfalse
max数组项最大数量(typearray生效)number
gutter栅格间隔number0
justifyflex布局下的水平排列方式'start''end'
alignflex布局下的垂直排列方式top / middle /bottom
v-model:activeCollapse展开的collapse项(typecollapse生效)array
label-position表单域标签的位置, 当设置为 leftright 时,则也需要设置 label-width 属性enumright
label-width标签的长度,例如 '50px'。 作为 Form 直接子元素的 form-item 会继承该值。 可以使用 autostring / number''
label-suffix表单域标签的后缀string''
hide-required-asterisk是否隐藏必填字段标签旁边的红色星号。booleanfalse
require-asterisk-position星号的位置。left / rightleft
show-message是否显示校验错误信息booleantrue
inline-message是否以行内形式展示校验信息booleanfalse
status-icon是否在输入框中显示校验结果反馈图标booleanfalse
validate-on-rule-change是否在 rules 属性改变后立即触发一次验证booleantrue
size用于控制该表单内组件的尺寸large / default / small
disabled是否禁用该表单内的所有组件。 如果设置为 true, 它将覆盖内部组件的 disabled 属性booleanfalse
scroll-to-error当校验失败时,滚动到第一个错误表单项booleanfalse
scroll-into-view-options当校验有失败结果时,滚动到第一个失败的表单项目object / boolean

group表单属性

属性名说明类型默认值
border-style设置分隔符样式none / solid / hidden / dashedsolid
content-position自定义分隔线内容的位置left / right / centercenter

array表单属性

属性名说明类型默认值
max最大表单项数量number

step表单属性

属性名说明类型默认值
process-status设置当前步骤的状态wait / process / finish / error / successprocess
finish-status设置结束步骤的状态wait / process / finish / error / successfinish
align-center居中对齐booleantrue

z-form方法

名称说明类型
validate对整个表单的内容进行验证。 接收一个回调函数,或返回 PromiseFunction
validateField验证具体的某个字段。Function
resetFields重置该表单项,将其值重置为初始值,并移除校验结果Function
scrollToField滚动到指定的字段Function
clearValidate清理某个字段的表单验证信息。Function

z-form 事件

事件名说明类型
update:modelValue表单项数据Function
update:activeCollapse折叠表单的展开项Function
update:activeStep步骤表单的当前步骤Function
collapse-change折叠表单折叠项改变时触发Function
next-step步骤表单点击下一步触发Function
previous-step步骤表单点击上一步触发Function

通常表单column属性

属性名说明类型默认值
component表单项组件input / checkbox / select / radio / 任意局部注册或全局注册组件
field字段名string
fieldPropscomponent组件属性object
formItemPropsformItem组件属性object
label表单标签名string / () => VNode
order表单排序number
hide显隐boolean / (formData) => boolean
show使用v-show显隐boolean / (formData) => boolean
slot表单项自定义内容插槽string
render表单项自定义内容render() => VNode
required表单项是否必填boolean
rules该表单项校验规则boolean
error错误信息string / () => VNode
tooltip提示信息string / () => VNode
extra额外信息string / () => VNode
children表单折叠模式下生效,column数组array
span占据的单元格number
offset左侧的间隔格数number
pull向左移动格数number
push向右移动格数boolean
xs<768px 响应式栅格数或者栅格属性对象number / object
sm≥768px 响应式栅格数或者栅格属性对象number / object
md≥992px 响应式栅格数或者栅格属性对象number / object
lg≥1200px 响应式栅格数或者栅格属性对象number / object
xl≥1920px 响应式栅格数或者栅格属性对象number / object

array表单column属性

属性名说明类型默认值
label标签名string / () => VNode
field字段名string
max最大表单项数量number
children当前步骤中的表单项(属性都为通常column属性)array

step表单column属性

属性名说明类型默认值
label步骤标题string / () => VNode
icon表单step模式生效,图标string / Component
description表单step模式生效,描述boolean
status表单step模式生效,步骤条状态wait / process / finish / error / success
children当前步骤中的表单项(属性都为通常column属性)array

formItemProps属性

属性名说明类型Default
tooltip提示文案string / () => VNode
extra额外信息string / () => VNode
colon冒号boolean
label标签文本string / () => VNode
label-width标签宽度,例如 '50px'。 可以使用 autostring / number''
required是否为必填项,如不设置,则会根据校验规则确认boolean
rules表单验证规则, 具体配置见下表, 更多内容可以参考async-validatorobject
error表单域验证错误时的提示信息。设置该值会导致表单验证状态变为 error,并显示该错误信息。string / () => VNode
show-message是否显示校验错误信息booleantrue
inline-message是否在行内显示校验信息string / boolean''
size用于控制该表单域下组件的默认尺寸large / default / small
for和原生标签相同能力string
validate-statusformItem 校验的状态error / validating / success

Released under the MIT License.