仿element-ui封装一个通用的表单组件
需求分析
最终理想效果:element Form 表单
创建components/form/JInput.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <template> <div> <!-- 自定义组件双向绑定::value @input --> <!-- v-bind="$attrs"展开$attrs 每一个项都可以单独设置上去 --> <input :type="type" :value="value" @input="onInput" v-bind="$attrs"> </div> </template> <script> export default { inheritAttrs: false, // 设置为false避免attrs设置到根元素上 props: { value: { type: String, default: '' }, type: { type: String, default: 'text' } }, methods: { onInput(e) { // 派发一个input事件即可 this.$emit('input', e.target.value) } }, } </script>
|
创建components/form/index.vue,添加如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <template> <div> <h3>JForm表单</h3><hr> <j-input v-model="model.username"></j-input> <j-input type="password" v-model="model.password"></j-input> </div> </template> <script> import JInput from "./JInput"; export default { components: { JInput }, data() { return { model: { username: "Joker", password: "" }, }; } }; </script>
|
创建components/form/JFormItem.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <template> <div> <label v-if="label">{{label}}</label> <solt></solt> <p v-if="error">{{error}}</p> </div> </template> <script> export default { props:{ label:{//输入项标签 type:String, default:'' } }, data(){ return{ error:''//校验错误信息 } } } </script>
|
在components/form/index.vue中添加基础代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <template> <div> <h3>JForm表单</h3><hr> <j-form-item label="用户名"> <j-input v-model="model.username"></j-input> </j-form-item> <j-form-item label="密码"> <j-input type="password" v-model="model.password"></j-input> </j-form-item> </div> </template> <script> import JInput from "./JInput"; import JFormItem from "./JFormItem"; export default { components: { JInput,JFormItem }, data() { return { model: { username: "Joker", password: "" }, }; } }; </script>
|
创建components/form/JForm.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <template> <form> <solt></solt> <form> </template> <script> export default { provide() { return { form: this // 将组件实例作为提供者,子代组件可方便获取 }; }, props: { model: { type: Object, required: true }, rules: { type: Object } } } </script>
|
在components/form/index.vue中添加基础代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| <template> <div> <!-- JForm --> <JForm :model="userInfo" :rules="rules" ref="loginForm"> <!-- 用户名 --> <JFormItem label="用户名" prop="username"> <JInput v-model="userInfo.username" placeholder="请输入用户名"></JInput> </JFormItem> <!-- 密码 --> <JFormItem label="密码" prop="password"> <JInput type="password" v-model="userInfo.password" placeholder="请输入用户名"></JInput> </JFormItem> <!-- 提交按钮 --> <JFormItem> <button @click="login">登录</button> </JFormItem> </JForm> </div> </template> <script> import JInput from "./JInput"; import JFormItem from "./JFormItem"; import JForm from "./JForm";
export default { components: { JInput,JFormItem,JForm }, data() { return { rules: { username: [{ required: true, message: "请输入用户名" }], password: [{ required: true, message: "请输入密码" }] }, model: { username: "Joker", password: "" }, }; }, methods:{ login(){ this.$refs['loginForm'].validate(valid => { if(valid){ alert("请求登录") }else{ alert("请求失败") } }) } } }; </script>
|
数据校验
JInput中通知校验,修改components/form/JInput.vue中代码
1 2 3 4 5 6 7 8 9 10
| <!-- dom --> <input :type="type" :value="value" @input="onInput" v-bind="$attrs">
<!-- methods --> onInput(e) { // 派发一个input事件即可 this.$emit('input', e.target.value) // 通知父级执行校验 this.$parent.$emit('validate') }
|
JFormItem监听校验通知,获取规则并执行校验,修改components/form/JFormItem.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <template> <div> <label v-if="label">{{label}}</label> <solt></solt> <p v-if="error">{{error}}</p> </div> </template> <script> export default { inject:['form'],//注入 mounted(){//监听校验事件 this.$on('validate',()=>{this.validate()}) }, methods:{ validate(){ //获取对应JFormItem的校验规则 console.log(this.form.rules[this.prop]); //获取对应JFormItem的值 console.log(this.form.model[this.prop]); } }, props:{ label:{//输入项标签 type:String, default:'' }, prop: { type: String } }, data(){ return{ error:''//校验错误信息 } } } </script>
|
安装校验库async-validator: npm i async-validator -S
在components/form/JFormItem.vue中引入,并添加校验代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import Schema from "async-validator"; ... validate(){ const rules = this.form.rules[this.prop]; const value = this.form.model[this.prop]; const desc = {[this.prop]:rules}; const schema = new Schema(desc) return schema.validate({[this.prop]:value},errors =>{ if(errors){ this.error=errors[0].message }else{ this.error='' } }) }
|
表单全局校验
为JForm提供validate方法,修改components/form/JForm.vue
1 2 3 4 5 6 7 8 9 10 11
| validate(cb){ const task = this.$children .filter(item => item.prop) .map(item => item.validate()) Promise.all(tasks) .then(()=> cb(true)) .catch(()=> cb(false)) }
|