## From组件
由输入框、选择器、单选框、多选框、图片上传等控件组成，用以收集、校验、提交数据，目前内置校验类型包括`email` `mobile` `bank` `idCard`，以及[async-validate](https://github.com/yiminghe/async-validator#type)自带的校验类型

### Import
```js
import { Form, FormItem } from 'bxs-ui-vue'
```

### Example

#### 普通示例
```html
<!-- 基础信息，普通对齐方式 -->
<bxs-form ref="form" :model="form" :rules="rules" :is-content-vertical-alignment="false">
  <!-- 可忽略字段 -->
  <bxs-form-item
    prop="ignoreField"
    label="可忽略校验字段"
    ignore-validate
    >
    <bxs-input
      type="text"
      v-model="form.ignoreField"
      placeholder="配置ignore-validate即可"
    />
  </bxs-form-item>
  <!-- 姓名 -->
  <bxs-form-item prop="name" label="姓名">
    <bxs-input type="text" v-model="form.name" placeholder="请填写姓名" />
  </bxs-form-item>
  <!-- 年龄 -->
  <bxs-form-item prop="age" label="年龄">
    <bxs-input type="text" v-model="form.age" placeholder="请填写年龄" />
  </bxs-form-item>
  <!-- 性别 -->
  <bxs-form-item prop="sex" label="性别" content-position="right">
    <bxs-radio-group v-model="form.sex">
      <bxs-radio :name="1">男</bxs-radio>
      <bxs-radio :name="0">女</bxs-radio>
    </bxs-radio-group>
  </bxs-form-item>
  <!-- 手机号码 -->
  <bxs-form-item prop="mobile" label="手机号码">
    <bxs-input type="text" v-model="form.mobile" placeholder="请填写手机号码" />
  </bxs-form-item>
</bxs-form>
<bxs-button class="demo-form-button" size="middle" @click="submit">提交</bxs-button>
<bxs-button class="demo-form-button" size="middle" @click="reset">重置</bxs-button>
```
```js
const getDefaultForm = () => ({
  name: '',
  age: '',
  sex: 0,
  mobile: '',
  ignoreField: ''
})
export default {
  data() {
    return {
      form: getDefaultForm(),
      rules: {
        name: { required: true, message: '请填写姓名', },
        age: [
          { required: true, message: '请填写年龄' },
          { len: 2, message: '请填写长度等于2的数字' },
        ],
        mobile: {
          required: true,
          type: 'mobile',
          message: '请输入手机号码',
        },
      },
    }
  },
  methods: {
    submit() {
      this.$refs.form.validate().then(() => {
        console.log('验证通过')
      }).catch(error => {
        console.log('验证失败 逻辑可自行处理', error)
      })
    },
    reset() {
      this.form = getDefaultForm()
      this.$refs.form.resetFields()
    },
  },
}
```

#### 复杂示例
```html
<bxs-form ref="form"
  :model="form"
  :rules="rules"
  auto-position-error
  :auto-show-toast="false"
>
  <!-- 普通输入框 -->
  <bxs-form-item prop="name" label="姓名">
    <bxs-input type="text" v-model="form.name" placeholder="请填写姓名" />
  </bxs-form-item>
  <!-- 多行输入框 -->
  <bxs-form-item prop="intro" label="自我介绍" required="请填写简介" label-position="top">
    <bxs-input type="textarea"
      v-model="form.intro"
      :autosize="{minHeight: 44, maxHeight: 88}"
      placeholder="请填写自我介绍"
    />
  </bxs-form-item>
  <!-- 单选框 -->
  <bxs-form-item prop="sex" label="性别" content-position="right">
    <bxs-radio-group v-model="form.sex">
      <bxs-radio :name="1">男</bxs-radio>
      <bxs-radio :name="0">女</bxs-radio>
    </bxs-radio-group>
  </bxs-form-item>
  <!-- 时间选择器 -->
  <bxs-form-item prop="brithday" label="出生日期" required="请选择出生日期">
    <bxs-input type="text"
      v-model="form.brithday"
      placeholder="请选择出生日期"
      readonly
      @click="chooseBrithday"
    />
  </bxs-form-item>
  <!-- 邮箱 -->
  <bxs-form-item prop="email" label="邮箱">
    <bxs-mail-input type="text" v-model="form.email" placeholder="请填写邮箱" />
  </bxs-form-item>
  <!-- 开关 -->
  <bxs-form-item prop="switch" label="通知" content-position="right">
    <bxs-switch v-model="form.switch" />
  </bxs-form-item>
  <!-- 步进器 -->
  <bxs-form-item prop="step" label="步进器" required="请选择数量" content-position="right">
    <bxs-stepper v-model="form.step" />
  </bxs-form-item>
  <!-- 原生input -->
  <bxs-form-item prop="pureInput" label="原生input" required="请填写内容">
    <input type="text"
      v-model="form.pureInput"
      placeholder="请填写内容"
      @focus="$refs.form.resetFields('pureInput')"
      @blur="$refs.form.validate('pureInput')"
    >
  </bxs-form-item>
  <!-- 上传图片 -->
  <bxs-form-item prop="image" label="身份证正面" required="请上传图片" label-position="top">
    <bxs-uploader :oss-type="1"
      list-style="single"
      bg-img="file"
      v-model="images"
      file-dir="project-name"
      @upload-success="uploadSuccess"
      @delete="uploadDelete"
    />
  </bxs-form-item>
  <!-- 勾选协议 -->
  <bxs-form-item prop="protocol">
    <bxs-protocol :protocol-list="protocolList"
      v-model="form.protocol"
      style="padding: 15px 0;background-color: #FFF"
    />
  </bxs-form-item>
</bxs-form>
<bxs-button class="demo-form-button" size="middle" @click="submit">提交</bxs-button>
<bxs-button class="demo-form-button" size="middle" @click="reset">重置</bxs-button>
```
```js
const getDefaultForm = () => ({
  name: '',
  brithday: '',
  sex: 0,
  email: '',
  intro: '',
  protocol: true,
  switch: false,
  pureInput: '',
  step: 0,
  image: '',
})
export default {
  data() {
    return {
      form: getDefaultForm(),
      images: [
        {
          bgImg: 'file',
        },
      ],
      rules: {
        name: { required: true, message: '请填写姓名',},
        email: { required: true, type: 'email', message: '请选择输入的邮箱',},
        protocol: {
          validator(rule, value) {
            return value === true
          },
          message: '请勾选协议约定',
        },
        switch: {
          validator(rule, value) {
            return value === true
          },
          message: '请切换开关',
        },
        step: {
          validator(rule, value) {
            return value > 0
          },
          message: '请选择数量',
        },
      },
      protocolList: [
        {
          name: '《投保须知》',
          onClick: this.a,
        },
        {
          name: '《投保须知2》',
          url: 'http://www.baidu.com',
          target: '_blank',
          onClick: this.a,
        },
      ],
    }
  },
  methods: {
    chooseBrithday() {
      const self = this
      this.$datetime({
        value: new Date(),
        type: 'datetime',
        confirmButtonColor: '#508cee',
        confirm(data) {
          self.form.brithday = new Date(data).getTime()
          self.$refs.form.validate('brithday')
        },
        cancel () {
          self.$refs.form.validate('brithday')
        }
      })
    },
    uploadSuccess(options) {
      this.form.image = options.image.url
    },
    uploadDelete(options) {
      this.form.image = options.image.url
    },
    submit() {
      this.$refs.form.validate().then(() => {
        console.log('验证通过')
      }).catch(error => {
        console.log('验证失败 逻辑可自行处理', error)
        this.$toast(error[0].message)
      })
    },
    reset() {
      this.form = getDefaultForm()
      this.$refs.form.resetFields()
    },
  },
}
```

#### 多表单示例
```html
<bxs-form ref="form_main" :model="form" :rules="rules" :ignore-last-line="false">
  <!-- 普通输入框 -->
  <bxs-form-item prop="name" label="姓名">
    <bxs-input type="text" v-model="form.name" placeholder="请填写姓名" />
  </bxs-form-item>
</bxs-from>
<bxs-form ref="form_other" :model="form" :rules="rules">
  <!-- 手机号码 -->
  <bxs-form-item prop="mobile" label="手机号码">
    <bxs-input type="text" v-model="form.mobile" placeholder="请填写手机号码" />
  </bxs-form-item>
</bxs-from>
```
```js
import PromiseSequential from 'promise-sequential'

const getDefaultForm = () => ({
  name: '',
  mobile: '',
})
export default {
  data() {
    return {
      form: getDefaultForm(),
      rules: {
        name: { required: true, message: '请填写姓名',},
        mobile: { required: true, type: 'mobile', message: '请输入手机号码',},
      },
    }
  },
  methods: {
    submit() {
      PromiseSequential([
        () => this.$refs.form_main.validate(),
        () => this.$refs.form_other.validate(),
      ])
        .then(() => {
          console.log('验证通过')
        })
        .catch(error => {
          console.log('验证失败 逻辑可自行处理', error)
        })
    },
    reset() {
      this.form = getDefaultForm()
      this.$refs.form_main.resetFields()
      this.$refs.form_other.resetFields()
    },
  },
}
```

### Form Attributes
| 参数| 说明 | 类型 | 可选值 | 默认值 |
| --- | --- | --- |  --- | --- |
| model | 必填，表单数据对象 | Object | ` ` | ` ` |
| rules | 可选，表单验证规则 | Object | ` ` | ` ` |
| showMessage | 可选，是否显示校验错误信息 | Boolean | `true` `false` | `true` |
| autoPositionError | 可选，是否自动定位至错误项 | Boolean | `true` `false` | `true` |
| autoShowToast | 可选，是否自动显示Toast提示 | Boolean | `true` `false` | `true` |
| toastMessage | 可选，默认Toast提示文案 | String | `string` | `请完善填写内容` |
| ignoreLastLine | 可选，是否忽略最后一项的分割线 | `true` `false` | `true` |
| isContentVerticalAlignment | 可选，是否所有内容垂直对齐 | `true` `false` | `true` |

### Form Methods
| 方法名称 | 说明 | 参数 | 返回值 |
| --- | --- | --- | --- |
| validate | 对单个或整个表单进行校验的方法, 返回`Promise` | `form item prop` | `Promise` |
| resetFields | 对单个或整个表单进行重置，将所有字段值重置为初始值并移除校验结果 | `form item prop` | ` ` |

### Validate 方法返回值
以`prop name`为例
```js
this.$refs.form.validate('name').then(res => {
  // 验证成功无返回值，res: undefined
}).catch({ errors } => {
  // 验证失败返回值结构如下，一般情况下errors便够用
})
```
验证失败结构：
```js
{
  errors: [
    field: 'name',
    message: '请填写姓名'
  ],
  fields: {
    name: [{
      field: 'name',
      message: '请填写姓名'
    }]
  }
}
```

### Rules 规则
与[async-validator](https://github.com/yiminghe/async-validator#rules)规则一致，并内置`email` `mobile` `bank` `idCard`等校验规则

### Form Slot
| 参数 | 说明 |
| --- | --- |
| — | 默认slot |

### FormItem Attributes
| 参数| 说明 | 类型 | 可选值 | 默认值 |
| --- | --- | --- |  --- | --- |
| prop | 必填，表单域 model 字段 | String | ` ` | ` ` |
| label | 可选，标签文本 | String | ` ` | ` ` |
| labelPosition | 可选，表单域标签的位置 | String | `top` `bottom` `left` `right` `middle` | `left` |
| contentPosition | 可选，表单域内容的位置 | String | `top` `bottom` `left` `right` `middle` | `left` |
| required | 可选，是否必填，值为文本时，表示错误提示信息，如不设置，则会根据校验规则自动生成 | Boolean String | `true` `false` `string` | `undefined` |
| rules | 可选，表单验证规则，会与`Form`的`rules`进行合并 | Object | ` ` | ` ` |
| showMessage | 可选，是否显示校验错误信息，会覆盖`Form`的`showMessage` | `true` `false` | `undefined` |
| ignoreValidate | 可选，是否忽略本次校验 | `true` `false` | `false` |

### FormItem Methods
| 方法名称 | 说明 | 参数 | 返回值 |
| --- | --- | --- | --- |
| validate | 对整个表单域进行校验的方法, 返回`Promise`，返回值同`Form`的`validate`方法 | ` ` | `Promise` |
| resetField | 对表单域进行重置，将所有字段值重置为初始值并移除校验结果 | ` ` | ` ` |

### FormItem Slot
| 参数 | 说明 |
| --- | --- |
| — | 默认slot |

### 自定义组件接入
#### 方法一：使用provide/inject
```js
export default {
  // 在FormItem组件内，已经provide bxsFormItem
  inject: {
    bxsFormItem: {
      default: {}
    }
  },
  computed: {
    formItem() {
      return this.bxsFormItem
    }
  },
  methods: {
    onBlur () {
      // 单个表单域执行验证
      this.formItem.onValidate()
    },
    onFocus () {
      // 单个表单域执行重置
      this.formItem.reset()
    }
  }
}
```

#### 方法二: 查找FormItem父组件（推荐）
FormItem组件内已对 `form.item.validate` `form.item.reset` 进行监听
```js
export default {
  methods: {
    onBlur () {
      // 单个表单域执行验证
      this.dispatch('bxs-form-item', 'form.item.validate')
    },
    onFocus () {
      // 单个表单域执行重置
      this.dispatch('bxs-form-item', 'form.item.reset')
    }
    // 触发父组件监听的方法
    dispatch (componentName, eventName, params) {
      var parent = this.$parent || this.$root
      var name = parent.$options.name
      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent
        if (parent) {
          name = parent.$options.name
        }
      }
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params))
      }
    }
  }
}
```
