Skip to content

ProFormFields

Nineteen standalone field components give regular forms, custom layouts, ProTable search and inline editing, and SchemaForm one value-binding, option-request, and readonly-display protocol. A field includes an Antdv Next FormItem by default; use fieldMode="field" when only the control should render.

Complete field demo

This Vue SFC renders all 19 public fields and demonstrates readonly state, async options, the captcha callback, upload entry points, and bare-control mode.

View complete source
vue
<script setup lang="ts">
import type { UploadFile } from 'antdv-next'

import { Form } from 'antdv-next'
import { computed, reactive, ref } from 'vue'
import { useData } from 'vitepress'
import {
  ProFormCaptcha,
  ProFormCheckbox,
  ProFormDatePicker,
  ProFormDateRangePicker,
  ProFormDateTimePicker,
  ProFormDateTimeRangePicker,
  ProFormDigit,
  ProFormMoney,
  ProFormRadioGroup,
  ProFormSegmented,
  ProFormSelect,
  ProFormSlider,
  ProFormSwitch,
  ProFormText,
  ProFormTextArea,
  ProFormTextPassword,
  ProFormTreeSelect,
  ProFormUploadButton,
  ProFormUploadDragger,
  type ProFormDateRangeValue,
  type ProFormDateValue,
} from 'antdv-next-pro'

interface DemoModel {
  text?: string
  digit?: number | string | null
  password?: string
  textarea?: string
  captcha?: string
  date?: ProFormDateValue
  dateTime?: ProFormDateValue
  dateRange?: ProFormDateRangeValue
  dateTimeRange?: ProFormDateRangeValue
  select?: string
  treeSelect?: string
  checkbox?: string[]
  radio?: string
  slider?: number | number[]
  switch?: boolean
  uploadButton: UploadFile[]
  uploadDragger: UploadFile[]
  money?: number | string | null
  segmented?: string | number
  bareText?: string
  bareChecked?: boolean
}

const { lang } = useData()
const english = computed(() => lang.value.startsWith('en'))
const readonly = ref(false)
const captchaStatus = ref('')
const model = reactive<DemoModel>({
  text: 'Antdv Next Pro',
  digit: 2,
  password: 'vue-vapor',
  textarea: 'One field core for forms, tables, and editable cells.',
  select: 'vue',
  treeSelect: 'pro-form-fields',
  checkbox: ['docs', 'tests'],
  radio: 'named',
  slider: 68,
  switch: true,
  uploadButton: [],
  uploadDragger: [],
  money: 128,
  segmented: 'preview',
  bareText: 'fieldMode="field"',
  bareChecked: true,
})

const tr = (zh: string, en: string) => (english.value ? en : zh)
const selectOptions = computed(() => [
  { label: 'Vue 3', value: 'vue' },
  { label: 'Antdv Next', value: 'antdv-next' },
])
const treeOptions = computed(() => [
  {
    label: tr('字段体系', 'Field system'),
    value: 'fields',
    children: [
      { label: 'ProFormFields', value: 'pro-form-fields' },
      { label: 'SchemaForm', value: 'schema-form' },
    ],
  },
])
const checkboxOptions = computed(() => [
  { label: tr('文档', 'Docs'), value: 'docs' },
  { label: tr('测试', 'Tests'), value: 'tests' },
  { label: tr('构建', 'Build'), value: 'build' },
])
const radioOptions = computed(() => [
  { label: tr('具名导入', 'Named import'), value: 'named' },
  { label: tr('全局注册', 'Global registration'), value: 'global' },
])
const segmentedOptions = computed(() => [
  { label: tr('编辑', 'Edit'), value: 'edit' },
  { label: tr('预览', 'Preview'), value: 'preview' },
])

async function requestSelectOptions() {
  await Promise.resolve()
  return selectOptions.value
}

async function getCaptcha() {
  await Promise.resolve()
  captchaStatus.value = tr('发送逻辑由当前示例提供', 'The demo supplied the send callback')
}
</script>

<template>
  <div class="demo-frame pro-form-fields-demo vp-raw">
    <div class="demo-heading">
      <div>
        <p class="demo-label">LIVE · 19 PRO FORM FIELDS</p>
        <p class="demo-note">
          {{
            tr(
              '所有字段共享相同的表单项、裸控件、只读与选项协议。',
              'Every field shares the same form-item, bare-control, readonly, and option protocols.',
            )
          }}
        </p>
      </div>
      <button type="button" @click="readonly = !readonly">
        {{ readonly ? tr('返回编辑', 'Edit values') : tr('查看只读', 'Readonly view') }}
      </button>
    </div>

    <Form :model="model" layout="vertical">
      <div class="field-grid">
        <ProFormText
          v-model="model.text"
          :label="tr('文本', 'Text')"
          :readonly="readonly"
          :field-props="{ allowClear: true }"
        >
          <template #extra>
            {{ tr('默认包含 FormItem', 'FormItem is included by default') }}
          </template>
        </ProFormText>

        <ProFormDigit v-model="model.digit" :label="tr('数字', 'Digit')" :readonly="readonly" />

        <ProFormTextPassword
          v-model="model.password"
          :label="tr('密码', 'Password')"
          :readonly="readonly"
        />

        <ProFormTextArea
          v-model="model.textarea"
          class="span-2"
          :label="tr('多行文本', 'Text area')"
          :readonly="readonly"
          :field-props="{ rows: 3 }"
        />

        <ProFormCaptcha
          v-model="model.captcha"
          class="span-2"
          :label="tr('验证码', 'Captcha')"
          :readonly="readonly"
          :on-get-captcha="getCaptcha"
          :count-down="5"
          :captcha-text="tr('发送演示验证码', 'Send demo code')"
        />

        <ProFormDatePicker v-model="model.date" :label="tr('日期', 'Date')" :readonly="readonly" />

        <ProFormDateTimePicker
          v-model="model.dateTime"
          :label="tr('日期时间', 'Date time')"
          :readonly="readonly"
        />

        <ProFormDateRangePicker
          v-model="model.dateRange"
          :label="tr('日期区间', 'Date range')"
          :readonly="readonly"
        />

        <ProFormDateTimeRangePicker
          v-model="model.dateTimeRange"
          :label="tr('日期时间区间', 'Date-time range')"
          :readonly="readonly"
        />

        <ProFormSelect
          v-model="model.select"
          :label="tr('异步选择', 'Async select')"
          :readonly="readonly"
          :request="requestSelectOptions"
        />

        <ProFormTreeSelect
          v-model="model.treeSelect"
          :label="tr('树选择', 'Tree select')"
          :readonly="readonly"
          :options="treeOptions"
        />

        <ProFormCheckbox
          v-model="model.checkbox"
          :label="tr('复选组', 'Checkbox group')"
          :readonly="readonly"
          :options="checkboxOptions"
          layout="vertical"
        />

        <ProFormRadioGroup
          v-model="model.radio"
          :label="tr('单选组', 'Radio group')"
          :readonly="readonly"
          :options="radioOptions"
        />

        <ProFormSlider
          v-model="model.slider"
          :label="tr('滑块', 'Slider')"
          :readonly="readonly"
          :field-props="{ min: 0, max: 100 }"
        />

        <ProFormSwitch v-model="model.switch" :label="tr('开关', 'Switch')" :readonly="readonly" />

        <ProFormMoney v-model="model.money" :label="tr('金额', 'Money')" :readonly="readonly" />

        <ProFormSegmented
          v-model="model.segmented"
          :label="tr('分段选择', 'Segmented')"
          :readonly="readonly"
          :options="segmentedOptions"
        />

        <ProFormUploadButton
          v-model="model.uploadButton"
          :label="tr('按钮上传', 'Upload button')"
          :readonly="readonly"
          :field-props="{ maxCount: 1 }"
        />

        <ProFormUploadDragger
          v-model="model.uploadDragger"
          class="span-2"
          :label="tr('拖拽上传', 'Upload dragger')"
          :readonly="readonly"
          :field-props="{ multiple: true }"
        />
      </div>
    </Form>

    <div class="bare-fields">
      <strong>{{ tr('裸控件模式', 'Bare-control mode') }}</strong>
      <ProFormText
        v-model="model.bareText"
        field-mode="field"
        :readonly="readonly"
        :placeholder="tr('不会生成 FormItem', 'No FormItem is rendered')"
      />
      <ProFormCheckbox v-model="model.bareChecked" field-mode="field" :readonly="readonly">
        {{
          tr(
            'checked / update:checked 已桥接到 v-model',
            'checked / update:checked bridged to v-model',
          )
        }}
      </ProFormCheckbox>
    </div>

    <p v-if="captchaStatus" class="demo-status">{{ captchaStatus }}</p>
  </div>
</template>

<style scoped>
.demo-heading {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 20px;
}

.demo-heading .demo-label,
.demo-note {
  margin: 0;
}

.demo-note {
  margin-top: 5px;
  color: var(--vp-c-text-2);
  font-size: 13px;
}

.demo-heading button {
  flex: none;
  padding: 6px 12px;
  border: 1px solid var(--bubbles-border);
  border-radius: 7px;
  background: var(--vp-c-bg);
  color: var(--vp-c-brand-1);
  cursor: pointer;
}

.field-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 20px;
}

.span-2 {
  grid-column: span 2;
}

.bare-fields {
  display: grid;
  grid-template-columns: minmax(120px, 0.45fr) minmax(220px, 1fr) minmax(220px, 1fr);
  align-items: center;
  gap: 14px;
  margin-top: 4px;
  padding-top: 18px;
  border-top: 1px solid var(--bubbles-border);
}

.demo-status {
  margin: 14px 0 0;
  color: var(--vp-c-text-2);
  font-size: 12px;
}

@media (max-width: 720px) {
  .field-grid,
  .bare-fields {
    grid-template-columns: 1fr;
  }

  .span-2 {
    grid-column: auto;
  }

  .demo-heading {
    flex-direction: column;
  }
}
</style>

Imports and names

All 19 template-friendly names support named imports. Installing the default plugin also registers them globally under the same names.

ts
import {
  ProFormCaptcha,
  ProFormCheckbox,
  ProFormDatePicker,
  ProFormDateRangePicker,
  ProFormDateTimePicker,
  ProFormDateTimeRangePicker,
  ProFormDigit,
  ProFormMoney,
  ProFormRadioGroup,
  ProFormSegmented,
  ProFormSelect,
  ProFormSlider,
  ProFormSwitch,
  ProFormText,
  ProFormTextArea,
  ProFormTextPassword,
  ProFormTreeSelect,
  ProFormUploadButton,
  ProFormUploadDragger,
} from 'antdv-next-pro'

The package also provides names matching the composed Ant Design Pro Components API:

ts
ProFormText.Password === ProFormTextPassword
ProFormRadio.Group === ProFormRadioGroup

Prefer ProFormTextPassword and ProFormRadioGroup in Vue templates. ProFormRadio is a readonly namespace object, not a twentieth field, and no dotted global component name is registered.

Common API

Props

PropTypeDefaultDescription
modelValueComponent-specificundefinedStandard v-model value
namestring | number | readonly (string | number)[]FormItem name, including nested paths
labelVNodeChildFormItem label
rulesAntdv Next FormItem rulesValidation rules
fieldMode'form-item' | 'field''form-item'Render a form item or a bare control
fieldPropsMatching base-control propsPassed to Input, Select, and other base controls
formItemPropsAntdv Next FormItem propsPassed only to FormItem
disabledbooleanfalsePreserve the control shape while disabling interaction
readonlybooleanfalseUse the shared readonly renderer
emptyTextVNodeChild'-'Placeholder for undefined, null, and empty strings
readonlyRender(value) => VNodeChildCustom readonly output; nullish output falls back to formatting

Form-item and bare-control modes

fieldMode="form-item" is the default. name, label, rules, and formItemProps apply to the FormItem created by the component.

vue
<ProFormText
  v-model="form.title"
  name="title"
  label="Title"
  :rules="[{ required: true, message: 'Enter a title' }]"
/>

Use bare-control mode when a parent already owns the FormItem, or when the field lives in a table cell or custom grid. It creates no FormItem, and name, label, rules, and formItemProps do not participate in rendering.

vue
<ProFormText v-model="keyword" field-mode="field" placeholder="Only Input is rendered" />

Prop precedence and attrs

Ordinary properties resolve in this order:

  1. Explicit top-level component prop.
  2. The same key in fieldProps or formItemProps.
  3. Component default.

Top-level disabled therefore overrides fieldProps.disabled, and top-level label overrides formItemProps.label. The field core always owns controlled value props and update listeners; consumer handlers are composed with, not substituted for, v-model updates.

fieldProps.class/style targets the base control, while formItemProps.class/style targets FormItem. Plain class, style, data-*, and aria-* attrs target FormItem in form-item mode and the base control in field mode.

v-model bridges

Every public component exposes modelValue / update:modelValue. The field core adapts the base-control protocol:

Field kindBase-control protocolPublic protocol
Input, InputNumber, date, Select, Radio, Slider, Segmentedvalue / update:valuev-model
Single Checkbox, Switchchecked / update:checkedv-model
Upload, UploadDraggerfileList / update:fileListv-model

fieldProps['onUpdate:value'], fieldProps['onUpdate:checked'], or fieldProps['onUpdate:fileList'] can coexist with component v-model and are each called once per update.

Events

EventComponentsDescription
update:modelValueAllNormalized field value
changeAllOriginal base-control change arguments
requestErrorSelect, TreeSelect, Checkbox group, RadioGroup, SegmentedOption request failed; valid existing options remain
captchaErrorCaptchaCallback rejected or returned false; false stays an explicit cancellation result
dropBoth Upload fieldsOriginal drop event

The base event order is internal model update → fieldProps handler → wrapper emit.

Slots and instance methods

label, extra, help, and tooltip are FormItem slots. Other named slots and the default slot are forwarded to the base control. FormItem-only slots are not rendered in fieldMode="field".

  • Captcha adds a captcha slot with { seconds }.
  • UploadButton's default slot replaces the upload-button content.
  • UploadDragger's default slot replaces the drag area.
  • Option fields expose refresh(): Promise<void> through a component ref.
  • Captcha exposes resetCountdown(): void.

Options, requests, and readonly display

options, valueEnum, and request

Select, TreeSelect, Checkbox groups, RadioGroup, and Segmented share one protocol:

vue
<ProFormSelect
  ref="selectRef"
  v-model="owner"
  :options="fallbackOptions"
  :params="{ teamId }"
  :request="loadOwners"
  @request-error="reportError"
/>
  • When request exists, it starts immediately. Local options may be shown before its first success.
  • After a successful request, the remote result is authoritative. An empty array is valid and does not fall back.
  • Without request, precedence is top-level optionsfieldProps.options (or treeData for TreeSelect) → valueEnum.
  • A failure preserves the last successful result. Before any success, it preserves the local fallback and emits requestError.
  • Changing request or params refreshes options. Only the newest concurrent request may update state.

Options use label ?? text ?? title ?? name ?? value as the label and value ?? key ?? id as the value. To preserve numeric valueEnum keys strictly, use a ReadonlyMap or set value explicitly:

ts
const levels = new Map([
  [1, { text: 'Level 1' }],
  [2, { text: 'Level 2' }],
])

const alternate = {
  1: { text: 'Level 1', value: 1 },
}

Readonly and reset

readonly uses one ReadonlyField instead of relying on inconsistent native readonly support. Choice fields show labels, arrays use commas, date ranges use ~, passwords are masked, money keeps its prefix, and uploads show file names. 0 and false are not considered empty.

Fields are controlled: when the parent resets and passes a new modelValue, the display updates immediately. Resetting the value does not clear the option cache. Captcha countdowns are cancelled explicitly with resetCountdown().

The 19 components

Every component below is interactive in the complete field demo.

ProFormText

A single-line text field with string | undefined model values and an Input base control. It adds no Pro-specific default. Pass allowClear, prefix, suffix, maxlength, and other Input props through fieldProps.

ProFormDigit

A numeric field with number | string | null values. It defaults to min=0 and precision=2. Top-level min / precision win over fieldProps; pass false to omit either constraint.

vue
<ProFormDigit v-model="count" :min="false" :precision="0" />

ProFormTextPassword

A password field with string | undefined values and InputPassword underneath. Non-empty readonly values render as ••••••. Use template-friendly ProFormTextPassword or the composed ProFormText.Password name.

ProFormTextArea

A multiline string | undefined field. Pass rows, autoSize, maxlength, and other TextArea props through fieldProps.

ProFormCaptcha

An Input and send-button composition. onGetCaptcha is required, countDown defaults to 60 seconds, and the default button text is “获取验证码”. A resolved result other than false starts the countdown; rejection or false restores the button and emits captchaError.

The component does not read phone numbers, send a built-in request, or persist codes. The consumer must implement onGetCaptcha. Loading, countdown, disabled, and readonly states all prevent repeated sends.

ProFormDatePicker

A date picker that preserves Antdv Next's Dayjs value protocol. It does not convert to strings or native Date values. Pass formatting, disabled dates, and panel props through fieldProps.

ProFormDateTimePicker

A Dayjs date-time picker with showTime=true by default. Override it with top-level showTime or a concrete fieldProps.showTime configuration.

ProFormDateRangePicker

A Dayjs range value or null. Clearing does not synthesize an empty string. Range presets, disabled dates, and related props pass through fieldProps.

ProFormDateTimeRangePicker

A Dayjs date-time range with showTime=true by default.

ProFormSelect

Supports options, valueEnum, request, params, and requestError. Use fieldProps for mode="multiple" / "tags" and other Select capabilities. Its ref exposes refresh().

ProFormTreeSelect

Uses the shared option-request protocol. Standard options map to tree nodes with title, label, value, disabled, and children. Consumer-supplied treeData, fieldNames, and related props remain available through fieldProps.

ProFormCheckbox

Without an option source, it renders a single Checkbox and bridges native checked to v-model. With options, valueEnum, request, or fieldProps.options, it renders CheckboxGroup. layout defaults to horizontal and also accepts vertical.

ProFormRadioGroup

A RadioGroup using the shared option-request protocol. Native orientation and button-style capabilities pass through fieldProps. Prefer ProFormRadioGroup in templates; ProFormRadio.Group is also exported.

ProFormSlider

Uses number | number[] | undefined model values. range, marks, min, max, step, and tooltip pass through fieldProps.

ProFormSwitch

Exposes v-model while bridging to native checked / update:checked. checkedValue and unCheckedValue are preserved instead of being forced to Boolean.

ProFormUploadButton

A button upload entry point with an UploadFile[] model bridged to fileList. Its default slot replaces the button content. Pass action, customRequest, beforeUpload, headers, data, multiple, accept, maxCount, itemRender, and other Upload props through fieldProps.

The library supplies no upload backend. Without action or customRequest, its internal beforeUpload returns false to prevent requests to an unknown destination, while still running a consumer beforeUpload handler.

ProFormUploadDragger

A drag-and-drop upload entry point with the same UploadFile[], fileList, and no-backend contract as UploadButton. Its default slot replaces the drag area and drop forwards the native event.

ProFormMoney

A number | string | null amount field. The prefix defaults to ¥; top-level prefix or fieldProps.prefix can override it, and false disables it. formatter, parser, and stringMode pass to InputNumber.

ProFormSegmented

A segmented string | number field using the shared option-request protocol. It supplies an empty options array when no source exists, and preserves disabled plus other option metadata.

Shared field core in higher-level components

Standalone fields and higher-level components use the same registry and FieldControl:

  • ProTable search items use form-item mode; editable cells use bare-control mode.
  • SchemaForm keeps its own FormItem, nested paths, dynamic slots, and composition fields while its default-control branch uses the bare field core.
  • EditableProTable reuses the core through ProTable instead of maintaining a third field map.

This release adds treeSelect, slider, and segmented to valueType. Captcha, UploadButton, and UploadDragger remain standalone-only components: they have no valueType mapping and no built-in backend.

Released under the MIT License