mirror of
https://gitee.com/ant-design-vue/ant-design-vue.git
synced 2024-12-03 04:27:41 +08:00
feat: add form
This commit is contained in:
parent
790fe9c4b9
commit
138274f9c6
@ -97,6 +97,9 @@ export default {
|
||||
getChildAttr (prop) {
|
||||
const child = this.getOnlyControl()
|
||||
let data = {}
|
||||
if (!child) {
|
||||
return undefined
|
||||
}
|
||||
if (child.data) {
|
||||
data = child.data
|
||||
} else if (child.$vnode && child.$vnode.data) {
|
||||
|
82
components/form/demo/coordinated.md
Normal file
82
components/form/demo/coordinated.md
Normal file
@ -0,0 +1,82 @@
|
||||
<cn>
|
||||
#### 表单联动
|
||||
使用 `setFieldsValue` 来动态设置其他控件的值。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Coordinated Controls
|
||||
Use `setFieldsValue` to set other control's value programmaticly.
|
||||
</us>
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Form } from 'vue-antd-ui'
|
||||
|
||||
const CoordinatedForm = {
|
||||
methods: {
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
this.form.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
console.log('Received values of form: ', values)
|
||||
}
|
||||
})
|
||||
},
|
||||
handleSelectChange (value) {
|
||||
console.log(value)
|
||||
this.form.setFieldsValue({
|
||||
note: `Hi, ${value === 'male' ? 'man' : 'lady'}!`,
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const { getFieldDecorator } = this.form
|
||||
return (
|
||||
<a-form onSubmit={this.handleSubmit}>
|
||||
<a-form-item
|
||||
label='Note'
|
||||
labelCol={{ span: 5 }}
|
||||
wrapperCol={{ span: 12 }}
|
||||
>
|
||||
{getFieldDecorator('note', {
|
||||
rules: [{ required: true, message: 'Please input your note!' }],
|
||||
})(
|
||||
<a-input />
|
||||
)}
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label='Gender'
|
||||
labelCol={{ span: 5 }}
|
||||
wrapperCol={{ span: 12 }}
|
||||
>
|
||||
{getFieldDecorator('gender', {
|
||||
rules: [{ required: true, message: 'Please select your gender!' }],
|
||||
})(
|
||||
<a-select
|
||||
placeholder='Select a option and change input text above'
|
||||
onChange={this.handleSelectChange}
|
||||
>
|
||||
<a-select-option value='male'>male</a-select-option>
|
||||
<a-select-option value='female'>female</a-select-option>
|
||||
</a-select>
|
||||
)}
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
wrapperCol={{ span: 12, offset: 5 }}
|
||||
>
|
||||
<a-button type='primary' htmlType='submit'>
|
||||
Submit
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export default Form.create()(CoordinatedForm)
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
|
129
components/form/demo/customized-form-controls.md
Normal file
129
components/form/demo/customized-form-controls.md
Normal file
@ -0,0 +1,129 @@
|
||||
<cn>
|
||||
#### 自定义表单控件
|
||||
自定义或第三方的表单控件,也可以与 Form 组件一起使用。只要该组件遵循以下的约定:
|
||||
> * 提供受控属性 `value` 或其它与 [`valuePropName`](/ant-design/components/form-cn/#getFieldDecorator(id,-options)-参数) 的值同名的属性。
|
||||
> * 提供 `onChange` 事件或 [`trigger`](/ant-design/components/form-cn/#getFieldDecorator(id,-options)-参数) 的值同名的事件。
|
||||
> * 不能是函数式组件。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Customized Form Controls
|
||||
Customized or third-party form controls can be used in Form, too. Controls must follow these conventions:
|
||||
> * It has a controlled property `value` or other name which is equal to the value of [`valuePropName`](/ant-design/components/form/#getFieldDecorator(id,-options)-parameters).
|
||||
> * It has event `onChange` or an event which name is equal to the value of [`trigger`](/ant-design/components/form/#getFieldDecorator(id,-options)-parameters).
|
||||
> * It must be a class component.
|
||||
</us>
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Form } from 'vue-antd-ui'
|
||||
|
||||
const hasProp = (instance, prop) => {
|
||||
const $options = instance.$options || {}
|
||||
const propsData = $options.propsData || {}
|
||||
return prop in propsData
|
||||
}
|
||||
const PriceInput = {
|
||||
props: ['value'],
|
||||
data () {
|
||||
const value = this.value || {}
|
||||
return {
|
||||
number: value.number || 0,
|
||||
currency: value.currency || 'rmb',
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (val = {}) {
|
||||
this.number = val.number || 0
|
||||
this.currency = val.currency || 'rmb'
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleNumberChange (e) {
|
||||
const number = parseInt(e.target.value || 0, 10)
|
||||
if (isNaN(number)) {
|
||||
return
|
||||
}
|
||||
if (!hasProp(this, 'value')) {
|
||||
this.number = number
|
||||
}
|
||||
this.triggerChange({ number })
|
||||
},
|
||||
handleCurrencyChange (currency) {
|
||||
if (!hasProp(this, 'value')) {
|
||||
this.currency = currency
|
||||
}
|
||||
this.triggerChange({ currency })
|
||||
},
|
||||
triggerChange (changedValue) {
|
||||
// Should provide an event to pass value to Form.
|
||||
this.$emit('change', Object.assign({}, this.$data, changedValue))
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const { number, currency } = this
|
||||
return (
|
||||
<span>
|
||||
<a-input
|
||||
type='text'
|
||||
value={number}
|
||||
onChange={this.handleNumberChange}
|
||||
style={{ width: '65%', marginRight: '3%' }}
|
||||
/>
|
||||
<a-select
|
||||
value={currency}
|
||||
style={{ width: '32%' }}
|
||||
onChange={this.handleCurrencyChange}
|
||||
>
|
||||
<a-select-option value='rmb'>RMB</a-select-option>
|
||||
<a-select-option value='dollar'>Dollar</a-select-option>
|
||||
</a-select>
|
||||
</span>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
const Demo = {
|
||||
methods: {
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
this.form.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
console.log('Received values of form: ', values)
|
||||
}
|
||||
})
|
||||
},
|
||||
checkPrice (rule, value, callback) {
|
||||
if (value.number > 0) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
callback('Price must greater than zero!')
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const { getFieldDecorator } = this.form
|
||||
return (
|
||||
<a-form layout='inline' onSubmit={this.handleSubmit}>
|
||||
<a-form-item label='Price'>
|
||||
{getFieldDecorator('price', {
|
||||
initialValue: { number: 0, currency: 'rmb' },
|
||||
rules: [{ validator: this.checkPrice }],
|
||||
})(<PriceInput />)}
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button type='primary' htmlType='submit'>Submit</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export default Form.create()(Demo)
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
|
144
components/form/demo/dynamic-form-item.md
Normal file
144
components/form/demo/dynamic-form-item.md
Normal file
@ -0,0 +1,144 @@
|
||||
<cn>
|
||||
#### 动态增减表单项
|
||||
动态增加、减少表单项。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Dynamic Form Item
|
||||
Add or remove form items dynamically.
|
||||
</us>
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Form } from 'vue-antd-ui'
|
||||
|
||||
let uuid = 0
|
||||
const DynamicFieldSet = {
|
||||
methods: {
|
||||
remove (k) {
|
||||
const { form } = this
|
||||
// can use data-binding to get
|
||||
const keys = form.getFieldValue('keys')
|
||||
// We need at least one passenger
|
||||
if (keys.length === 1) {
|
||||
return
|
||||
}
|
||||
|
||||
// can use data-binding to set
|
||||
form.setFieldsValue({
|
||||
keys: keys.filter(key => key !== k),
|
||||
})
|
||||
},
|
||||
|
||||
add () {
|
||||
const { form } = this
|
||||
// can use data-binding to get
|
||||
const keys = form.getFieldValue('keys')
|
||||
const nextKeys = keys.concat(uuid)
|
||||
uuid++
|
||||
// can use data-binding to set
|
||||
// important! notify form to detect changes
|
||||
form.setFieldsValue({
|
||||
keys: nextKeys,
|
||||
})
|
||||
},
|
||||
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
this.form.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
console.log('Received values of form: ', values)
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const { getFieldDecorator, getFieldValue } = this.form
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 4 },
|
||||
},
|
||||
wrapperCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 20 },
|
||||
},
|
||||
}
|
||||
const formItemLayoutWithOutLabel = {
|
||||
wrapperCol: {
|
||||
xs: { span: 24, offset: 0 },
|
||||
sm: { span: 20, offset: 4 },
|
||||
},
|
||||
}
|
||||
getFieldDecorator('keys', { initialValue: [] })
|
||||
const keys = getFieldValue('keys')
|
||||
const formItems = keys.map((k, index) => {
|
||||
return (
|
||||
<a-form-item
|
||||
{...{ props: (index === 0 ? formItemLayout : formItemLayoutWithOutLabel) }}
|
||||
label={index === 0 ? 'Passengers' : ''}
|
||||
required={false}
|
||||
key={k}
|
||||
>
|
||||
{getFieldDecorator(`names[${k}]`, {
|
||||
validateTrigger: ['onChange', 'onBlur'],
|
||||
rules: [{
|
||||
required: true,
|
||||
whitespace: true,
|
||||
message: "Please input passenger's name or delete this field.",
|
||||
}],
|
||||
})(
|
||||
<a-input placeholder='passenger name' style={{ width: '60%', marginRight: '8px' }} />
|
||||
)}
|
||||
{keys.length > 1 ? (
|
||||
<a-icon
|
||||
class='dynamic-delete-button'
|
||||
type='minus-circle-o'
|
||||
disabled={keys.length === 1}
|
||||
onClick={() => this.remove(k)}
|
||||
/>
|
||||
) : null}
|
||||
</a-form-item>
|
||||
)
|
||||
})
|
||||
return (
|
||||
<a-form onSubmit={this.handleSubmit}>
|
||||
{formItems}
|
||||
<a-form-item {...{ props: formItemLayoutWithOutLabel }}>
|
||||
<a-button type='dashed' onClick={this.add} style={{ width: '60%' }}>
|
||||
<a-icon type='plus' /> Add field
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
<a-form-item {...{ props: formItemLayoutWithOutLabel }}>
|
||||
<a-button type='primary' htmlType='submit'>Submit</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export default Form.create()(DynamicFieldSet)
|
||||
</script>
|
||||
<style>
|
||||
.dynamic-delete-button {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
font-size: 24px;
|
||||
color: #999;
|
||||
transition: all .3s;
|
||||
}
|
||||
.dynamic-delete-button:hover {
|
||||
color: #777;
|
||||
}
|
||||
.dynamic-delete-button[disabled] {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
94
components/form/demo/dynamic-rule.md
Normal file
94
components/form/demo/dynamic-rule.md
Normal file
@ -0,0 +1,94 @@
|
||||
<cn>
|
||||
#### 动态校验规则
|
||||
根据不同情况执行不同的校验规则。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Dynamic Rules
|
||||
Perform different check rules according to different situations.
|
||||
</us>
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Form } from 'vue-antd-ui'
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: { span: 4 },
|
||||
wrapperCol: { span: 8 },
|
||||
}
|
||||
const formTailLayout = {
|
||||
labelCol: { span: 4 },
|
||||
wrapperCol: { span: 8, offset: 4 },
|
||||
}
|
||||
const DynamicRule = {
|
||||
data () {
|
||||
return {
|
||||
checkNick: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
check () {
|
||||
this.form.validateFields(
|
||||
(err) => {
|
||||
if (!err) {
|
||||
console.info('success')
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
handleChange (e) {
|
||||
this.checkNick = e.target.checked
|
||||
this.$nextTick(() => {
|
||||
this.form.validateFields(['nickname'], { force: true })
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const { getFieldDecorator } = this.form
|
||||
return (
|
||||
<div>
|
||||
<a-form-item {...{ props: formItemLayout }} label='Name'>
|
||||
{getFieldDecorator('username', {
|
||||
rules: [{
|
||||
required: true,
|
||||
message: 'Please input your name',
|
||||
}],
|
||||
})(
|
||||
<a-input placeholder='Please input your name' />
|
||||
)}
|
||||
</a-form-item>
|
||||
<a-form-item {...{ props: formItemLayout }} label='Nickname'>
|
||||
{getFieldDecorator('nickname', {
|
||||
rules: [{
|
||||
required: this.checkNick,
|
||||
message: 'Please input your nickname',
|
||||
}],
|
||||
})(
|
||||
<a-input placeholder='Please input your nickname' />
|
||||
)}
|
||||
</a-form-item>
|
||||
<a-form-item {...{ props: formTailLayout }}>
|
||||
<a-checkbox
|
||||
value={this.checkNick}
|
||||
onChange={this.handleChange}
|
||||
>
|
||||
Nickname is required
|
||||
</a-checkbox>
|
||||
</a-form-item>
|
||||
<a-form-item {...{ props: formTailLayout }}>
|
||||
<a-button type='primary' onClick={this.check}>
|
||||
Check
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export default Form.create()(DynamicRule)
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
|
105
components/form/demo/form-in-modal.md
Normal file
105
components/form/demo/form-in-modal.md
Normal file
@ -0,0 +1,105 @@
|
||||
<cn>
|
||||
#### 弹出层中的新建表单
|
||||
当用户访问一个展示了某个列表的页面,想新建一项但又不想跳转页面时,可以用 Modal 弹出一个表单,用户填写必要信息后创建新的项。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Form in Modal to Create
|
||||
When user visit a page with a list of items, and want to create a new item. The page can popup a form in Modal, then let user fill in the form to create an item.
|
||||
</us>
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Form } from 'vue-antd-ui'
|
||||
|
||||
const CollectionCreateForm = Form.create()(
|
||||
{
|
||||
props: ['visible'],
|
||||
render () {
|
||||
const { visible, form } = this
|
||||
const { getFieldDecorator } = form
|
||||
return (
|
||||
<a-modal
|
||||
visible={visible}
|
||||
title='Create a new collection'
|
||||
okText='Create'
|
||||
onCancel={() => { this.$emit('cancel') }}
|
||||
onOk={() => { this.$emit('create') }}
|
||||
>
|
||||
<a-form layout='vertical'>
|
||||
<a-form-item label='Title'>
|
||||
{getFieldDecorator('title', {
|
||||
rules: [{ required: true, message: 'Please input the title of collection!' }],
|
||||
})(
|
||||
<a-input />
|
||||
)}
|
||||
</a-form-item>
|
||||
<a-form-item label='Description'>
|
||||
{getFieldDecorator('description')(<a-input type='textarea' />)}
|
||||
</a-form-item>
|
||||
<a-form-item className='collection-create-form_last-form-item'>
|
||||
{getFieldDecorator('modifier', {
|
||||
initialValue: 'public',
|
||||
})(
|
||||
<a-radio-group>
|
||||
<a-radio value='public'>Public</a-radio>
|
||||
<a-radio value='private'>Private</a-radio>
|
||||
</a-radio-group>
|
||||
)}
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
visible: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showModal () {
|
||||
this.visible = true
|
||||
},
|
||||
handleCancel () {
|
||||
this.visible = false
|
||||
},
|
||||
handleCreate () {
|
||||
const form = this.formRef.form
|
||||
form.validateFields((err, values) => {
|
||||
if (err) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log('Received values of form: ', values)
|
||||
form.resetFields()
|
||||
this.visible = false
|
||||
})
|
||||
},
|
||||
saveFormRef (formRef) {
|
||||
this.formRef = formRef
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<a-button type='primary' onClick={this.showModal}>New Collection</a-button>
|
||||
<CollectionCreateForm
|
||||
wrappedComponentRef={this.saveFormRef}
|
||||
visible={this.visible}
|
||||
onCancel={this.handleCancel}
|
||||
onCreate={this.handleCreate}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
|
92
components/form/demo/global-state.md
Normal file
92
components/form/demo/global-state.md
Normal file
@ -0,0 +1,92 @@
|
||||
<cn>
|
||||
#### 表单数据存储于上层组件
|
||||
通过使用 `onFieldsChange` 与 `mapPropsToFields`,可以把表单的数据存储到上层组件。
|
||||
**注意:**
|
||||
`mapPropsToFields` 里面返回的表单域数据必须使用 `Form.createFormField` 包装。
|
||||
上层组件传递的属性,必须在`Form.create({ props: ...})`的props中声明。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Store Form Data into Upper Component
|
||||
We can store form data into upper component.
|
||||
**Note:**
|
||||
You must wrap field data with `Form.createFormField` in `mapPropsToFields`.
|
||||
The properties passed by the upper component must be declared in the props of `Form.create({ props: ...})`.
|
||||
</us>
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Form } from 'vue-antd-ui'
|
||||
|
||||
const CustomizedForm = Form.create({
|
||||
props: ['username'], // must declare like vue `props` https://vuejs.org/v2/api/#props
|
||||
onFieldsChange (instance, changedFields) {
|
||||
instance.$emit('change', changedFields)
|
||||
},
|
||||
mapPropsToFields (props) {
|
||||
return {
|
||||
username: Form.createFormField({
|
||||
...props.username,
|
||||
value: props.username.value,
|
||||
}),
|
||||
}
|
||||
},
|
||||
onValuesChange (_, values) {
|
||||
console.log(values)
|
||||
},
|
||||
})({
|
||||
render () {
|
||||
const { getFieldDecorator } = this.form
|
||||
return (
|
||||
<a-form layout='inline'>
|
||||
<a-form-item label='Username'>
|
||||
{getFieldDecorator('username', {
|
||||
rules: [{ required: true, message: 'Username is required!' }],
|
||||
})(<a-input />)}
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
)
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
fields: {
|
||||
username: {
|
||||
value: 'benjycui',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleFormChange (changedFields) {
|
||||
this.fields = { ...this.fields, ...changedFields }
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const fields = this.fields
|
||||
return (
|
||||
<div id='components-form-demo-global-state'>
|
||||
<CustomizedForm {...{ props: fields }} onChange={this.handleFormChange} />
|
||||
<pre class='language-bash'>
|
||||
{JSON.stringify(fields, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
#components-form-demo-global-state .language-bash {
|
||||
max-width: 400px;
|
||||
border-radius: 6px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
|
||||
|
82
components/form/demo/horizontal-login.md
Normal file
82
components/form/demo/horizontal-login.md
Normal file
@ -0,0 +1,82 @@
|
||||
<cn>
|
||||
#### 水平登录栏
|
||||
水平登录栏,常用在顶部导航栏中。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Horizontal Login Form
|
||||
Horizontal login form is often used in navigation bar.
|
||||
</us>
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Form } from 'vue-antd-ui'
|
||||
|
||||
function hasErrors (fieldsError) {
|
||||
return Object.keys(fieldsError).some(field => fieldsError[field])
|
||||
}
|
||||
|
||||
const HorizontalLoginForm = {
|
||||
mounted () {
|
||||
// To disabled submit button at the beginning.
|
||||
this.form.validateFields()
|
||||
},
|
||||
methods: {
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
this.form.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
console.log('Received values of form: ', values)
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const { getFieldDecorator, getFieldsError, getFieldError, isFieldTouched } = this.form
|
||||
|
||||
// Only show error after a field is touched.
|
||||
const userNameError = isFieldTouched('userName') && getFieldError('userName')
|
||||
const passwordError = isFieldTouched('password') && getFieldError('password')
|
||||
return (
|
||||
<a-form layout='inline' onSubmit={this.handleSubmit}>
|
||||
<a-form-item
|
||||
validateStatus={userNameError ? 'error' : ''}
|
||||
help={userNameError || ''}
|
||||
>
|
||||
{getFieldDecorator('userName', {
|
||||
rules: [{ required: true, message: 'Please input your username!' }],
|
||||
})(
|
||||
<a-input prefix={<a-icon type='user' style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder='Username' />
|
||||
)}
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
validateStatus={passwordError ? 'error' : ''}
|
||||
help={passwordError || ''}
|
||||
>
|
||||
{getFieldDecorator('password', {
|
||||
rules: [{ required: true, message: 'Please input your Password!' }],
|
||||
})(
|
||||
<a-input prefix={<a-icon type='lock' style={{ color: 'rgba(0,0,0,.25)' }} />} type='password' placeholder='Password' />
|
||||
)}
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button
|
||||
type='primary'
|
||||
htmlType='submit'
|
||||
disabled={hasErrors(getFieldsError())}
|
||||
>
|
||||
Log in
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export default Form.create()(HorizontalLoginForm)
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
|
81
components/form/demo/layout.md
Normal file
81
components/form/demo/layout.md
Normal file
@ -0,0 +1,81 @@
|
||||
<cn>
|
||||
#### 表单布局
|
||||
表单有三种布局。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Form Layout
|
||||
There are three layout for form: `horizontal`, `vertical`, `inline`.
|
||||
</us>
|
||||
|
||||
```html
|
||||
<template>
|
||||
<div>
|
||||
<a-form :layout="formLayout">
|
||||
<a-form-item
|
||||
label='Form Layout'
|
||||
:labelCol="formItemLayout.labelCol"
|
||||
:wrapperCol="formItemLayout.wrapperCol"
|
||||
>
|
||||
<a-radio-group defaultValue='horizontal' @change="handleFormLayoutChange">
|
||||
<a-radio-button value='horizontal'>Horizontal</a-radio-button>
|
||||
<a-radio-button value='vertical'>Vertical</a-radio-button>
|
||||
<a-radio-button value='inline'>Inline</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label='Field A'
|
||||
:labelCol="formItemLayout.labelCol"
|
||||
:wrapperCol="formItemLayout.wrapperCol"
|
||||
>
|
||||
<a-input placeholder='input placeholder' />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label='Field B'
|
||||
:labelCol="formItemLayout.labelCol"
|
||||
:wrapperCol="formItemLayout.wrapperCol"
|
||||
>
|
||||
<a-input placeholder='input placeholder' />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
:wrapperCol="buttonItemLayout.wrapperCol"
|
||||
>
|
||||
<a-button type='primary'>Submit</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
formLayout: 'horizontal',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleFormLayoutChange (e) {
|
||||
this.formLayout = e.target.value
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
formItemLayout () {
|
||||
const { formLayout } = this
|
||||
return formLayout === 'horizontal' ? {
|
||||
labelCol: { span: 4 },
|
||||
wrapperCol: { span: 14 },
|
||||
} : {}
|
||||
},
|
||||
buttonItemLayout () {
|
||||
const { formLayout } = this
|
||||
return formLayout === 'horizontal' ? {
|
||||
wrapperCol: { span: 14, offset: 4 },
|
||||
} : {}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
|
@ -1,64 +1,67 @@
|
||||
<script>
|
||||
import { Form } from 'vue-antd-ui'
|
||||
<template>
|
||||
<div>
|
||||
<a-form :layout="formLayout">
|
||||
<a-form-item
|
||||
label='Form Layout'
|
||||
:labelCol="formItemLayout.labelCol"
|
||||
:wrapperCol="formItemLayout.wrapperCol"
|
||||
>
|
||||
<a-radio-group defaultValue='horizontal' @change="handleFormLayoutChange">
|
||||
<a-radio-button value='horizontal'>Horizontal</a-radio-button>
|
||||
<a-radio-button value='vertical'>Vertical</a-radio-button>
|
||||
<a-radio-button value='inline'>Inline</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label='Field A'
|
||||
:labelCol="formItemLayout.labelCol"
|
||||
:wrapperCol="formItemLayout.wrapperCol"
|
||||
>
|
||||
<a-input placeholder='input placeholder' />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label='Field B'
|
||||
:labelCol="formItemLayout.labelCol"
|
||||
:wrapperCol="formItemLayout.wrapperCol"
|
||||
>
|
||||
<a-input placeholder='input placeholder' />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
:wrapperCol="buttonItemLayout.wrapperCol"
|
||||
>
|
||||
<a-button type='primary'>Submit</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
const NormalLoginForm = {
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
formLayout: 'horizontal',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
this.form.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
console.log('Received values of form: ', values)
|
||||
}
|
||||
})
|
||||
handleFormLayoutChange (e) {
|
||||
this.formLayout = e.target.value
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const { getFieldDecorator } = this.form
|
||||
return (
|
||||
<a-form id='components-form-demo-normal-login' onSubmit={this.handleSubmit} class='login-form'>
|
||||
<a-form-item>
|
||||
{getFieldDecorator('userName', {
|
||||
rules: [{ required: true, message: 'Please input your username!' }],
|
||||
})(
|
||||
<a-input prefix={<a-icon type='user' style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder='Username' />
|
||||
)}
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
{getFieldDecorator('password', {
|
||||
rules: [{ required: true, message: 'Please input your Password!' }],
|
||||
})(
|
||||
<a-input prefix={<a-icon type='lock' style={{ color: 'rgba(0,0,0,.25)' }} />} type='password' placeholder='Password' />
|
||||
)}
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
{getFieldDecorator('remember', {
|
||||
valuePropName: 'checked',
|
||||
initialValue: true,
|
||||
})(
|
||||
<a-checkbox>Remember me</a-checkbox>
|
||||
)}
|
||||
<a class='login-form-forgot' href=''>Forgot password</a>
|
||||
<a-button type='primary' htmlType='submit' class='login-form-button'>
|
||||
Log in
|
||||
</a-button>
|
||||
Or <a href=''>register now!</a>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
)
|
||||
computed: {
|
||||
formItemLayout () {
|
||||
const { formLayout } = this
|
||||
return formLayout === 'horizontal' ? {
|
||||
labelCol: { span: 4 },
|
||||
wrapperCol: { span: 14 },
|
||||
} : {}
|
||||
},
|
||||
buttonItemLayout () {
|
||||
const { formLayout } = this
|
||||
return formLayout === 'horizontal' ? {
|
||||
wrapperCol: { span: 14, offset: 4 },
|
||||
} : {}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default Form.create()(NormalLoginForm)
|
||||
</script>
|
||||
<style>
|
||||
#components-form-demo-normal-login .login-form {
|
||||
max-width: 300px;
|
||||
}
|
||||
#components-form-demo-normal-login .login-form-forgot {
|
||||
float: right;
|
||||
}
|
||||
#components-form-demo-normal-login .login-form-button {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
@ -7,7 +7,7 @@ import createFieldsStore from './createFieldsStore'
|
||||
import { cloneElement } from '../../_util/vnode'
|
||||
import BaseMixin from '../../_util/BaseMixin'
|
||||
import { getOptionProps, getEvents } from '../../_util/props-util'
|
||||
// import PropTypes from '../../_util/vue-types'
|
||||
import PropTypes from '../../_util/vue-types'
|
||||
|
||||
import {
|
||||
argumentContainer,
|
||||
@ -34,17 +34,24 @@ function createBaseForm (option = {}, mixins = []) {
|
||||
fieldMetaProp,
|
||||
fieldDataProp,
|
||||
formPropName = 'form',
|
||||
// @deprecated
|
||||
withRef,
|
||||
props = {},
|
||||
} = option
|
||||
|
||||
return function decorate (WrappedComponent) {
|
||||
let formProps = {}
|
||||
if (Array.isArray(props)) {
|
||||
props.forEach((prop) => {
|
||||
formProps[prop] = PropTypes.any
|
||||
})
|
||||
} else {
|
||||
formProps = props
|
||||
}
|
||||
const Form = {
|
||||
mixins: [BaseMixin, ...mixins],
|
||||
// props: {
|
||||
// hideRequiredMark: PropTypes.bool,
|
||||
// layout: PropTypes.string,
|
||||
// },
|
||||
props: {
|
||||
...formProps,
|
||||
wrappedComponentRef: PropTypes.func.def(() => {}),
|
||||
},
|
||||
data () {
|
||||
const fields = mapPropsToFields && mapPropsToFields(this.$props)
|
||||
this.fieldsStore = createFieldsStore(fields || {})
|
||||
@ -81,6 +88,9 @@ function createBaseForm (option = {}, mixins = []) {
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.wrappedComponentRef(this.$refs.WrappedComponent)
|
||||
},
|
||||
methods: {
|
||||
onCollectCommon (name, action, args) {
|
||||
const fieldMeta = this.fieldsStore.getFieldMeta(name)
|
||||
@ -554,17 +564,22 @@ function createBaseForm (option = {}, mixins = []) {
|
||||
...props,
|
||||
}),
|
||||
on: $listeners,
|
||||
}
|
||||
if (withRef) {
|
||||
wrappedComponentProps.ref = 'wrappedComponent'
|
||||
ref: 'WrappedComponent',
|
||||
}
|
||||
return <WrappedComponent {...wrappedComponentProps}/>
|
||||
},
|
||||
}
|
||||
if (!(WrappedComponent.props && formPropName in WrappedComponent.props)) {
|
||||
WrappedComponent.props = {
|
||||
...WrappedComponent.props,
|
||||
[formPropName]: Object,
|
||||
if (Array.isArray(WrappedComponent.props)) {
|
||||
const newProps = {}
|
||||
WrappedComponent.props.forEach((prop) => {
|
||||
newProps[prop] = PropTypes.any
|
||||
})
|
||||
newProps[formPropName] = Object
|
||||
WrappedComponent.props = newProps
|
||||
} else {
|
||||
WrappedComponent.props = WrappedComponent.props || {}
|
||||
if (!(formPropName in WrappedComponent.props)) {
|
||||
WrappedComponent.props[formPropName] = Object
|
||||
}
|
||||
}
|
||||
return argumentContainer(Form, WrappedComponent)
|
||||
|
Loading…
Reference in New Issue
Block a user