Skip to content

Commit 322bf3f

Browse files
committed
feat: add form
1 parent b89c002 commit 322bf3f

File tree

11 files changed

+901
-71
lines changed

11 files changed

+901
-71
lines changed

components/form/FormItem.jsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ export default {
9797
getChildAttr (prop) {
9898
const child = this.getOnlyControl()
9999
let data = {}
100+
if (!child) {
101+
return undefined
102+
}
100103
if (child.data) {
101104
data = child.data
102105
} else if (child.$vnode && child.$vnode.data) {

components/form/demo/coordinated.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<cn>
2+
#### 表单联动
3+
使用 `setFieldsValue` 来动态设置其他控件的值。
4+
</cn>
5+
6+
<us>
7+
#### Coordinated Controls
8+
Use `setFieldsValue` to set other control's value programmaticly.
9+
</us>
10+
11+
```html
12+
<script>
13+
import { Form } from 'vue-antd-ui'
14+
15+
const CoordinatedForm = {
16+
methods: {
17+
handleSubmit (e) {
18+
e.preventDefault()
19+
this.form.validateFields((err, values) => {
20+
if (!err) {
21+
console.log('Received values of form: ', values)
22+
}
23+
})
24+
},
25+
handleSelectChange (value) {
26+
console.log(value)
27+
this.form.setFieldsValue({
28+
note: `Hi, ${value === 'male' ? 'man' : 'lady'}!`,
29+
})
30+
},
31+
},
32+
33+
render () {
34+
const { getFieldDecorator } = this.form
35+
return (
36+
<a-form onSubmit={this.handleSubmit}>
37+
<a-form-item
38+
label='Note'
39+
labelCol={{ span: 5 }}
40+
wrapperCol={{ span: 12 }}
41+
>
42+
{getFieldDecorator('note', {
43+
rules: [{ required: true, message: 'Please input your note!' }],
44+
})(
45+
<a-input />
46+
)}
47+
</a-form-item>
48+
<a-form-item
49+
label='Gender'
50+
labelCol={{ span: 5 }}
51+
wrapperCol={{ span: 12 }}
52+
>
53+
{getFieldDecorator('gender', {
54+
rules: [{ required: true, message: 'Please select your gender!' }],
55+
})(
56+
<a-select
57+
placeholder='Select a option and change input text above'
58+
onChange={this.handleSelectChange}
59+
>
60+
<a-select-option value='male'>male</a-select-option>
61+
<a-select-option value='female'>female</a-select-option>
62+
</a-select>
63+
)}
64+
</a-form-item>
65+
<a-form-item
66+
wrapperCol={{ span: 12, offset: 5 }}
67+
>
68+
<a-button type='primary' htmlType='submit'>
69+
Submit
70+
</a-button>
71+
</a-form-item>
72+
</a-form>
73+
)
74+
},
75+
}
76+
77+
export default Form.create()(CoordinatedForm)
78+
</script>
79+
```
80+
81+
82+
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
<cn>
2+
#### 自定义表单控件
3+
自定义或第三方的表单控件,也可以与 Form 组件一起使用。只要该组件遵循以下的约定:
4+
> * 提供受控属性 `value` 或其它与 [`valuePropName`](/ant-design/components/form-cn/#getFieldDecorator(id,-options)-参数) 的值同名的属性。
5+
> * 提供 `onChange` 事件或 [`trigger`](/ant-design/components/form-cn/#getFieldDecorator(id,-options)-参数) 的值同名的事件。
6+
> * 不能是函数式组件。
7+
</cn>
8+
9+
<us>
10+
#### Customized Form Controls
11+
Customized or third-party form controls can be used in Form, too. Controls must follow these conventions:
12+
> * 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).
13+
> * It has event `onChange` or an event which name is equal to the value of [`trigger`](/ant-design/components/form/#getFieldDecorator(id,-options)-parameters).
14+
> * It must be a class component.
15+
</us>
16+
17+
```html
18+
<script>
19+
import { Form } from 'vue-antd-ui'
20+
21+
const hasProp = (instance, prop) => {
22+
const $options = instance.$options || {}
23+
const propsData = $options.propsData || {}
24+
return prop in propsData
25+
}
26+
const PriceInput = {
27+
props: ['value'],
28+
data () {
29+
const value = this.value || {}
30+
return {
31+
number: value.number || 0,
32+
currency: value.currency || 'rmb',
33+
}
34+
},
35+
watch: {
36+
value (val = {}) {
37+
this.number = val.number || 0
38+
this.currency = val.currency || 'rmb'
39+
},
40+
},
41+
methods: {
42+
handleNumberChange (e) {
43+
const number = parseInt(e.target.value || 0, 10)
44+
if (isNaN(number)) {
45+
return
46+
}
47+
if (!hasProp(this, 'value')) {
48+
this.number = number
49+
}
50+
this.triggerChange({ number })
51+
},
52+
handleCurrencyChange (currency) {
53+
if (!hasProp(this, 'value')) {
54+
this.currency = currency
55+
}
56+
this.triggerChange({ currency })
57+
},
58+
triggerChange (changedValue) {
59+
// Should provide an event to pass value to Form.
60+
this.$emit('change', Object.assign({}, this.$data, changedValue))
61+
},
62+
},
63+
64+
render () {
65+
const { number, currency } = this
66+
return (
67+
<span>
68+
<a-input
69+
type='text'
70+
value={number}
71+
onChange={this.handleNumberChange}
72+
style={{ width: '65%', marginRight: '3%' }}
73+
/>
74+
<a-select
75+
value={currency}
76+
style={{ width: '32%' }}
77+
onChange={this.handleCurrencyChange}
78+
>
79+
<a-select-option value='rmb'>RMB</a-select-option>
80+
<a-select-option value='dollar'>Dollar</a-select-option>
81+
</a-select>
82+
</span>
83+
)
84+
},
85+
}
86+
87+
const Demo = {
88+
methods: {
89+
handleSubmit (e) {
90+
e.preventDefault()
91+
this.form.validateFields((err, values) => {
92+
if (!err) {
93+
console.log('Received values of form: ', values)
94+
}
95+
})
96+
},
97+
checkPrice (rule, value, callback) {
98+
if (value.number > 0) {
99+
callback()
100+
return
101+
}
102+
callback('Price must greater than zero!')
103+
},
104+
},
105+
106+
render () {
107+
const { getFieldDecorator } = this.form
108+
return (
109+
<a-form layout='inline' onSubmit={this.handleSubmit}>
110+
<a-form-item label='Price'>
111+
{getFieldDecorator('price', {
112+
initialValue: { number: 0, currency: 'rmb' },
113+
rules: [{ validator: this.checkPrice }],
114+
})(<PriceInput />)}
115+
</a-form-item>
116+
<a-form-item>
117+
<a-button type='primary' htmlType='submit'>Submit</a-button>
118+
</a-form-item>
119+
</a-form>
120+
)
121+
},
122+
}
123+
124+
export default Form.create()(Demo)
125+
</script>
126+
```
127+
128+
129+
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<cn>
2+
#### 动态增减表单项
3+
动态增加、减少表单项。
4+
</cn>
5+
6+
<us>
7+
#### Dynamic Form Item
8+
Add or remove form items dynamically.
9+
</us>
10+
11+
```html
12+
<script>
13+
import { Form } from 'vue-antd-ui'
14+
15+
let uuid = 0
16+
const DynamicFieldSet = {
17+
methods: {
18+
remove (k) {
19+
const { form } = this
20+
// can use data-binding to get
21+
const keys = form.getFieldValue('keys')
22+
// We need at least one passenger
23+
if (keys.length === 1) {
24+
return
25+
}
26+
27+
// can use data-binding to set
28+
form.setFieldsValue({
29+
keys: keys.filter(key => key !== k),
30+
})
31+
},
32+
33+
add () {
34+
const { form } = this
35+
// can use data-binding to get
36+
const keys = form.getFieldValue('keys')
37+
const nextKeys = keys.concat(uuid)
38+
uuid++
39+
// can use data-binding to set
40+
// important! notify form to detect changes
41+
form.setFieldsValue({
42+
keys: nextKeys,
43+
})
44+
},
45+
46+
handleSubmit (e) {
47+
e.preventDefault()
48+
this.form.validateFields((err, values) => {
49+
if (!err) {
50+
console.log('Received values of form: ', values)
51+
}
52+
})
53+
},
54+
},
55+
56+
render () {
57+
const { getFieldDecorator, getFieldValue } = this.form
58+
const formItemLayout = {
59+
labelCol: {
60+
xs: { span: 24 },
61+
sm: { span: 4 },
62+
},
63+
wrapperCol: {
64+
xs: { span: 24 },
65+
sm: { span: 20 },
66+
},
67+
}
68+
const formItemLayoutWithOutLabel = {
69+
wrapperCol: {
70+
xs: { span: 24, offset: 0 },
71+
sm: { span: 20, offset: 4 },
72+
},
73+
}
74+
getFieldDecorator('keys', { initialValue: [] })
75+
const keys = getFieldValue('keys')
76+
const formItems = keys.map((k, index) => {
77+
return (
78+
<a-form-item
79+
{...{ props: (index === 0 ? formItemLayout : formItemLayoutWithOutLabel) }}
80+
label={index === 0 ? 'Passengers' : ''}
81+
required={false}
82+
key={k}
83+
>
84+
{getFieldDecorator(`names[${k}]`, {
85+
validateTrigger: ['onChange', 'onBlur'],
86+
rules: [{
87+
required: true,
88+
whitespace: true,
89+
message: "Please input passenger's name or delete this field.",
90+
}],
91+
})(
92+
<a-input placeholder='passenger name' style={{ width: '60%', marginRight: '8px' }} />
93+
)}
94+
{keys.length > 1 ? (
95+
<a-icon
96+
class='dynamic-delete-button'
97+
type='minus-circle-o'
98+
disabled={keys.length === 1}
99+
onClick={() => this.remove(k)}
100+
/>
101+
) : null}
102+
</a-form-item>
103+
)
104+
})
105+
return (
106+
<a-form onSubmit={this.handleSubmit}>
107+
{formItems}
108+
<a-form-item {...{ props: formItemLayoutWithOutLabel }}>
109+
<a-button type='dashed' onClick={this.add} style={{ width: '60%' }}>
110+
<a-icon type='plus' /> Add field
111+
</a-button>
112+
</a-form-item>
113+
<a-form-item {...{ props: formItemLayoutWithOutLabel }}>
114+
<a-button type='primary' htmlType='submit'>Submit</a-button>
115+
</a-form-item>
116+
</a-form>
117+
)
118+
},
119+
}
120+
121+
export default Form.create()(DynamicFieldSet)
122+
</script>
123+
<style>
124+
.dynamic-delete-button {
125+
cursor: pointer;
126+
position: relative;
127+
top: 4px;
128+
font-size: 24px;
129+
color: #999;
130+
transition: all .3s;
131+
}
132+
.dynamic-delete-button:hover {
133+
color: #777;
134+
}
135+
.dynamic-delete-button[disabled] {
136+
cursor: not-allowed;
137+
opacity: 0.5;
138+
}
139+
</style>
140+
141+
```
142+
143+
144+

0 commit comments

Comments
 (0)