feat: add form
parent
790fe9c4b9
commit
138274f9c6
|
@ -97,6 +97,9 @@ export default {
|
||||||
getChildAttr (prop) {
|
getChildAttr (prop) {
|
||||||
const child = this.getOnlyControl()
|
const child = this.getOnlyControl()
|
||||||
let data = {}
|
let data = {}
|
||||||
|
if (!child) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
if (child.data) {
|
if (child.data) {
|
||||||
data = child.data
|
data = child.data
|
||||||
} else if (child.$vnode && child.$vnode.data) {
|
} else if (child.$vnode && child.$vnode.data) {
|
||||||
|
|
|
@ -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>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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>
|
<template>
|
||||||
import { Form } from 'vue-antd-ui'
|
<div>
|
||||||
|
<a-form :layout="formLayout">
|
||||||
const NormalLoginForm = {
|
<a-form-item
|
||||||
methods: {
|
label='Form Layout'
|
||||||
handleSubmit (e) {
|
:labelCol="formItemLayout.labelCol"
|
||||||
e.preventDefault()
|
:wrapperCol="formItemLayout.wrapperCol"
|
||||||
this.form.validateFields((err, values) => {
|
>
|
||||||
if (!err) {
|
<a-radio-group defaultValue='horizontal' @change="handleFormLayoutChange">
|
||||||
console.log('Received values of form: ', values)
|
<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>
|
||||||
},
|
|
||||||
|
|
||||||
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>
|
||||||
<a-form-item>
|
<a-form-item
|
||||||
{getFieldDecorator('password', {
|
label='Field A'
|
||||||
rules: [{ required: true, message: 'Please input your Password!' }],
|
:labelCol="formItemLayout.labelCol"
|
||||||
})(
|
:wrapperCol="formItemLayout.wrapperCol"
|
||||||
<a-input prefix={<a-icon type='lock' style={{ color: 'rgba(0,0,0,.25)' }} />} type='password' placeholder='Password' />
|
>
|
||||||
)}
|
<a-input placeholder='input placeholder' />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item
|
||||||
{getFieldDecorator('remember', {
|
label='Field B'
|
||||||
valuePropName: 'checked',
|
:labelCol="formItemLayout.labelCol"
|
||||||
initialValue: true,
|
:wrapperCol="formItemLayout.wrapperCol"
|
||||||
})(
|
>
|
||||||
<a-checkbox>Remember me</a-checkbox>
|
<a-input placeholder='input placeholder' />
|
||||||
)}
|
</a-form-item>
|
||||||
<a class='login-form-forgot' href=''>Forgot password</a>
|
<a-form-item
|
||||||
<a-button type='primary' htmlType='submit' class='login-form-button'>
|
:wrapperCol="buttonItemLayout.wrapperCol"
|
||||||
Log in
|
>
|
||||||
</a-button>
|
<a-button type='primary'>Submit</a-button>
|
||||||
Or <a href=''>register now!</a>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</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 },
|
||||||
|
} : {}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Form.create()(NormalLoginForm)
|
|
||||||
</script>
|
</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 { cloneElement } from '../../_util/vnode'
|
||||||
import BaseMixin from '../../_util/BaseMixin'
|
import BaseMixin from '../../_util/BaseMixin'
|
||||||
import { getOptionProps, getEvents } from '../../_util/props-util'
|
import { getOptionProps, getEvents } from '../../_util/props-util'
|
||||||
// import PropTypes from '../../_util/vue-types'
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
argumentContainer,
|
argumentContainer,
|
||||||
|
@ -34,17 +34,24 @@ function createBaseForm (option = {}, mixins = []) {
|
||||||
fieldMetaProp,
|
fieldMetaProp,
|
||||||
fieldDataProp,
|
fieldDataProp,
|
||||||
formPropName = 'form',
|
formPropName = 'form',
|
||||||
// @deprecated
|
props = {},
|
||||||
withRef,
|
|
||||||
} = option
|
} = option
|
||||||
|
|
||||||
return function decorate (WrappedComponent) {
|
return function decorate (WrappedComponent) {
|
||||||
|
let formProps = {}
|
||||||
|
if (Array.isArray(props)) {
|
||||||
|
props.forEach((prop) => {
|
||||||
|
formProps[prop] = PropTypes.any
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
formProps = props
|
||||||
|
}
|
||||||
const Form = {
|
const Form = {
|
||||||
mixins: [BaseMixin, ...mixins],
|
mixins: [BaseMixin, ...mixins],
|
||||||
// props: {
|
props: {
|
||||||
// hideRequiredMark: PropTypes.bool,
|
...formProps,
|
||||||
// layout: PropTypes.string,
|
wrappedComponentRef: PropTypes.func.def(() => {}),
|
||||||
// },
|
},
|
||||||
data () {
|
data () {
|
||||||
const fields = mapPropsToFields && mapPropsToFields(this.$props)
|
const fields = mapPropsToFields && mapPropsToFields(this.$props)
|
||||||
this.fieldsStore = createFieldsStore(fields || {})
|
this.fieldsStore = createFieldsStore(fields || {})
|
||||||
|
@ -81,6 +88,9 @@ function createBaseForm (option = {}, mixins = []) {
|
||||||
deep: true,
|
deep: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mounted () {
|
||||||
|
this.wrappedComponentRef(this.$refs.WrappedComponent)
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onCollectCommon (name, action, args) {
|
onCollectCommon (name, action, args) {
|
||||||
const fieldMeta = this.fieldsStore.getFieldMeta(name)
|
const fieldMeta = this.fieldsStore.getFieldMeta(name)
|
||||||
|
@ -554,17 +564,22 @@ function createBaseForm (option = {}, mixins = []) {
|
||||||
...props,
|
...props,
|
||||||
}),
|
}),
|
||||||
on: $listeners,
|
on: $listeners,
|
||||||
}
|
ref: 'WrappedComponent',
|
||||||
if (withRef) {
|
|
||||||
wrappedComponentProps.ref = 'wrappedComponent'
|
|
||||||
}
|
}
|
||||||
return <WrappedComponent {...wrappedComponentProps}/>
|
return <WrappedComponent {...wrappedComponentProps}/>
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if (!(WrappedComponent.props && formPropName in WrappedComponent.props)) {
|
if (Array.isArray(WrappedComponent.props)) {
|
||||||
WrappedComponent.props = {
|
const newProps = {}
|
||||||
...WrappedComponent.props,
|
WrappedComponent.props.forEach((prop) => {
|
||||||
[formPropName]: Object,
|
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)
|
return argumentContainer(Form, WrappedComponent)
|
||||||
|
|
Loading…
Reference in New Issue