Skip to content
On this page

Form/FormFooter/FormItem 表单

寄语:表单这块一直没有能深度去思考,之前做过一般跟其他 UI 库一样校验,但是都觉得不满意,现只保留最简单最布局功能。

注:

  • 这几个组件主要聚焦于布局。
  • 如果想要复杂的表单设计或者校验功能,可以借助 formilyjs 来实现。
vue
<template>
  <ta-group title="基础">
    <ta-form>
      <ta-form-item label="昵称">
        <ta-input v-model="baseForm.nickname" placeholder="请输入昵称" />
      </ta-form-item>
      <ta-form-item label="性别">
        <ta-radio-group v-model="baseForm.gender" :options="genderOptions"></ta-radio-group>
      </ta-form-item>
      <template #footer>
        <ta-button type="primary" @click="onBaseSubmit">提交</ta-button>
      </template>
    </ta-form>
  </ta-group>
  <ta-group title="Formily">
    <FormProvider :form="form">
      <Field
        name="nickname"
        title="昵称"
        required
        :component="[TaInput, { placeholder: '请输入昵称', showClear: true }]"
        :decorator="[FormItem]"
      />
      <Field
        name="avatar"
        title="头像"
        required
        :component="[TaImageUploader, { uploadReady: hookUpload, columnNumber: 1, maxCount: 1 }]"
        :decorator="[FormItem]"
      />
      <Field
        name="gender"
        title="性别"
        required
        :component="[
          TaRadioGroup,
          {
            options: genderOptions
          }
        ]"
        :decorator="[FormItem]"
      />
      <Field
        name="weight"
        title="体重Kg"
        required
        :component="[TaSlider, { showValue: true, min: 35, max: 120 }]"
        :decorator="[FormItem]"
      />
      <Field
        name="season"
        title="季节"
        required
        :component="[TaPicker, { options: multiOptions, placeholder: '选择季节' }]"
        :decorator="[FormItem]"
      />
      <Field
        name="birthday"
        title="生日"
        required
        :component="[TaCalendar, { placeholder: '选择生日' }]"
        :decorator="[FormItem]"
      />
      <Field
        name="character"
        title="性格"
        required
        :component="[TaCheckboxGroup, { options: characters }]"
        :decorator="[FormItem]"
      />
      <Field
        name="region"
        title="地区"
        required
        :component="[
          TaCascader,
          {
            placeholder: '选择地区',
            fieldNames: { value: 'label' },
            options: regionOptions
          }
        ]"
        :decorator="[FormItem]"
      />
      <Field
        name="happinessIndex"
        title="幸福指数"
        required
        :component="[TaRate, { allowHalf: true }]"
        :decorator="[FormItem]"
      />
      <Field
        name="TaStepper"
        title="步进器"
        required
        :component="[TaStepper, { max: 10, step: 0.2, decimalLength: 1 }]"
        :decorator="[FormItem]"
      />
      <Field name="agree" title="认可" required :component="[TaSwitch]" :decorator="[FormItem]" />
      <FormConsumer>
        <template #default="{ form }">
          <pre class="exp-form-json">{{ JSON.stringify(form.values, null, 2) }}</pre>
          <ta-form-footer>
            <ta-button
              type="primary"
              @click="
                () => {
                  form.submit(onSubmit)
                }
              "
              >提交</ta-button
            >
          </ta-form-footer>
        </template>
      </FormConsumer>
    </FormProvider>
  </ta-group>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { multiOptions, regionOptions } from '../Picker/data'
import {
  TaInput,
  TaPicker,
  TaCalendar,
  TaCascader,
  TaSwitch,
  TaRate,
  TaSlider,
  TaRange,
  TaStepper,
  TaRadioGroup,
  TaCheckboxGroup,
  TaImageUploader,
  showToast,
  showDialog,
  type ImageUploaderUploadReady
} from '@/index'
import { createForm, setValidateLanguage } from '@formily/core'
import { FormProvider, Field, FormConsumer } from '@formily/vue'
import FormItem from './FormItem'

setValidateLanguage('zh-CN')

