这次开发了个小TRS系统,虽然是很小,但是作为初心者,第一次用到了很多看起来洋气使用起来有相对简单的各种前端(主要是和bootstrap配合使用)组件。包括bootstrap-select2,bootstrap-datetimepicker,bootstrap-fileinput等。本文就旨在记录一些这些组件相关的内容
【bootstrap-select2】
官方文档:【https://select2.org/options】
这个组件主要用于优化
<link href="{% static 'select2/dist/css/select2.min.css' %}" rel="stylesheet" /> <script src="{% static 'select2/dist/js/select2.min.js' %}">script> <script src="{% static 'select2/dist/js/i18n/zh-CN.js' %}">script>
zh-CN.js是语言翻译文件,需要注意引入必须在select2.min.js后面,否则会报错。这一点也适用于绝大多数支持国际化显示的组件。
当然因为是bootstrap的组件,自然是不能少bootstrap的js和css以及支持bootstrap的jquery了,这个就不写出来了。
■ 带静态搜索框的下拉菜单
所谓静态搜索框,就是指这个下拉菜单里所有的option都是在页面渲染时就已经固化好了的,用这个搜索框进行搜索时不会动态发请求到后台去取数据。这个搜索框的HTML可以这么写:
<select id="static_dropdown" class="form-control"> <option>1option> <option>2option> <option>3option> select>
这就是一个普通的select,只不过需要有form-control这个class和一个用于后续表示用的id。
然后在js中这么写:
$('#static_dropdown').select2({ language: 'zh-CN', width: '100%', placeholder: '请选择', minimumInputLength: 10 });
利用前端组件一个非常重要的内容就是把握初始化时各个初始化参数的含义。通过对不同参数发出调整来搞出一个符合自己需求的组件。
这里用到的四个参数,language显而易见是国际化显示用的。width指出了这个select的宽度,placeholder就是placeholder...,minimunInputLength指出了在搜索时至少要输入多少字符才会给出响应的搜索结果。如果待选项不是太多的话尽量不要设置过大的值,像示例的10,我觉得是很大的一个值了。。
下面也会零散性地给出一些参数说明:
selectOnClose 当设置成true时,当收起菜单时的高亮项会自动被选择,如果是false则不会自动选择。
dropdownParent 可以指定下拉菜单在html代码中的位置。默认情况下,下拉菜单都是被append到body中,并且被设置成position:absolute,且位置在select的下方。而设置这个参数比如dropdownParent: $('#myModal'),就可以将下拉菜单放入一个指定的容器中。
tags 把tags设置成true了之后,在搜索时即便没有搜索到相关内容,也会把你输入的内容当成一个可选项。之所以被称为tags,就是因为tags这个东西不一定要都是既存的,可以有非既存而我们自己添加的情况。
placeholder 如果值是一个字符串那么就是一个简单的placeholder,如果换成一个对象也可以,即为菜单添加一个默认的选项。比如placeholder: {id:-1,text:'--请选择--'}
tokenSeparators 可以设置成一个包含各种字符的列表如[',',' '],常和tags以及多选菜单配合使用。即自己输入时,输入逗号或空格(看你这个参数设置的情况)时认为是一个tag的终结,就把前面的内容作为一个新tag添加到多选框里面。
createTag 这个参数的值也是一个函数对象。这个函数接受一个参数param,其结构是这样的
我们主要关注term这一项的值,我们可以在createTag指定的函数中对term进行一些逻辑判断和处理,然后返回一些结果来让添加标签的过程更加智能化一些。比如:
$('#testselect').select2({ createTag: function(param){ var term = $.trim(param.term); if (term.indexOf('@') == -1){ return null; } else { var id = term.split('@')[0]; var text = term.split('@')[1]; return {id:id,text:text}; } } });
在用户输入自定义的tag的时候可以检查存不存在@,如果不就不允许其增加这个tag,如果存在则@前面的部分作为value,后面的部分作为
minimunResultsForSearch 通常这个参数设置成一个整数,当option的待选数量大于这个数字时才显示搜索框。当把这个参数设置为Infinity时将默认不显示搜索框。这样select就变成了一个simple的select了。
maximumSelectionLength 若是多选框,设置最多可以选择几项
另外,可以在select和option中引入一层optgroup标签并设置其label属性。optgroup可以为所有选项划分出合适的组分并且显示以label的标签。将option的disabled设置为disabled还可以禁止选择某一个选项。
● 带有图片的下拉菜单
上面的说明,得到的下拉菜单都是纯文字的。其实select2支持我们传递图片进去作为每个选项的一个修饰。这里就要用到templateResult这个参数。
首先要明确,我们虽然在html上写的是select和option,但是最终经过select2的加工呈现出来的是ul和li,每个li里面是当时option中的内容。在了解这一点的基础上,我们的templateResult参数是一个函数对象。这个函数对象接受一个object(这个object八成是select2内部定义的抽象了菜单选项的object)为参数并且返回字符串或jquery对象供select2渲染成菜单选项。简单来说,就是select2会对所有选项进行一个遍历,依次执行一下这个函数然后按照我们定义的方式渲染出我们想要的菜单选项的样式和内容。比如下面这样:
templateResult: function(row){ return $(''+row.text+''); }
上面这个函数有点绕,简单来说就是可以把/static/img/<每个option设置好的value值>.png作为标识图片放在option的旁边。如果在这个函数中断点看下row的具体情况的话,就可以看到其实这个row的结构是这样的:
Object { selected: false, disabled: false, text: "张三", id: "10001", title: "", _resultId: "select2-addTrainSpeaker-result-5o5i-10001", element: option }
也就是说我们的option的value在这里是id,option的字符串在这里是text,这样就可以根据value和字符串来定制了。顺便一提,上面的函数中return要加上$()是因为不这样返回的默认是字符串,也就是每一项的内容都会变成xxxx的样子而不是html内容。所以返回jquery对象比较靠谱。
■ tag形式的多选框
所谓tag形式的多选框就是指这种:
这个看起来比较复杂,其实也通过select2实现也比较方便。html可以这么写:
<button class="close" type="button" id="multiple_all">+button> <select class="form-control" id="multiple_choice" multiple="multiple"> <option value="1">aoption> <option value="2">boption> <option value="3">coption> select>
然后在js中这样写:
var multiple = $('#multiple_choice').select2({ placeholder: '请选择', allowClear: true }); $('#multiple_all').click(function(event){ event.preventDefault(); var res = []; $(this).next('select').find('option').each(function(i,ele){ res.push($(ele).val()) }); $(multiple).val(res).trigger('change'); });
第一部分很好理解,和上面的下拉菜单一样,也是初始化。只是这个初始化返回了一个jquery对象,我们先用multiple这个变量保存下来。然后当select前面的那个按钮被点击时,就是执行了第二部分代码,是将select中所有选项的值都加入到一个数组中,然后把这个数组传递给刚才那个multiple对象的val方法,再trigger一下change事件。这样就可以自动地选择所有选项到多选框里面了。(这部分是我自己加上去,不是select2要求的)。
可以看出来$(multiple).val(xxx).trigger('change')就是拿来引发多选框被选中值变化的方法。如果xxx处填写null那么就是清空多选框了。
关于如何取值,类似的我们可以$('#multiple_choice').val()来取值,得到的应该是一个数组。这里多提一句,如何通过ajax传递数组给后台。直接写入ajax方法的data字段需要特殊处理过的数组。比如我们有数组对象array想要作为参数parti的值通过ajax传递出去的话,那么可以先JSON.stringify(array),得到的json化的字符串再拿来传递即可。在后台我们只要用相关后台的方法来解析json格式字符串即可。
■ 编程控制
上面提到了如何一键清空或全选多选框的选项、这个其实是一个select2组件的编程控制。更一般地来说我们可以这样给下拉菜单多增加一个选项:
var data = { id: 1, text: 'test' }; var newOption = new Option(data.text,data.id,false,false); $('#testselect').append(newOption).trigger('change');
如果是选取若干个可以传递一个数组给val方法。如果需要对待加入项是否已经存在做出判断的话可以这么做:
if ($('#testselect').find('option[value='+data.id+']').length){ $('#testselect').val(data.id).trigger('change'); //如果已经存在则不添加而是选择那一项 } else{ var newOption = new Option(data.id,data.text,true,true); $('#testselect').append(newOption).trigger('change'); }
■ 动态搜索下拉菜单
所谓动态,即每次搜索框中数据发生变化时,前端会向后台发送ajax请求来获取一些数据,把这些数据用于填充下拉菜单的内容。实现方法是在初始化select2组件的时候加入ajax参数,如:
$('#testselect').select2({ ajax : { url: '/api', dataType: 'json', //比较重要,没有的话返回json数据会无法识别,搜索失败 data: function(param){ return { keyword: param.term, searchType: 'public', page: param.page || 1 } } } })
ajax参数的值是一个object,包括url等key。url这个肯定是要有的,指出了ajax请求发往什么地方。data参数是可选的,在默认情况下select2发出的请求包含了以下几个参数:
term 此时搜索框中的内容
q 和term内容一样
_type 通常是query,如果涉及到结果分页时可能不一样
page 当有结果分页时有此参数,指出当前分页
在设置data参数之后,data参数的值是一个获取当前搜索框中对象(具体搜索内容是其term参数)并返回一个object作为ajax参数的函数对象,我们可以借此来定义一些自定义的请求参数字段。
需要特别说明的是,这里的ajax请求默认是get方法,目前还没找到如何改成post的办法。
因为前端会自动对后端传递过来的数据进行适应select2组件的渲染,所以其对于数据的格式肯定是有要求的。具体的要求是这样的:
{ results: [ {id: '10001', text: 'Option_1'}, {id: '10002', text: 'Option_2'}, //等等,这部分就是主要的数据 ], pagination: { more: true } }
主要数据部分每个列表项都是一个object且包含id和text两个字段分别用来填充option标签对应的那两个字段。pagination则是指出了前端会对上送的结果做一个分页处理,即一次性只在下拉列表中显示有限个项,但在最下方有一个“显示更多项”,当滚动条到最下方时再自动发送请求去调取下一个分页的数据。当然,顺利地实现分页需要前端发出的ajax请求中带有param.page这个信息(如上上面的代码所示),然后后端也要根据上送上来的page的值来进行相应数据段的返回。前后端协调了才可以给出比较好的分页效果。
上面的说明中可以看得出对数据格式要求比较严格(results,pagination,id,text等字段都不能自定义),为了适应更加多样化的形式的数据,我们可以在ajax参数中再加上一个processResults字段:
$('#testselect').select2({ ajax: { url: '/api', dataType: 'json', data: function(param){ return { kw: param.term, page: param.page || 1 }; }, processResults: function(data){ return { results: data.items }; } } })
上面的示例虽然有了分页,不过还有个小瑕疵,就是即使加载完了所有数据,最下面的加载所有数据依然会存在而不消失。这个问题可以在前端解决,官方文档中给出的示例是这样的:
processResults: function(data,params){ params.page = params.page || 1; return { results: data.results, pagination: { more: (params.page * 10) < data.total_count } }; }
首先,返回的结果中多了一个total_count字段,它指出了未分页时结果集的个数。注意是未分页时,即搜索条件不变时不论page是多少这个字段的值是不变的。然后在后端返回中的pagination字段可以去掉了,因为这个字段通过前端的逻辑判断来置true或false,这个条件,就是上面写着的当前页面*10是否小于总条数(也就是一次显示10条)。若否,表明已经加载到了最后一页,此时pagination就可以是false,即不需要再显示“加载更多数据”了。
● 更多ajax参数
delay 可以在ajax的参数中加上delay,取值单位是毫秒。意思是说当用户停止输入了这么多毫秒之后再发出ajax请求。用户每改动一个字母就发出一次请求显然是比较浪费资源的,这么做更加合理一些。
【bootstrap-datetimepicker】
datetimepicker是一个比较方便的日期时间插件。有了这个之后,我们可以在类似于表单的地方提供一个友好的日期(时间)输入功能。官方文档:【http://www.bootcss.com/p/bootstrap-datetimepicker/】
要使用datetimepicker组件,首先引入一些文件:
<link href="{% static 'bootstrap-datetimepicker/css/bootstrap-datetimepicker.min.css' %}" rel="stylesheet"> <script src="{% static 'bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js' %}">script> <script src="{% static 'bootstrap-datetimepicker/js/locales/bootstrap-datetimepicker.zh-CN.js' %}">script>
同样这里没有写出来bootstrap的css和js以及jquery,但都是也要包含的。
■ 典型应用:
HTML写成这样:
<div class="input-group date"> <input type="text" class="form-control" /> <span class="input-group-addon"> <span class="glyphicon glyphicon-calendar">span> span> div>
然后在js中这样写:
var today = new Date(); var format_td = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate(); $('.input-group.date').datetimepicker({ format: 'yyyy-mm-dd hh:00', language: 'zh-CN', todayBtn: 'linked', maxView: 3, minView: 1, autoClose: true, minuteStep: 30, startDate: format_td, pickerPosition: 'bottom-right' });
我们就可以在页面上得到一个输入框(type还是text的,不是date或者datetime),然后这个输入框右边会带有一个日历图标(input-group-addon),然后点击输入框或日历时会跳出一个日历来供用户选择具体的日期时间。选择完成后输入框中会出现特定格式的时间字符串,供表单提取数据或其他用。
下面来解释一下在这个初始化过程中,各个参数的含义:
format 指出了最后在输入框中的字符串的样式,上面这个yyyy-mm-dd hh都是格式化字符串,对应于类似"2017-01-01 08"(2017年1月1日上午8时)这样子。而:00是我固定写死的。更多的还有hh:ii:ss表示精确到秒的时间格式等,至于两位年份,一位小时数等等可以去官方文档看格式化字符串的写法。
language 国际化显示适应,需要引入bootstrap-datetimepicker/js/locales/bootstrap-datetimepicker.zh-CN.js才可顺利显示中文。
todayBtn 可以设置为true,false或者'linked'。让日历组件下方有一个“今天”按钮。说是“今天”,其实应该是“现在”,如果是true则点击后跳转到当前日期;如果是'linked'则点击时自动选中当前时间日期并且写入输入框
maxView,minView 日历组件可以根据需要分成好多不同的维度来看,这两个参数用来控制用户能够到达的最宏观和最微观的维度。值的各个数字的含义是:0-小时;1-天;2-月;3-年;4-十年。其默认值分别是0和4。另外还有startView参数用来指出点击调用日历控件时默认显示的维度。默认值是2
autoClose 当选择一个日期后关闭此日历控件
minuteStep 控制分钟选择的粒度。在时间下面,我们还可以选择具体到多少分钟。比如点击14:00,这个值是30的话,我们能够点击14:00和14:30
startDate,endDate 分别指出该日历能够选择的最早和最晚日期。从上面的代码中也可以看出来其取值是个yyyy-mm-dd格式的字符串。实际上也可以是个Date对象,也就是说这里写new Date()也完全可以。另外提一下,Date对象getMonth的值是从0开始到11的,getFullYear才是获取4位数年份的方法。
pickerPosition 指出日历控件出现相对于输入框的位置,默认值就是bottom-right,也可以top-left之类的
daysOfWeekDisabled 一个数组,指出一个星期的星期X不能被选择。需要注意的是这里day是西方那一套,也就是如果值是[0,6]就可以把周末都disabled掉,而非[6,7]。
初始化参数就说这么多吧,更多的可以参考文档
■ 编程控制和方法
datetimepicker方法不仅可以用于初始化控件,还可以在初始化完成之后,传入不同的参数来实现不同的编程控制。比如:
$('#testdp').datetimepicker('show') //显示日历 $('#testdp').datetimepicker('hide') //隐藏日历 $('#testdp').datetimepicker('update') //将输入框中的日期字符串解析并反映到日历汇总 $('#testdp').datetimepicker('setStartDate','2017-01-01') //或者null,用来设置日历最早可选日期 /*类似的很多参数也可以这么设置 比如 setEndDate setDaysOfWeekDisabled ... */
■ 事件
datetimepicker留出了一些事件的接口供我们调用比如
show , hide , changeDate,changeYear,changeMonth这些都一目了然
outOfRange 当用户试图在日历上选择超出了我们设置的范围(由startDate,endDate,daysOfWeekDisabled等决定)的日期时触发的事件
嗯 datetimepicker基本上就是这样了,没有什么很复杂的地方
【bootstrap-fileinput】
这是个据传最好用的bootstrap相关联的文件上传控件,支持拖曳上传,多线程上传,上传文件预览等等功能。
首先还是说一下要引入的一些文件:
<link href="{% static 'bootstrap-fileinput/css/fileinput.min.css' %}" rel="stylesheet" /> <script src="{% static 'bootstrap-fileinput/js/fileinput.min.js' %}">script> <script src="{% static 'bootstrap-fileinput/js/locales/zh.js' %}">script>
同样也需要jquery和bootstrap的支持。
■ 基本使用
HTML:
<label class="control-label" for="testfile">上传文件label> <input type="file" id="testfile" name="test" multiple />
JS:
$('#testfile').fileinput({ language: 'zh', uploadUrl: location.pathname + 'upload/', showCaption: true, showUpload: true, showRemove: true, showClose: true, layoutTemplates:{ actionDelete: '' }, browseClass: 'btn btn-primary' });
通过这样的初始化得到的上传组件大概是这样子的:
我们姑且把显示一个个文件的虚线区域称为文件区,每个文件标识称为文件缩略图
然后来解释一下各个参数的含义:
language指出国际化显示的语言环境,需要locales下面响应的js文件的支持;uploadUrl指出了点击上传之后会将上传文件的这个post请求发向哪个url。(uploadUrl这个参数其实还牵扯到了fileinput支持的两种不同的上传方式——form上传和ajax上传,在配置了uploadUrl时默认就是选择了ajax上传方式。fileinput对于ajax方式的上传支持更好,而拖曳上传等功能也只有在ajax上传方式中才奏效。关于具体的两种上传方式的区别后面再说,现在先默认所有操作都是基于ajax方式上传来做的。)
showCaption 默认true,设置为false时将不显示下面那个类似于input[type=text]那样的显示框(然而这个显示框并不能直接编辑,它只是显示了文件名等信息)
showUpload 默认true,设置为false是不显示右下角那个上传按钮
showRemove 默认为true,设置为false时不显示右下角移除按钮
showClose 默认为true,设置为false时不显示右上角的小叉(这个叉的作用就是移除当前所有文件区中的文件)
以上这些都是一些非文件区内的显示调整
layoutTemplates和文件缩略图的样式相关,暂且先不谈
browseClass指出了右下角选择按钮的样式。一般尽量不要用btn-sm和btn-lg,会和左边的输入框不协调。
● 更多初始化参数
showPreview 默认true,设置为false时默认不显示整个文件区,自然就无法拖曳文件进行上传了。
showCancel 默认true,只在ajax上传模式中可用,在上传过程中右下角有一个取消按钮,可以取消上传
showUploadedThumbs 默认为true,这个属性是基于这样一个使用方法的:选择若干个文件后点击右下角上传按钮批量上传,待全部完成后再选择一批文件,此时之前上传成功的文件是否要保存。就是这个属性控制的。注意,点击文件缩略图下面的上传按钮不会导致之前的成功上传的文件消失,即使这里设置了true
showBrowse fileinput支持部分文件格式(如各种图片格式,txt,pdf等)的预览,点击缩略图的右上角按钮即可,这个就是控制是否显示这个按钮的。
autoReplace,maxFileCount 这两个属性配合一起使用,即可以设置一个文件区存放的最多文件的数量,当要处理的文件个数超过这个数量时自动只选取最后的那几个进行展示和处理。
captionClass/previewClass/mainClass/frameClass 这四个class参数可以为各自部分增加一些class属性、mainClass针对元素.file-caption-main,frameClass针对每个缩略图的框架。
initialPreview 通过这个参数,我们可以为文件区设置一些默认的缩略图。通常可以用于一些附带的默认要上传的文件。参数的值写html代码即可,不过为了和其他部分统一,我们要用file-preview-image,file-preview-text等fileinput预设的class。代码实例可以参考官方文档【http://plugins.krajee.com/file-input/plugin-options#initialPreview】。事实上,initialPreview这一块内容是很丰富的,也有很多很多相关的参数可以使用。这个主要是用于当需要直接从后端读取一些文件数据展示给前端,并且允许前端进行新增或删除操作时使用。这种场景其实已经超越了普通的文件上传表单的意义了。就不详细叙述。等到要用的时候再研究吧
● layoutTemplates
这个参数内容也比较多,所以单独拿出来说一下。这个参数的值应该是一个object,而object中的各个键值对指出了一个整个fileinput组件的各种样式。上面的基本应用示例中的actionDelete其实就是指缩略图上的删除按钮。如像我一样设置成了''(空值),则文件缩略图上就不会显示删除按钮了。从观念上来说,我们可以把各个键值对的值看成是一个模板性质的东西,改变相应的模板自然就可以渲染出相应的内容了。
既然有模板,那么肯定就有模板变量和有一定语法的模板语言。fileinput里面有一种叫做tag的机制,即在模板中会出现类似{variable}的变量,可以被具体内容替换。而模板之间是可以嵌套的。比如layoutTemplates.footer中有{actions}这个变量:
footer: '',
,其内容是actions模板,而这个模板中又含有{upload},{download}等变量:
actions: '\n' + '', \n' + ' {drag}\n' + '{indicator}\n' + ' \n' + '
有了模板机制,可以让我们自由地选择再哪个层面的组件上进行模板的修改,然后模板系统会自动将这种改变传递给所有长辈模板,非常方便。因为我们不设置layoutTemplates参数也能渲染出一个界面来,说明这些模板中的所有变量(递归到最底层)都是有默认值的,这些默认值可以在这里看到:【http://plugins.krajee.com/file-input/plugin-options#layoutTemplates】。同时在修改模板时也要注意,除非有十足的把握,否则应该尽量不动这个模板中原本就有的那些变量。如果在改后的模板中少写了某个变量,就意味着这个变量指代的内容将不会被体现出来。
上面示例中给出的actionDelete相对还是比较低层级的模板,下面我们从高到低梳理一下各个层级的模板,部分模板没有变量,表明其是一个相对独立的模板:
main1 带有caption的渲染模板
main2 不带caption的渲染模板
preview 文件区的渲染模板
fileIcon caption中在文件名前面那个图标,默认glypyhicon-file
caption caption的渲染模板
modal modal的渲染模板(这个模态对话框在预览文件内容时出现)
progress 进度条的渲染模板,包括总进度条和每一个缩略图中进度条
size 含有{sizeText}变量,文件缩略图中文件大小部分信息的渲染模板
footer 含有{actions}变量等。
actions 含有{upload}、{download}、{delete}等变量,这三个变量分别代表了actionUpload,actionDownload和actionDelete三个模板的内容。
以actionDelete为例:
actionDelete里面还有removeClass,removeIcon,removeTitle等变量。这三个变量比较特殊,是在fileActionSettings里面进行设置的。
内容还有很多很多。。。我不想写了。总之就是看文档看文档
还有一个previewTemplates,是文件预览界面跳出来的模态框的样式设置, 不多说了。。
● 好的,继续来看其他的初始化参数
previewSettings 这个参数可以为不同类型的文件设置预览时不同的CSS,同这个系列的也还有很多
allowedFileTypes 一个数组,指出哪些类型的文件才是被接受上传的,尽量不要同时设置allowedFileTypes和allowedFileExtensions两个参数
allowedFileExtensions 一个数组,指出带有哪些后缀名的文件才是被接受上传的,设置msgInvalidFileExtension可以个性化出现此错误时的错误信息
previewFileIcon 当文件无法被预览时出现在缩略图中的图标,默认是
previewFileIconSettings 可以将不同的后缀的文件有不同的缩略图图标
uploadExtraData 刚才说过,如果使用ajax上传的话,可以设置这个属性来上传一些其他的数据。这个是一个object。
minFileSize/maxFileSize 指出上传支持的最小/最大的文件大小,同时可以设置msgFileTooSmall和msgFileTooLarge个性化报错信息
minFileCount/maxFileCount 指出上传最少/多几个文件,若设置成0则表示没有限制。默认是0
msgPlaceholder 默认是Select {files} ,代表了caption中的placeholder。如果是中文环境最好改一下,因为Select这个是没办法自己消除的。
msgFileRequired 当要求文件但是没上传时的报错信息
关于msg的还有很多参数比如太多太少,太大太小,没找到文件,文件不可读等等,不一一列举。
dropZoneEnabled 是否有拖曳文件上传区,和showPreview的区别在于,上传之后用户还是能看到文件缩略图,只是单纯不能拖曳上传了。
● fileActionSettings
最上面的那些show都是在缩略图外面的,现在来说说缩略图里面的一些东西要怎么控制显示隐藏:
showUpload,showDownload,showRemove,showZoom等就不说了
showDrag 显示拖动按钮(只适用于initialPreview的部分)
uploadIcon/uploadTitle/uploadClass 上传按钮相关的属性
download/remove/drag/zoom也有上面这三个
篇幅有些过长了。。关于fileinput的方法、事件以及两种上传方法的比较另开一片写