Skip to content

naive-ui-form文档

更新日志

v3.1.0

  • 修改富文本为naive-ui-editor

简介

INFO

naive-ui-form是基于naive-ui封装的表单组件。一个配置,生成一个表单。支持typescript

naive-ui-form相比于使用n-form原生表单有如下优势:

  • 无需手动布局,组件内部使用n-grid自动布局,只需传入props参数即可,灵活配置。
  • 无需手写校验rules,大部分场景下只需要传一个required:true就可以了。
  • 支持动态表单,减少开发时间和复杂度。
  • 内置了文件上传(支持图片裁剪)富文本等插件,方便开发
  • 完整的ts类型提示

下面是naive-ui-form的一个完整表单的示例:

vue
<template>
  <BasicForm @register="register"></BasicForm>
</template>

<script setup lang="tsx">
import { BasicForm, useForm, type Recordable } from 'naive-ui-form'

const [register] = useForm({
  schemas: [
    {
      field: 'name',
      type: 'input',
      label: '姓名',
      required: true,
      labelPlacement: 'left',
      defaultValue: '张三',
      componentProps: {
        onUpdateValue(value: string) {
          console.log(value)
        }
      },
      style: {
        width: '200px'
      }
    },
    {
      field: 'sex',
      type: 'radio',
      label: '性别',
      required: true,
      componentProps: {
        options: [
          {
            label: '男',
            value: 'male'
          },
          {
            label: '女',
            value: 'female'
          }
        ]
      }
    },
    {
      field: 'hobbies',
      type: 'checkbox',
      label: '爱好',
      required: true,
      requiredType: 'array',
      componentProps: {
        options: [
          {
            label: '吃饭',
            value: 'eat'
          },
          {
            label: '睡觉',
            value: 'sleep'
          },
          {
            label: '打豆豆',
            value: 'play'
          }
        ]
      }
    },
    {
      field: 'birthday',
      type: 'date-picker',
      label: '生日'
    },
    {
      field: 'family',
      label: '家庭成员',
      type: 'dynamic',
      dynamicOptions: [
        {
          field: 'name',
          label: '姓名',
          type: 'input',
          required: true
        },
        {
          field: 'age',
          label: '年龄',
          type: 'input-number',
          rules: {
            required: true,
            message: ''
          }
        },
        {
          field: 'sex',
          label: '性别',
          type: 'radio',
          componentProps: {
            options: [
              {
                label: '男',
                value: 'male'
              },
              {
                label: '女',
                value: 'female'
              }
            ]
          }
        }
      ]
    }
  ]
})
</script>

<style scoped></style>

安装

shell
pnpm add naive-ui-form

也可以使用npmyarn等安装。

注意

必须安装了以下依赖才能使用naive-ui-form

json
{
  "vue": ">=3.2.0",
  "naive-ui": ">=2.34.0",
  "@vicons/ionicons5": ">=0.12.0"
}

确保naive-ui已经配置到项目中:

vue
<template>
  <NConfigProvider :locale="zhCN" :date-locale="dateZhCN">
    <NMessageProvider>
      <router-view></router-view>
    </NMessageProvider>
  </NConfigProvider>
</template>

<script setup lang="ts">
import { NConfigProvider, zhCN, dateZhCN, NMessageProvider } from 'naive-ui'
</script>

<style scoped></style>

提示

  • 如果表单中使用了type: 'upload',则需要安装naive-ui-upload,并注册该组件。
  • 如果表单中使用了type: 'editor',则需要安装naive-ui-editor,并注册该组件。

基本使用

局部导入

vue
<template>
  <BasicForm></BasicForm>
</template>

<script setup lang="ts">
import { BasicForm } from 'naive-ui-form'
</script>

全局导入

ts
import { createApp } from 'vue'
import NaiveUiForm from 'naive-ui-form'

const app = createApp(App)

app.use(NaiveUiForm)

props传值方式

传递props有两种方法:

  1. 使用经典传值方式:

    vue
    <template>
      <BasicForm :schemas="schemas"></BasicForm>
    </template>
  2. 使用useForm

    vue
    <template>
      <BasicForm @register="register"></BasicForm>
    </template>
    
    <script setup lang="ts">
    import { BasicForm, useForm } from 'naive-ui-form'
    
    const [register] = useForm({
      schemas: []
    })
    </script>

提示

如果propsuseForm()传值有冲突,useForm会覆盖props

注册事件