export default defineComponent({
  name: 'ExpForm',
  components: { FormProvider, Field, FormConsumer },
  setup() {
    function getDataUrl(file: File) {
      return new Promise<string>(resolve => {
        const fr = new FileReader()
        fr.onload = function (e) {
          resolve((e.target?.result as string) ?? '')
        }
        fr.readAsDataURL(file)
      })
    }

    const hookUpload: ImageUploaderUploadReady = (file, handlers) => {
      getDataUrl(file).then(url => {
        handlers.success(url)
      })
    }

    return {
      hookUpload
    }
  },
  data() {
    return {
      baseForm: {
        nickname: '',
        gender: ''
      },

      FormItem,
      TaInput,
      TaPicker,
      TaCalendar,
      TaCascader,
      TaSwitch,
      TaRate,
      TaSlider,
      TaRange,
      TaStepper,
      TaRadioGroup,
      TaCheckboxGroup,
      TaImageUploader,
      form: createForm({ validateFirst: true }),

      genderOptions: [
        { label: '', value: 1 },
        { label: '', value: 2 }
      ],
      regionOptions,
      multiOptions,
      characters: ['活泼', '内向', '高冷']
    }
  },
  methods: {
    onBaseSubmit() {
      showDialog({
        showCancel: false,
        content: `nickname: ${this.baseForm.nickname}
        gender: ${this.baseForm.gender}
        `
      })
    },
    onSubmit(...args: any[]) {
      console.log(...args)
      showToast('校验通过')
    }
  }
})
</script>

Import

js
import { TaForm, TaFormFooter, TaFormItem } from 'tantalum-ui-mobile'

具体的引入方式可以参考引入组件

Form Slots

#default

vue
<ta-form>
  <ta-input type="text" />
</ta-form>
vue
<ta-form>
  <template #footer>
    <ta-button form-type="submit">提交</ta-button>
  </template>
</ta-form>

FormFooter Slots

#default

vue
<ta-form-footer>
  <ta-button form-type="submit">提交</ta-button>
</ta-form-footer>

FormItem Props

属性类型默认值必填说明
errorstring | string[]错误提示信息
labelstring设置该行名称,比如 昵称
requiredbooleanfalse是否必填,设置 true 后 label 会展示必填*

FormItem Slots

default

vue
<ta-form-item>
  <ta-input type="text" />
</ta-form-item>

支持表单的组件

Formily 配合

关于 Formily 的介绍可以查看阿里巴巴的官网

安装

sh
npm install --save @formily/core @formily/vue

FormItem 做下适配

FormilyFormItem.js:

js
import { TaFormItem } from 'tantalum-ui-mobile'
import { connect, mapProps } from '@formily/vue'
import { isVoidField } from '@formily/core'

export default connect(
  TaFormItem,
  mapProps({ validateStatus: true, title: 'label', required: true }, (props, field) => {
    if (isVoidField(field)) return props
    if (!field) return props

    const getMessage = () => {
      if (field.validating) return
      if (props.error) return props.error
      if (field.selfErrors.length) return field.selfErrors
      // if (field.selfWarnings.length) return split(field.selfWarnings)
      // if (field.selfSuccesses.length) return split(field.selfSuccesses)
    }

    return {
      error: getMessage()
    }
  })
)

设计表单

vue
<template>
  <FormProvider :form="form">
    <Field
      name="nickname"
      title="昵称"
      required
      :component="[Input, { placeholder: '请输入昵称' }]"
      :decorator="[FormilyFormItem]"
    />
    <Field
      name="gender"
      title="性别"
      required
      :component="[
        RadioGroup,
        {
          options: genderOptions
        }
      ]"
      :decorator="[FormilyFormItem]"
    />
    <FormConsumer>
      <template #default="{ form }">
        <pre class="exp-form-json">{{ JSON.stringify(form.values, null, 2) }}</pre>
        <ta-form-footer>
          <ta-button
            type="primary"
            @click="
              () => {
                form.submit(onSubmit)
              }
            "
            >提交</ta-button
          >
        </ta-form-footer>
      </template>
    </FormConsumer>
  </FormProvider>
</template>

<script>
import { multiOptions, regionOptions } from '../Picker/data'
import { Input, RadioGroup, Dialog } from 'tantalum-ui-mobile'
import { createForm, setValidateLanguage } from '@formily/core'
import { FormProvider, Field, FormConsumer } from '@formily/vue'
import FormilyFormItem from './FormilyFormItem'

setValidateLanguage('zh-CN')

export default {
  name: 'ExpForm',
  components: { FormProvider, Field, FormConsumer },
  data() {
    return {
      FormilyFormItem,
      Input,
      RadioGroup,
      form: createForm({ validateFirst: true }),

      genderOptions: [
        { label: '', value: 1 },
        { label: '', value: 2 }
      ]
    }
  },
  methods: {
    onBaseSubmit() {
      Dialog.showDialog({
        showCancel: false,
        content: `nickname: ${this.baseForm.nickname}
        gender: ${this.baseForm.gender}
        `
      })
    }
  }
}
</script>

附录: