Filepond是我个人最喜欢的上传组件之一,在功能全面的基础上提供了额及其强大的可定制性以及最美观的样式。虽然官网有vue下的使用指南,但是并不是很全面。
在开发我的开源图床框架PARA时,我选择了Filepond作为我的前端上传插件。它具备官方的前端框架支持和几乎所有你能想到的定制属性。更棒的是,它在nuxt下依然非常好用。
基本使用
安装:
npm install vue-filepond filepond --save
如果你和我一样更喜欢yarn,那么命令为:
yarn add vue-filepond filepond
然后应该就可以在vue中使用<file-pond>
组件了,当然这通常不够。
挂载插件:
Filepond提供了很多插件,我通常会选择可以预览上传图片的ImagePreview
和文件大小以及类型插件FileValidateType
和FileValidateSize
。插件要使用组件局部声明的方式,挂载在filepond组件上。同时如果插件需要额外的css文件也需要提前引入,对于我的插件配置来说,代码应该像这样:
//index.vue
import vueFilePond from "vue-filepond";
import "filepond/dist/filepond.min.css";
import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css';
import FilePondPluginImagePreview from 'filepond-plugin-image-preview';
import FilePondPluginFileValidateType from "filepond-plugin-file-validate-type";
import FilepondPluginFileValidateSize from "filepond-plugin-file-validate-size";
const FilePond = vueFilePond(
FilePondPluginFileValidateType,
FilepondPluginFileValidateSize,
FilePondPluginImagePreview
);
配置参数
官网的示例如下:
<template>
<div id="app">
<file-pond
name="test"
ref="pond"
class-name="my-pond"
label-idle="Drop files here..."
allow-multiple="true"
accepted-file-types="image/jpeg, image/png"
v-bind:files="myFiles"
v-on:init="handleFilePondInit"/>
</div>
</template>
<script>
// Import FilePond
import vueFilePond from 'vue-filepond';
// Import plugins
import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type/dist/filepond-plugin-file-validate-type.esm.js';
import FilePondPluginImagePreview from 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.esm.js';
// Import styles
import 'filepond/dist/filepond.min.css';
import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css';
// Create FilePond component
const FilePond = vueFilePond( FilePondPluginFileValidateType, FilePondPluginImagePreview );
export default {
name: 'app',
data: function() {
return { myFiles: ['index.html'] };
},
methods: {
handleFilePondInit: function() {
console.log('FilePond has initialized');
// example of instance method call on pond reference
this.$refs.pond.getFiles();
}
},
components: {
FilePond
}
};
</script>
如果只看官网的文档,可定制的项似乎很少,但是根据在devtool中查看的结果,几乎所有的定制项组件都有,所以直接在组件上设置就可以了。
坑:
其实我写这篇文章主要是为了记录这个样式上的坑:
背景:
在框架中我设置的用户权限组配置项有“是否可以多文件上传这一项”,所以组件多文件上传的开关是动态绑定的。于此同时网站的首页为了美观设置了{height:100vh;overflow:hidden}来保证始终占据整屏且没有滚动栏。这就导致了如果用户一次上传了过多的文件,上传列表将会超出屏幕。
官方解决方案:
为组件的根元素设置最大高度,当设置了最大高度时,如果超过了最大高度将会自动显示滚动条:
.filepond--root {
max-height: 10em;
}
但是这也带来了一个问题,因为官方在生成组件的时候,单文件上传和多文件上传的DOM结构是不一样的(可能官方以为只允许单文件上传的时候开发者是想不到设置高度这码事的)
问题:
单文件的组件逻辑为:将图片拖入时,有提示信息和“支撑元素高度”的label元素就会隐藏
我来抽象一下它的DOM树:
<div class="filepond--wrapper">
<div class="filepond--root filepond--hopper" style="height: 76px;">
<input class="filepond--browser" type="file" accept="image/jpeg,image/png">
<div class="filepond--drop-label" style="transform: translate3d(0px, 0px, 0px); opacity: 1;">
<label aria-hidden="true">将文件拖入框中,或点击<span class="filepond--label-action" tabindex="0">选择</span></label></div>
<div class="filepond--list-scroller" style="transform: translate3d(0px, 0px, 0px);">
<ul class="filepond--list" role="list"></ul>
</div>
<div class="filepond--panel filepond--panel-root">
<div class="filepond--panel-top filepond--panel-root"></div>
<div class="filepond--panel-center filepond--panel-root"
style="transform: translate3d(0px, 8px, 0px) scale3d(1, 0.6, 1);"></div>
<div class="filepond--panel-bottom filepond--panel-root" style="transform: translate3d(0px, 68px, 0px);">
</div>
</div><span class="filepond--assistant" ></span>
<div class="filepond--drip"></div>
</div>
</div>
我删去了一些和这个问题无关紧要的属性,可以看出,我们设置最大高度的“filepond–root”元素它的高度是由一个内联的属性决定的。而背景的灰色圆角边框其实是绝对定位的“filepond-panel”元素所提供的。由top,center,bottom分别展示顶部圆角边,中部,底部圆角边。与此同时,中部元素的css为:
.filepond--panel-center {
height: 100px !important;
border-top: none !important;
border-bottom: none !important;
border-radius: 0 !important;
}
但是如果我们对单文件上传的组件设置最大高度,初始化组件的时候就会根据多文件组件的方式生成:
可以很直观的看出,中间的元素高度缺失了。但其实元素和元素的高度还在,只是元素被隐藏了,造成隐藏的css样式为:
.filepond--panel-center:not([style]) {
visibility: hidden;
}
这一原因为当多文件上传时,标签的dom是包含在panel内的,这个时候panel的高度是真正的由标签占据,此时标签是不消失的:
单文件版本标签消失:
所以如果是否允许多文件上传的属性是动态绑定的,就不能把最大高度写在CSS中!!!
解决方案:
如果不能把样式固定在<style>
中,而class绑定也不会生效(我试过,会被覆盖),那么只能在组件的初始化上下功夫了,借助原生js直接修改样式表:
//index.vue
<template>
...some code...
<Filepond @init="handlerInit" />
...some code...
</template>
<script>
...some code
export default{
mounted:{
if (allowMultiple) {
document.styleSheets[0].insertRule('.filepond--root {max-height: 60vh;}', 0);
}
}
}
</script>
即可达成想要的效果。
至于为什么会设置高度会导致DOM预设发生变化,仍在审计中…