文本框类的表单元素组件
说了这么多,还没看到代码,是不是等不急了呢?其实代码也没啥好说的,就是用了最笨的方法,一点一点设置属性。
Vue.component('my-input', { props:["value","meta"], data:function(){ return { type:{ //把编号变成文本的形式 100:'textarea', //多行文本框 101:'text', //单行文本框 102:'password', //密码 103:'date', //日期 //其他略。。。。。。 } } }, methods:{ //text textInput:function(event, meta){ var returnValue = event.target.value; //添加自己的监听事件。本来想写在一起的,但是不好用,只好分开了。 //vue的回调 this.$emit('input',returnValue); }, textChange:function(event, meta){ var returnValue = event.target.value; //添加自己的监听事件 this.$emit('change', returnValue,event.target, meta); } }, template: `<span> <span v-if="meta.controlType===100"> <!--多行文本框--> <textarea :id="'c'+meta.controlId" :name="'c'+meta.controlId" :class="meta.class" :readonly="meta.readonly" :rows="meta.rows" :cols="meta.cols" @input="$emit('input',$event.target.value)" @onkeyup="textChange($event,meta)" :placeholder="meta.placeholder" > </textarea> </span> <span v-else-if="meta.controlType>100 && meta.controlType<130 "> <!--文本框类--> <input :id="'c'+meta.controlId" :name="'c'+meta.controlId" :disabled="meta.disabled" :class="meta.class" :type="type[meta.controlType]" :value="value" :placeholder="meta.placeholder" :readonly="meta.readonly" :size="meta.size" :maxlength="meta.maxlength" :autocomplete="meta.autocomplete" :min="meta.min" :max="meta.max" :step="meta.step" :multiple="meta.multiple" :list="meta.listKey" :title="meta.title" @input="textInput($event,meta)" @change="textChange($event,meta)" :key="'ckey_'+meta.controlId"> <!--文本框的备选项--> <datalist v-if="typeof(meta.listKey)!=='undefined'" :id="meta.listKey"> <option v-for="item in meta.list" :label="item.id" :value="item.name" /> </datalist> </span> });
1、属性
value用于双向绑定,这个要单独设置,其他的属性统统放在meta里面。这样接口就固定了,以后需要新的属性也不用修改接口。
2、内部变量
这个是为了做个替换,因为外部设置的是类型编号,而不是类型名称,所以内部需要做一个替换,这样浏览器才能识别。
那么为啥用编号,而不直接用浏览器支持的类型呢?因为有些类型要做两种用途,比如file上传文件和上传图片。两种方式要做个区分的,比如上传图片,可以做个图片预览,图片处理等功能,上传文件的话,就没有这些了。所以要做个编号加以区分。另外像多行文本框和下拉列表框用的不是input,没有type。
3、模板
这里就很笨了,用v-if根据controlType做判断,是哪种控件就渲染对应的模板。然后把属性一一绑定上就可以了。
然后就是事件的绑定。因为用户输入内容后,要通知上层调用者,所以需要加个事件返回用户输入值。第一个input是给Vue准备的,加上这个才能实现Vue的双向绑定。
那么第二个事件是干啥的?有的时候我们自己需要知道用户的输入操作,依据输入做些操作,比如联动下拉列表框。我们要知道第一个下拉列表框的change,然后设置第二个下拉列表框。这个时候就需要我们自己的事件通知。一开始想在一个函数里通知两个上层事件的,但是没有成功。所以只好分开了。Emmm,也许可以改成数据驱动的方式,这个还没太想好。
4、方法
写了两个方法,一个是返回给Vue的,实现数据双向绑定。另一个是给我们自己用的。
选择类的表单元素组件
选择类指的是多选组(checkbox)、单选组(radio)、复选框(checkbox)以及下拉列表框。
Vue.component('my-input', { props:["value","meta"], methods:{ //select selectChange:function(event, meta){ var returnValue = ''; var items = event.target.selectedOptions; //选中项的集合 var arr = []; for (var i=0;i<items.length;i++) { var item = items[i]; arr.push(item.value); } returnValue = arr.join(','); //添加联动事件 this.$emit('select', returnValue, meta.nextSelect); //vue的回调 this.$emit('input',returnValue); return returnValue; }, //CheckBox checkChange: function (event) { var returnValue = event.target.value; if (this.meta.controlType === 155) { //复选框 returnValue = event.target.checked; } else{ //修改绑定情况 var selectValue = returnValue; var arr = []; for (var key in this.meta.list) { var item = this.meta.list[key]; if (item.id === selectValue) { this.meta.list[key].check = event.target.checked; } if (item.check) { arr.push(item.id); } } returnValue = arr.join(','); } //调用上级的input事件 this.$emit('input',returnValue); return returnValue; }, //radio radioChange: function (event, meta) { //单选 var returnValue = ''; var items = event.target.selectedOptions; //选中项的集合 var arr = []; for (var i=0;i<items.length;i++) { var item = items[i]; arr.push(item.id); } returnValue = arr.join(','); this.$emit('select', returnValue, meta.nextSelect); //添加联动事件 return returnValue; } }, template: `<span> <span v-else-if="meta.controlType >= 150 && meta.controlType <= 152 "> <!--下拉列表框--> <select :id="'c'+meta.controlId" :name="'c'+meta.controlId" :class="meta.class" :multiple="meta.controlType === 151" @change="selectChange($event,meta)" > <option :key="-2" value="-2" >请选择</option> <option v-for="(item,index) in meta.list" :key="index" :value="item.id" :selected="item.check"> {{item.name}} </option> </select> </span> <span v-else-if="meta.controlType === 153 "> <!--单选组 --> <label role="radio" v-for="item in meta.list" > <input type="radio" :class="meta.class" :checked="item.check" :name="'c'+meta.controlId" :value="item.id" @input="$emit('input',$event.target.value)" > <span>{{item.name}}</span> </label> </span> <span v-else-if="meta.controlType === 154 "> <!--多选组--> <label role="checkbox" v-for="item in meta.list" class="checkbox_g_t" :key="'lblchks'+item.id" > <input :id="'c'+meta.controlId" type="checkbox" :name="'c'+meta.controlId" :class="meta.class" :value="item.id" :readonly="meta.readonly" :key="'chks'+item.id" @input="checkChange($event)" > <span>{{item.name}}</span> </label> </span> <span v-else-if="meta.controlType === 155 "> <!--复选框--> <label role="checkbox" v-for="item in meta.list" class="checkbox_g_t" :key="'lblchk'+item.id" > <input :id="'c'+meta.controlId" type="checkbox" :name="'c'+meta.controlId" :class="meta.class" :value="item.id" :readonly="meta.readonly" :key="'chk'+item.id" @input="checkChange($event)" > <span>{{item.name}}</span> </label> </span> </span>` });
- 模板
还是老办法,用v-if判断渲染哪个模板,然后还是一个一个赋值,然后选项有一个循环,v-for一下就可以了。这里的选项格式和文本框的备选项格式采用了相同的设置。这样统一一下比较方便。 - 方法
每类控件都做一个方法,对应不同的取值方式。不知道有没有更好的方式,现在用的比较麻烦,期待更好的方法。如果发现了肯定会更新的。
还有个返回值类型的问题,我是习惯返回字符串的形式,比如1,2,3 。而不是数组。因为数据库里保存的是字符串而不是数组。当然这块应该能够灵活一些,打算加一个返回值类型的设置。
辅助工具
这么复杂的json要怎么弄?不会告诉我要手撸吧!当然不是,我这么懒怎么能手写呢,当然是弄个工具来辅助了。
辅助工具的思路,首先确定要哪种类型的表单元素,然后根据类型显示需要设置的属性,然后就可以点点点了(当然有些属性需要打几个字),就可以生成json文件,同时还可以预览效果。
这个只是第一步哦,后面的还会有根据文档生成的辅助工具。
文档在哪里?做项目总会有个数据库文档吧,文档会描述都有啥表,啥字段。会介绍一下字段名称、字段类型、字段大小吧。这样我们就可以根据这些信息设置默认的json了。然后不能默认的再点点点一下就可以了。
这个辅助工具就是用的这个表单元素组件写的,也算是一个实际应用,代码比较多,就不贴了。感兴趣的话,看下面开源介绍。
开源
源码下载:https://github.com/naturefwvue
在线演示:https://naturefwvue.github.io/form/formHelp.html
这里是表单元素组件源码和demo,还有那个辅助工具。
另外会保持持续更新的,毕竟现在还只是初步学习vue实现的也只是简单的功能。
下图是辅助工具的页面,首先选择类型,然后预留会有变化,然后按照下面的属性选择即可,同时预览也会有对应的变化。
最后感谢大家的支持!