提交 95df5bf9 编写于 作者: richard_1015's avatar richard_1015

feat(form): add blur、change validate & fix #902

上级 c834450e
......@@ -44,6 +44,8 @@ export const component = {
prop: vnode.props?.['prop'],
rules: vnode.props?.['rules'] || []
});
} else if (vnode.children?.length) {
task = task.concat(findFormItem(vnode.children as VNode[]));
}
});
return task;
......@@ -66,7 +68,12 @@ export const component = {
});
};
const value = props.modelValue[prop];
const getPropByPath = (obj: any, keyPath: string) => {
return keyPath.split('.').reduce((prev, curr) => prev[curr], obj);
};
let value = getPropByPath(props.modelValue, prop);
// clear tips
tipMessage({ prop, message: '' });
......@@ -104,12 +111,25 @@ export const component = {
return Promise.resolve(true);
};
const validate = () => {
/**
* 校验
* @param customProp 指定校验,用于用户自定义场景时触发,例如 blur、change 事件
* @returns
*/
const validate = (customProp: string = '') => {
return new Promise((resolve, reject) => {
let task = findFormItem(slots.default());
let errors = task.map((item) => {
return checkRule(item);
if (customProp) {
if (customProp == item.prop) {
return checkRule(item);
} else {
return Promise.resolve(true);
}
} else {
return checkRule(item);
}
});
Promise.all(errors).then((errorRes) => {
......
......@@ -18,10 +18,37 @@
<nut-textarea placeholder="请输入备注" type="text" />
</nut-form-item>
</nut-form>
<h2>动态表单</h2>
<nut-form :model-value="dynamicForm.state" ref="dynamicRefForm">
<nut-form-item label="姓名" prop="name" required :rules="[{ required: true, message: '请填写姓名' }]">
<input class="nut-input-text" v-model="dynamicForm.state.name" placeholder="请输入姓名" type="text" />
</nut-form-item>
<nut-form-item
:label="'联系方式' + index"
:prop="'tels.' + index + '.value'"
required
:rules="[{ required: true, message: '请填写联系方式' + index }]"
:key="item.key"
v-for="(item, index) in dynamicForm.state.tels"
>
<input class="nut-input-text" v-model="item.value" :placeholder="'请输入联系方式' + index" type="text" />
</nut-form-item>
<nut-cell>
<nut-button size="small" style="margin-right: 10px" @click="dynamicForm.methods.add">添加</nut-button>
<nut-button size="small" style="margin-right: 10px" @click="dynamicForm.methods.remove">删除</nut-button>
<nut-button type="primary" size="small" @click="dynamicForm.methods.submit">提交</nut-button>
</nut-cell>
</nut-form>
<h2>表单校验</h2>
<nut-form :model-value="formData" ref="ruleForm">
<nut-form-item label="姓名" prop="name" required :rules="[{ required: true, message: '请填写姓名' }]">
<input class="nut-input-text" v-model="formData.name" placeholder="请输入姓名" type="text" />
<input
class="nut-input-text"
@blur="customBlurValidate('name')"
v-model="formData.name"
placeholder="请输入姓名,blur 事件校验"
type="text"
/>
</nut-form-item>
<nut-form-item
label="年龄"
......@@ -130,6 +157,40 @@ export default createDemo({
tel: '',
address: ''
});
const dynamicRefForm = ref<any>(null);
const dynamicForm = {
state: reactive({
name: '',
tels: new Array({
key: 1,
value: ''
})
}),
methods: {
submit() {
dynamicRefForm.value.validate().then(({ valid, errors }: any) => {
if (valid) {
console.log('success', dynamicForm);
} else {
console.log('error submit!!', errors);
}
});
},
remove() {
dynamicForm.state.tels.splice(dynamicForm.state.tels.length - 1, 1);
},
add() {
let newIndex = dynamicForm.state.tels.length;
dynamicForm.state.tels.push({
key: Date.now(),
value: ''
});
}
}
};
const validate = (item: any) => {
console.log(item);
};
......@@ -213,6 +274,16 @@ export default createDemo({
const reset = () => {
ruleForm.value.reset();
};
// 失去焦点校验
const customBlurValidate = (prop: string) => {
ruleForm.value.validate(prop).then(({ valid, errors }: any) => {
if (valid) {
console.log('success', formData);
} else {
console.log('error submit!!', errors);
}
});
};
// 函数校验
const customValidator = (val: string) => /^\d+$/.test(val);
// Promise 异步校验
......@@ -225,7 +296,20 @@ export default createDemo({
}, 1000);
});
};
return { ruleForm, formData, validate, customValidator, asyncValidator, submit, reset, formData2, addressModule };
return {
ruleForm,
formData,
validate,
customValidator,
asyncValidator,
customBlurValidate,
submit,
reset,
formData2,
addressModule,
dynamicForm,
dynamicRefForm
};
}
});
</script>
......
......@@ -44,33 +44,98 @@ app.use(CellGroup);
</nut-form>
```
### 动态表单
``` html
<nut-form :model-value="dynamicForm.state" ref="dynamicRefForm">
<nut-form-item label="姓名" prop="name" required :rules="[{ required: true, message: '请填写姓名' }]">
<input class="nut-input-text" v-model="dynamicForm.state.name" placeholder="请输入姓名" type="text" />
</nut-form-item>
<nut-form-item :label="'联系方式'+index" :prop="'tels.' + index + '.value'" required
:rules="[{ required: true, message: '请填写联系方式'+index }]" :key="item.key"
v-for="(item,index) in dynamicForm.state.tels">
<input class="nut-input-text" v-model="item.value" :placeholder="'请输入联系方式'+index" type="text" />
</nut-form-item>
<nut-cell>
<nut-button size="small" style="margin-right: 10px" @click="dynamicForm.methods.add">添加</nut-button>
<nut-button size="small" style="margin-right: 10px" @click="dynamicForm.methods.remove">删除</nut-button>
<nut-button type="primary" size="small" @click="dynamicForm.methods.submit">提交</nut-button>
</nut-cell>
</nut-form>
```
``` javascript
setup(){
const dynamicRefForm = ref<any>(null);
const dynamicForm = {
state: reactive({
name: '',
tels: new Array({
key: 1,
value: ''
})
}),
methods: {
submit() {
dynamicRefForm.value.validate().then(({ valid, errors }: any) => {
if (valid) {
console.log('success', dynamicForm);
} else {
console.log('error submit!!', errors);
}
});
},
remove() {
dynamicForm.state.tels.splice(dynamicForm.state.tels.length - 1, 1);
},
add() {
let newIndex = dynamicForm.state.tels.length;
dynamicForm.state.tels.push({
key: Date.now(),
value: ''
});
}
}
};
return {
dynamicForm,
dynamicRefForm
};
}
```
### 表单校验
``` html
<nut-form :model-value="formData" ref="ruleForm">
<nut-form-item label="姓名" prop="name" required :rules="[{ required: true, message: '请填写姓名' }]">
<input class="nut-input-text" v-model="formData.name" placeholder="请输入姓名" type="text" />
</nut-form-item>
<nut-form-item label="年龄" prop="age" required :rules="[
{ required: true, message: '请填写年龄' },
{ validator: customValidator, message: '必须输入数字' },
{ regex: /^(\d{1,2}|1\d{2}|200)$/, message: '必须输入0-200区间' }
<nut-form-item label="姓名" prop="name" required :rules="[{ required: true, message: '请填写姓名' }]">
<input class="nut-input-text" @blur="customBlurValidate('name')" v-model="formData.name"
placeholder="请输入姓名,blur 事件校验" type="text" />
</nut-form-item>
<nut-form-item label="年龄" prop="age" required :rules="[
{ required: true, message: '请填写年龄' },
{ validator: customValidator, message: '必须输入数字' },
{ regex: /^(\d{1,2}|1\d{2}|200)$/, message: '必须输入0-200区间' }
]">
<input class="nut-input-text" v-model="formData.age" placeholder="请输入年龄,必须数字且0-200区间" type="text" />
</nut-form-item>
<nut-form-item label="联系电话" prop="tel" required :rules="[
{ required: true, message: '请填写联系电话' },
{ validator: asyncValidator, message: '电话格式不正确' }
<input class="nut-input-text" v-model="formData.age" placeholder="请输入年龄,必须数字且0-200区间" type="text" />
</nut-form-item>
<nut-form-item label="联系电话" prop="tel" required :rules="[
{ required: true, message: '请填写联系电话' },
{ validator: asyncValidator, message: '电话格式不正确' }
]">
<input class="nut-input-text" v-model="formData.tel" placeholder="请输入联系电话,异步校验电话格式" type="text" />
</nut-form-item>
<nut-form-item label="地址" prop="address" required :rules="[{ required: true, message: '请填写地址' }]">
<input class="nut-input-text" v-model="formData.address" placeholder="请输入地址" type="text" />
</nut-form-item>
<nut-cell>
<nut-button type="primary" size="small" style="margin-right: 10px" @click="submit">提交</nut-button>
<nut-button size="small" @click="reset">重置提示状态</nut-button>
</nut-cell>
<input class="nut-input-text" v-model="formData.tel" placeholder="请输入联系电话,异步校验电话格式" type="text" />
</nut-form-item>
<nut-form-item label="地址" prop="address" required :rules="[{ required: true, message: '请填写地址' }]">
<input class="nut-input-text" v-model="formData.address" placeholder="请输入地址" type="text" />
</nut-form-item>
<nut-cell>
<nut-button type="primary" size="small" style="margin-right: 10px" @click="submit">提交</nut-button>
<nut-button size="small" @click="reset">重置提示状态</nut-button>
</nut-cell>
</nut-form>
```
``` javascript
......@@ -98,6 +163,16 @@ setup(){
const reset = () => {
ruleForm.value.reset();
};
// 失去焦点校验
const customBlurValidate = (prop: string) => {
ruleForm.value.validate(prop).then(({ valid, errors }: any) => {
if (valid) {
console.log('success', formData);
} else {
console.log('error submit!!', errors);
}
});
};
// 函数校验
const customValidator = (val: string) => /^\d+$/.test(val);
// Promise 异步校验
......@@ -110,7 +185,7 @@ setup(){
}, 1000);
});
};
return { ruleForm, formData, validate, customValidator, asyncValidator, submit, reset, formData2, addressModule };
return { ruleForm, formData, validate, customValidator, asyncValidator, customBlurValidate, submit, reset };
}
```
......@@ -240,15 +315,16 @@ setup(){
### FormItem Props
| 参数 | 说明 | 类型 | 默认值 |
|---------------------|--------------------------------------------------|------------------|---------|
| required | 是否显示必填字段的标签旁边的红色星号 | boolean | `false` |
| label-width | 表单项 label 宽度,默认单位为`px` | number \| string | `90px` |
| label-align | 表单项 label 对齐方式,可选值为 `center` `right` | string | `left` |
| body-align | 输入框对齐方式,可选值为 `center` `right` | string | `left` |
| error-message-align | 错误提示文案对齐方式,可选值为 `center` `right` | string | `left` |
| show-error-line | 是否在校验不通过时标红输入框 | boolean | `true` |
| show-error-message | 是否在校验不通过时在输入框下方展示错误提示 | boolean | `true` |
| 参数 | 说明 | 类型 | 默认值 |
|---------------------|------------------------------------------------------------------|------------------|---------|
| required | 是否显示必填字段的标签旁边的红色星号 | boolean | `false` |
| prop | 表单域 v-model 字段, 在使用表单校验功能的情况下,该属性是必填的 | string | - |
| label-width | 表单项 label 宽度,默认单位为`px` | number \| string | `90px` |
| label-align | 表单项 label 对齐方式,可选值为 `center` `right` | string | `left` |
| body-align | 输入框对齐方式,可选值为 `center` `right` | string | `left` |
| error-message-align | 错误提示文案对齐方式,可选值为 `center` `right` | string | `left` |
| show-error-line | 是否在校验不通过时标红输入框 | boolean | `true` |
| show-error-message | 是否在校验不通过时在输入框下方展示错误提示 | boolean | `true` |
### FormItem Rule 数据结构
......@@ -265,7 +341,8 @@ setup(){
通过 [ref](https://v3.cn.vuejs.org/api/special-attributes.html#ref) 可以获取到 Form 实例并调用实例方法
| 方法名 | 说明 | 参数 | 返回值 |
|--------|------------------------|------|---------|
| submit | 提交表单进行校验的方法 | - | Promise |
| reset | 清空校验结果 | - | - |
\ No newline at end of file
| 方法名 | 说明 | 参数 | 返回值 |
|-------------------|--------------------------------------------------------------------|---------------------|---------|
| submit | 提交表单进行校验的方法 | - | Promise |
| reset | 清空校验结果 | - | - |
| validate`v3.1.13` | 用户主动触发校验,用于用户自定义场景时触发,例如 blur、change 事件 | 同 FormItem prop 值 | - |
\ No newline at end of file
......@@ -25,7 +25,7 @@
font-weight: normal;
width: 90px;
margin-right: 10px;
flex: none;
flex: none !important;
display: inline-block;
word-wrap: break-word;
&.required {
......
......@@ -34,7 +34,7 @@ export const component = (componentName: string) => {
}
},
emits: ['sorter'],
setup(props, { emit, slots }) {
setup(props: any, { emit, slots }: any) {
const state = reactive({
curData: props.data
});
......
......@@ -18,10 +18,37 @@
<nut-textarea placeholder="请输入备注" type="text" />
</nut-form-item>
</nut-form>
<h2>动态表单</h2>
<nut-form :model-value="dynamicForm.state" ref="dynamicRefForm">
<nut-form-item label="姓名" prop="name" required :rules="[{ required: true, message: '请填写姓名' }]">
<input class="nut-input-text" v-model="dynamicForm.state.name" placeholder="请输入姓名" type="text" />
</nut-form-item>
<nut-form-item
:label="'联系方式' + index"
:prop="'tels.' + index + '.value'"
required
:rules="[{ required: true, message: '请填写联系方式' + index }]"
:key="item.key"
v-for="(item, index) in dynamicForm.state.tels"
>
<input class="nut-input-text" v-model="item.value" :placeholder="'请输入联系方式' + index" type="text" />
</nut-form-item>
<nut-cell>
<nut-button size="small" style="margin-right: 10px" @click="dynamicForm.methods.add">添加</nut-button>
<nut-button size="small" style="margin-right: 10px" @click="dynamicForm.methods.remove">删除</nut-button>
<nut-button type="primary" size="small" @click="dynamicForm.methods.submit">提交</nut-button>
</nut-cell>
</nut-form>
<h2>表单校验</h2>
<nut-form :model-value="formData" ref="ruleForm">
<nut-form-item label="姓名" prop="name" required :rules="[{ required: true, message: '请填写姓名' }]">
<input class="nut-input-text" v-model="formData.name" placeholder="请输入姓名" type="text" />
<input
class="nut-input-text"
@blur="customBlurValidate('name')"
v-model="formData.name"
placeholder="请输入姓名,blur 事件校验"
type="text"
/>
</nut-form-item>
<nut-form-item
label="年龄"
......@@ -127,6 +154,40 @@ export default {
tel: '',
address: ''
});
const dynamicRefForm = ref<any>(null);
const dynamicForm = {
state: reactive({
name: '',
tels: new Array({
key: 1,
value: ''
})
}),
methods: {
submit() {
dynamicRefForm.value.validate().then(({ valid, errors }: any) => {
if (valid) {
console.log('success', dynamicForm);
} else {
console.log('error submit!!', errors);
}
});
},
remove() {
dynamicForm.state.tels.splice(dynamicForm.state.tels.length - 1, 1);
},
add() {
let newIndex = dynamicForm.state.tels.length;
dynamicForm.state.tels.push({
key: Date.now(),
value: ''
});
}
}
};
const validate = (item: any) => {
console.log(item);
};
......@@ -210,6 +271,16 @@ export default {
const reset = () => {
ruleForm.value.reset();
};
// 失去焦点校验
const customBlurValidate = (prop: string) => {
ruleForm.value.validate(prop).then(({ valid, errors }: any) => {
if (valid) {
console.log('success', formData);
} else {
console.log('error submit!!', errors);
}
});
};
// 函数校验
const customValidator = (val: string) => /^\d+$/.test(val);
// Promise 异步校验
......@@ -222,7 +293,20 @@ export default {
}, 1000);
});
};
return { ruleForm, formData, validate, customValidator, asyncValidator, submit, reset, formData2, addressModule };
return {
ruleForm,
formData,
validate,
customValidator,
asyncValidator,
customBlurValidate,
submit,
reset,
formData2,
addressModule,
dynamicForm,
dynamicRefForm
};
}
};
</script>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册