naive-ui-form注册了以下事件:

  • register: 使用useForm注册的时候用到
  • submit:表单提交出发(只有表单校验成功后才会触发
  • reset:表单重置的时候触发

useForm

naive-ui-form为了方便对表单的操作,封装了一些方法。

注意

在使用这些方法之前,一定要先注册registernaive-ui-form

vue
<template>
  <BasicForm @register="register"></BasicForm>
</template>

<script setup lang="ts">
import { BasicForm, useForm } from 'naive-ui-form'

const [register, { submit }] = useForm()
</script>
ts
interface FormInstance {
  // 重置表单
  reset(): void
  // 提交表单,返回Promise<Recordable>,校验通过后返回表单的值,校验失败后返回校验的Error
  submit(): Promise<Recordable>
  // 校验表单,返回Primise,校验通过后进入resolve状态
  validate(nameList?: string[]): Promise<any>
  // 清空校验
  clearValidate(): void
  // 获取表单的值
  getValue(): Recordable
  // 获取表单某个项的值
  getFieldValue(field: string): any
  // 设置表单的值
  setValue(value: Recordable): void
  // 动态设置表单的Props
  setProps(props: Props): void
  // 手动设置表单提交按钮的加载状态
  setLoading(loading: boolean): void
}
vue
<template>
  <div>
    <BasicForm @register="register" :show-action-btns="false"></BasicForm>
    <NSpace>
      <NButton type="primary" @click="handleSubmit">提交</NButton>
      <NButton type="primary" @click="handleReset">重置</NButton>
      <NButton type="primary" @click="handleVerify">校验</NButton>
      <NButton type="primary" @click="handleClearVerify">清空校验</NButton>
      <NButton type="primary" @click="handleGetValue">获取表单值</NButton>
      <NButton type="primary" @click="handleGetFieldValue">获取某个值</NButton>
      <NButton type="primary" @click="handleSetValue">手动赋值</NButton>
    </NSpace>
  </div>
</template>

<script setup lang="tsx">
import { NSpace, NButton } from 'naive-ui'
import { BasicForm, useForm, type Recordable } from 'naive-ui-form'

const [
  register,
  { submit, reset, validate, clearValidate, getValue, getFieldValue, setValue, setProps }
] = useForm({
  schemas: [
    {
      field: 'name',
      type: 'input',
      label: '姓名',
      required: true,
      labelPlacement: 'left',
      defaultValue: '张三',
      componentProps: {
        onUpdateValue(value: string) {
          console.log(value)
        }
      },
      style: {
        width: '200px'
      }
    },
    {
      field: 'sex',
      type: 'radio',
      label: '性别',
      required: true,
      componentProps: {
        options: [
          {
            label: '男',
            value: 'male'
          },
          {
            label: '女',
            value: 'female'
          }
        ]
      }
    },
    {
      field: 'hobbies',
      type: 'checkbox',
      label: '爱好',
      required: true,
      requiredType: 'array',
      componentProps: {
        options: [
          {
            label: '吃饭',
            value: 'eat'
          },
          {
            label: '睡觉',
            value: 'sleep'
          },
          {
            label: '打豆豆',
            value: 'play'
          }
        ]
      }
    },
    {
      field: 'birthday',
      type: 'date-picker',
      label: '生日'
    },
    {
      field: 'school',
      type: 'custom',
      label: '学校',
      required: true,
      render(formValue: Recordable, field: string) {
        return <input v-model={formValue[field]} style={{ border: '1px solid #ccc' }} />
      }
    }
  ]
})

function handleSubmit() {
  submit()
    .then((res: Recordable) => {
      console.log(`表单校验成功`)
      console.log(res)
    })
    .catch((err) => {
      console.log('表单校验失败失败', err)
    })
}

function handleReset() {
  reset()
  console.log('表单已重置')
}

function handleVerify() {
  validate()
    .then(() => console.log('校验通过'))
    .catch(() => console.log('校验失败'))
}

function handleClearVerify() {
  clearValidate()
  console.log('校验已清空')
}

function handleGetValue() {
  console.log(getValue())
}

function handleGetFieldValue() {
  console.log(getFieldValue('name'))
}

function handleSetValue() {
  setValue({
    name: '李四',
    school: '北京大学',
    sex: 'male',
    hobbies: ['eat', 'sleep'],
    birthday: '2000-01-01'
  })
}
</script>

<style scoped></style>

组件api

如果不想使用useForm,也可以直接通过naive-form-ui组件自身调用上面的方法:

vue
<template>
  <div>
    <BasicForm ref="formRef" :schemas="schemas" :show-action-btns="false"></BasicForm>
    <NSpace>
      <NButton type="primary" @click="handleSubmit">提交</NButton>
      <NButton type="primary" @click="handleReset">重置</NButton>
    </NSpace>
  </div>
</template>

<script setup lang="ts">
import { NSpace, NButton } from 'naive-ui'
import { BasicForm, type FormSchema, type FormInstance } from 'naive-ui-form'
import { ref } from 'vue'
const formRef = ref<FormInstance | null>(null)
const schemas: FormSchema[] = [
  {
    field: 'name',
    label: '姓名',
    type: 'input',
    required: true
  }
]

function handleSubmit() {
  formRef.value?.submit()
}

function handleReset() {
  formRef.value?.reset()
}
</script>

<style scoped></style>

props说明

字段类型描述必传默认值
schemasFormSchema[]表单配置,详见schemas-
gridObject<n-grid>的props,详见n-grid-
showActionBtnsBoolean是否展示表单的操作按钮(提交、重置、展开),优先级最高true
showSubmitBtnBoolean是否展示提交按钮true
submitBtnTextString提交按钮文字"提交"
showResetBtnBoolean是否展示重置按钮true
resetBtnTextString重置按钮文字"重置"
showExpandBtnBoolean是否展示展开/折叠按钮true
expandBtnOffTextString”折叠“状态时候的文字”展开“
expandBtnOnTextString”展开“状态时候的文字”收起“
defaultExpandBoolean是否默认折叠false
defaultShowExpandColumnNumber默认展开的行数1

提示

支持传入n-form的props。组件内部重写了部分props,即使你手动传入也是无效的,如modelrules

schemas

schemasnaive-ui-form中重要的配置,如果你使用了typescript,你可以从naive-ui-form中导入FormSchema 类型

vue
<template>
  <BasicForm @register="register"></BasicForm>
</template>

<script setup lang="ts">
import { BasicForm, useForm, type FormSchema } from 'naive-ui-form'

const schemas: FormSchema[] = []

const [register] = useForm({
  schemas
})
</script>

FormSchema字段说明

字段类型描述是否必填
tipstring() => VNode填写自此字段会在表单下面生成一句提示信息
fieldstring整个表单的值是一个对象,该字段就是描述需要v-model到该字段上,如该字段设置为"name",那么表单的值就是
typestringtype字段说明
defaultValueany该项的默认值
componentPropsObjectnaive-ui原生表单项(如n-inputn-select等)的props,通过该字段传入
requiredboolean该项是否必填,如果设置为true组件内部会自动校验必填,优先级最高
requiredTypestringrequiredtrue时校验的特殊类型,如arraynumber
requiredMessagestringrequiredtrue校验未通过的信息
requiredTriggerstringstring[]requiredtrue校验的出发方式
rulesFormItemRuleFormItemRule[]requiredtrue时无效,自定义表单校验,同n-form-itemrule属性
styleObject设置n-form-item的style
vif(value:Recordable) => boolean动态显示该表单,需要返回一个布尔值,value是表单的值
slotstringtype字段为slot的时候必传,插槽名称
dynamicOptionsFormSchema[]动态表单的配置,当type字段为dynamic的时候必传
groupNamestring表单分组标识,该选项设置会了该表单项前面添加一个分组标识

提示

FormSchema也接收NFormItemGi的props。

type字段说明

type字段映射了naive-ui的表单组件,映射关系如下:

字段映射的组件
auto-completeNAutoComplete
cascaderNCascader
color-pickerNColorPicker
checkboxNCheckboxGroup
date-pickerNDatePicker
dynamic-inputNDynamicInput
dynamic-tagsNDynamicTags
inputNInput
input-numberNInputNumber
mentionNMention
radio-singleNRadio
radioNRadioGroup
rateNRate
selectNSelect
sliderNSlider
switchNSwitch
time-pickerNTimePicker
transferNTransfer
tree-selectNTreeSelect
slot插槽
dynamic动态表单
uploadnaive-ui-upload
editornaive-ui-editor
textNText,用于数据展示

提示

一些特殊的type说明:

  • radio: 需要在componentProps传入options字段,如果是异步数据可以定义为ref响应数据。

    ts
        {
          field: 'sex',
          type: 'radio',
          label: '性别',
          required: true,
          componentProps: {
            options: [
              {
                label: '男',
                value: 'male'
              },
              {
                label: '女',
                value: 'female'
              }
            ]
          }
        }
  • checkbox:同上radio

  • date-picker: 默认的格式为yyyy-MM-dd,格式请参考这里修改方式如下:

    ts
        {
          field: 'birthday',
          type: 'date-picker',
          label: '生日',
          componentProps: {
            valueFormat: 'xxxxx'
          }
        }
  • time-picker: 默认的格式为HH:mm:ss,修改同上。

插槽

如果内置的type不满足时,可以使用插槽。type设置为slot, 再加一个slot名称,示例如下:

vue
<template>
  <BasicForm @register="register">
    <template #username="{ formValue, field }">
      <input
        v-model="formValue[field]"
        type="text"
        placeholder="这是插槽"
        style="border: 1px solid #ccc"
      />
    </template>
  </BasicForm>
</template>

<script setup lang="ts">
import { BasicForm, useForm } from 'naive-ui-form'

const [register] = useForm({
  schemas: [
    {
      field: 'username',
      type: 'slot',
      label: '用户名',
      required: true,
      slot: 'username'
    }
  ]
})
</script>

<style scoped></style>

动态表单

提示

在使用动态表单时候,由于naive-uin-form组件设置了label-width: "auto"后,导致子组件无法自适应宽度,所以需要设置naive-ui-formlabel-width不为auto,否则会出现宽度不协调的问题。

动态表单需要将type 设置为dynamic,再新增一个dynamicOptions配置,示例如下:

vue
<template>
  <BasicForm @register="register"></BasicForm>
</template>

<script setup lang="ts">
import { BasicForm, useForm } from 'naive-ui-form'

const [register] = useForm({
  labelWidth: '200px',
  schemas: [
    {
      field: 'family',
      label: '家庭成员',
      type: 'dynamic',
      dynamicOptions: [
        {
          field: 'name',
          label: '姓名',
          type: 'input',
          required: true
        },
        {
          field: 'age',
          label: '年龄',
          type: 'input-number',
          required: true
        }
      ]
    }
  ]
})
</script>

<style scoped></style>

ModalForm

ModalFormBasicForm的封装,主要是为了方便在弹窗中使用。可以直接传入BasicFormn-modaldialog支持的propsModalForm会在校验通过后出发submit事件,参数就是表单的值。点击取消按钮时还会触发cancel事件。

vue
<template>
  <div>
    <NButton type="primary" @click="showModal = true">显示弹窗</NButton>
    <ModalForm
      v-model:show="showModal"
      :schemas="schemas"
      title="新增用户"
      :loading="loading"
      @submit="handleSubmit"
    ></ModalForm>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { NButton } from 'naive-ui'
import { ModalForm, type FormSchema, type Recordable } from 'naive-ui-form'

const showModal = ref(false)
const schemas: FormSchema[] = [
  {
    field: 'name',
    label: '姓名',
    type: 'input',
    required: true
  },
  {
    field: 'age',
    label: '年龄',
    type: 'input-number',
    required: true,
    requiredType: 'number'
  }
]

const loading = ref(false)
function handleSubmit(values: Recordable) {
  console.log(values)
  loading.value = true
  setTimeout(() => {
    loading.value = false
    showModal.value = false
  }, 2000)
}
</script>

<style scoped></style>

有时候需要获取整个表单值去做一些额外的事情,如表单校验。ModalForm组件暴露了getValuesetValuegetFieldValue方法:

vue
<template>
  <ModalForm
    v-model:show="showModal"
    title="新增"
    ref="modalRef"
    :schemas="schemas"
    style="width: 800px"
    @submit="handleModalSubmit"
  ></ModalForm>
</template>

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

import { type FormSchema, type ModalFormInstance } from 'naive-ui-form'

const showModal = ref(true)
const schemas: FormSchema[] = [
  {
    field: 'name',
    type: 'input',
    label: '姓名',
    required: true,
    labelPlacement: 'left',
    defaultValue: '张三',
    componentProps: {
      onUpdateValue(value: string) {
        console.log(value)
      }
    },
    style: {
      width: '200px'
    }
  }
]

const modalRef = ref<ModalFormInstance | null>(null)
setTimeout(() => {
  console.log(modalRef.value?.getValue())
}, 3000)
</script>

<style scoped></style>