[improvement] rename packages dir to src (#3659)

This commit is contained in:
neverland
2019-06-27 11:25:57 +08:00
committed by GitHub
parent 8489918dca
commit 75c79b7044
619 changed files with 21 additions and 21 deletions
+97
View File
@@ -0,0 +1,97 @@
<template>
<demo-section>
<demo-block :title="$t('basicUsage')">
<van-uploader :after-read="afterRead" />
</demo-block>
<demo-block :title="$t('preview')">
<van-uploader
v-model="fileList"
multiple
accept="*"
/>
</demo-block>
<demo-block :title="$t('maxCount')">
<van-uploader
v-model="fileList2"
multiple
:max-count="2"
/>
</demo-block>
<demo-block :title="$t('uploadStyle')">
<van-uploader>
<van-button
type="primary"
icon="photo"
>
{{ this.$t('upload') }}
</van-button>
</van-uploader>
</demo-block>
<demo-block :title="$t('beforeRead')">
<van-uploader
v-model="fileList3"
:before-read="beforeRead"
/>
</demo-block>
</demo-section>
</template>
<script>
export default {
i18n: {
'zh-CN': {
upload: '上传文件',
preview: '文件预览',
maxCount: '限制上传数量',
beforeRead: '上传前校验',
uploadStyle: '自定义上传样式',
invalidType: '请上传 jpg 格式图片'
},
'en-US': {
upload: 'Upload File',
preview: 'Preview File',
maxCount: 'Max Count',
beforeRead: 'Before Read',
uploadStyle: 'Upload Style',
invalidType: 'Please upload an image in jpg format'
}
},
data() {
return {
fileList: [],
fileList2: [],
fileList3: []
};
},
methods: {
beforeRead(file) {
if (file.type !== 'image/jpeg') {
this.$toast(this.$t('invalidType'));
return false;
}
return true;
},
afterRead(file) {
console.log(file);
}
}
};
</script>
<style lang="less">
.demo-uploader {
background-color: #fff;
.van-uploader {
margin-left: 15px;
}
}
</style>
+143
View File
@@ -0,0 +1,143 @@
# Uploader
### Install
``` javascript
import { Uploader } from 'vant';
Vue.use(Uploader);
```
## Usage
### Basic Usage
```html
<van-uploader :after-read="afterRead" />
```
```javascript
export default {
methods: {
afterRead(file) {
console.log(file)
}
}
};
```
### Preview File
```html
<van-uploader v-model="fileList" multiple />
```
```javascript
export default {
data() {
return {
fileList: []
}
}
};
```
### Max Count
```html
<van-uploader
v-model="fileList"
multiple
:max-count="2"
/>
```
```javascript
export default {
data() {
return {
fileList: []
}
}
};
```
### Upload Style
```html
<van-uploader>
<van-button icon="photo" type="primary">Upload Image</van-button>
</van-uploader>
```
### Before Read
```html
<van-uploader :before-read="beforeRead" />
```
```js
export default {
methods: {
beforeRead(file) {
if (file.type !== 'image/jpeg') {
Toast('Please upload an image in jpg format');
return false;
}
return true;
},
asyncBeforeRead(file) {
return new Promise((resolve, reject) => {
if (file.type !== 'image/jpeg') {
Toast('Please upload an image in jpg format');
reject();
} else {
resolve();
}
});
}
}
}
```
## API
### Props
| Attribute | Description | Type | Default |
|------|------|------|------|
| name | Input name | `String` | - |
| accept | Accepted file type | `String` | `image/*` |
| preview-image | Whether to show image preview | `Boolean` | `true` |
| preview-size | Size of preview image | `String | Number` | `80px` |
| multiple | Whether to enable multiple selection pictures | `Boolean` | `false` |
| disabled | Whether to disabled the upload | `Boolean` | `false` |
| capture | Capturecan be set to `camera` | `String` | - |
| before-read | Hook before reading the file, return false to stop reading the file, can return Promise | `Function` | - |
| after-read | Hook after reading the file | `Function` | - |
| max-size | Max size of file | `Number` | - |
| max-count | Max count of image | `Number` | - |
| result-type | Type of file read result, can be set to `dataUrl` `text` | `String` | `dataUrl` |
| upload-text | Upload text | `String` | - |
### Events
| Event | Description | Arguments |
|------|------|------|
| oversize | Triggered when file size over limit | Same as after-read |
| delete | Triggered when delete preview file | file |
### Slots
| Name | Description |
|------|------|
| default | Custom icon |
### Parematers of before-read、after-read
| Attribute | Description | Type |
|------|------|------|
| file | File object | `Object` |
| detail | Detail info | `Object` |
+249
View File
@@ -0,0 +1,249 @@
import { createNamespace, suffixPx } from '../utils';
import { toArray, readFile, isOversize } from './utils';
import Icon from '../icon';
import Image from '../image';
import ImagePreview from '../image-preview';
const [createComponent, bem] = createNamespace('uploader');
function isImageDataUrl(dataUrl) {
return dataUrl.indexOf('data:image') === 0;
}
export default createComponent({
inheritAttrs: false,
model: {
prop: 'fileList'
},
props: {
fileList: Array,
disabled: Boolean,
uploadText: String,
afterRead: Function,
beforeRead: Function,
previewSize: [Number, String],
previewImage: {
type: Boolean,
default: true
},
accept: {
type: String,
default: 'image/*'
},
resultType: {
type: String,
default: 'dataUrl'
},
maxSize: {
type: Number,
default: Number.MAX_VALUE
},
maxCount: {
type: Number,
default: Number.MAX_VALUE
}
},
computed: {
detail() {
return {
name: this.$attrs.name || ''
};
}
},
methods: {
onChange(event) {
let { files } = event.target;
if (this.disabled || !files.length) {
return;
}
files = files.length === 1 ? files[0] : [].slice.call(files);
if (this.beforeRead) {
const response = this.beforeRead(files, this.detail);
if (!response) {
this.resetInput();
return;
}
if (response.then) {
response
.then(() => {
this.readFile(files);
})
.catch(this.resetInput);
return;
}
}
this.readFile(files);
},
readFile(files) {
const oversize = isOversize(files, this.maxSize);
if (Array.isArray(files)) {
const maxCount = this.maxCount - this.fileList.length;
if (files.length > maxCount) {
files = files.slice(0, maxCount);
}
Promise.all(files.map(file => readFile(file, this.resultType))).then(contents => {
const fileList = files.map((file, index) => ({
file,
content: contents[index]
}));
this.onAfterRead(fileList, oversize);
});
} else {
readFile(files, this.resultType).then(content => {
this.onAfterRead({ file: files, content }, oversize);
});
}
},
onAfterRead(files, oversize) {
if (oversize) {
this.$emit('oversize', files, this.detail);
return;
}
this.resetInput();
this.$emit('input', [...this.fileList, ...toArray(files)]);
if (this.afterRead) {
this.afterRead(files, this.detail);
}
},
onDelete(file, index) {
const fileList = this.fileList.slice(0);
fileList.splice(index, 1);
this.$emit('input', fileList);
this.$emit('delete', file);
},
resetInput() {
/* istanbul ignore else */
if (this.$refs.input) {
this.$refs.input.value = '';
}
},
onPreviewImage(item) {
const imageFiles = this.fileList
.map(item => item.content)
.filter(content => isImageDataUrl(content));
ImagePreview({
images: imageFiles,
startPosition: imageFiles.indexOf(item.content)
});
},
renderPreview() {
if (!this.previewImage) {
return;
}
return this.fileList.map((item, index) => (
<div class={bem('preview')}>
<Image
fit="cover"
src={item.content}
class={bem('preview-image')}
width={this.previewSize}
height={this.previewSize}
scopedSlots={{
error() {
return [
<Icon class={bem('file-icon')} name="description" />,
<div class={[bem('file-name'), 'van-ellipsis']}>{item.file.name}</div>
];
}
}}
onClick={() => {
if (isImageDataUrl(item.content)) {
this.onPreviewImage(item);
}
}}
/>
<Icon
name="delete"
class={bem('preview-delete')}
onClick={() => {
this.onDelete(item, index);
}}
/>
</div>
));
},
renderUpload() {
if (this.fileList.length >= this.maxCount) {
return;
}
const slot = this.slots();
const Input = (
<input
{...{ attrs: this.$attrs }}
ref="input"
type="file"
accept={this.accept}
class={bem('input')}
disabled={this.disabled}
onChange={this.onChange}
/>
);
if (slot) {
return (
<div class={bem('input-wrapper')}>
{slot}
{Input}
</div>
);
}
let style;
if (this.previewSize) {
const size = suffixPx(this.previewSize);
style = {
width: size,
height: size
};
}
return (
<div class={bem('upload')} style={style}>
<Icon name="plus" class={bem('upload-icon')} />
{this.uploadText && <span class={bem('upload-text')}>{this.uploadText}</span>}
{Input}
</div>
);
}
},
render(h) {
return (
<div class={bem()}>
<div class={bem('wrapper')}>
{this.renderPreview()}
{this.renderUpload()}
</div>
</div>
);
}
});
+86
View File
@@ -0,0 +1,86 @@
@import '../style/var';
.van-uploader {
position: relative;
display: inline-block;
&__wrapper {
display: flex;
flex-wrap: wrap;
}
&__input {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden; // to clip file-upload-button
cursor: pointer;
opacity: 0;
&-wrapper {
position: relative;
}
}
&__upload {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-sizing: border-box;
width: @uploader-size;
height: @uploader-size;
margin: 0 10px 10px 0;
background-color: @uploader-upload-background-color;
border: 1px dashed @uploader-upload-border-color;
&-icon {
color: @uploader-icon-color;
font-size: @uploader-icon-size;
}
&-text {
margin-top: 10px;
color: @uploader-text-color;
font-size: @uploader-text-font-size;
}
}
&__preview {
position: relative;
margin: 0 10px 10px 0;
&-image {
display: block;
width: @uploader-size;
height: @uploader-size;
}
&-delete {
position: absolute;
right: 0;
bottom: 0;
padding: 1px;
color: @uploader-delete-color;
background-color: @uploader-delete-background-color;
}
}
&__file-icon {
color: @gray-darker;
font-size: 20px;
}
&__file-name {
box-sizing: border-box;
width: 100%;
margin-top: 5px;
padding: 0 5px;
color: @gray-darker;
font-size: 12px;
text-align: center;
}
}
@@ -0,0 +1,48 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders demo correctly 1`] = `
<div>
<div>
<div class="van-uploader">
<div class="van-uploader__wrapper">
<div class="van-uploader__upload"><i class="van-icon van-icon-plus van-uploader__upload-icon">
<!----></i><input type="file" accept="image/*" class="van-uploader__input"></div>
</div>
</div>
</div>
<div>
<div class="van-uploader">
<div class="van-uploader__wrapper">
<div class="van-uploader__upload"><i class="van-icon van-icon-plus van-uploader__upload-icon">
<!----></i><input multiple="multiple" type="file" accept="*" class="van-uploader__input"></div>
</div>
</div>
</div>
<div>
<div class="van-uploader">
<div class="van-uploader__wrapper">
<div class="van-uploader__upload"><i class="van-icon van-icon-plus van-uploader__upload-icon">
<!----></i><input multiple="multiple" type="file" accept="image/*" class="van-uploader__input"></div>
</div>
</div>
</div>
<div>
<div class="van-uploader">
<div class="van-uploader__wrapper">
<div class="van-uploader__input-wrapper"><button class="van-button van-button--primary van-button--normal"><i class="van-icon van-icon-photo van-button__icon">
<!----></i><span class="van-button__text">
上传文件
</span></button><input type="file" accept="image/*" class="van-uploader__input"></div>
</div>
</div>
</div>
<div>
<div class="van-uploader">
<div class="van-uploader__wrapper">
<div class="van-uploader__upload"><i class="van-icon van-icon-plus van-uploader__upload-icon">
<!----></i><input type="file" accept="image/*" class="van-uploader__input"></div>
</div>
</div>
</div>
</div>
`;
@@ -0,0 +1,92 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`delete preview image 1`] = `
<div class="van-uploader">
<div class="van-uploader__wrapper">
<div class="van-uploader__upload" style="width: 30px; height: 30px;"><i class="van-icon van-icon-plus van-uploader__upload-icon">
<!----></i><input type="file" accept="image/*" class="van-uploader__input"></div>
</div>
</div>
`;
exports[`disable preview image 1`] = `
<div class="van-uploader">
<div class="van-uploader__wrapper">
<div class="van-uploader__upload"><i class="van-icon van-icon-plus van-uploader__upload-icon">
<!----></i><input type="file" accept="image/*" class="van-uploader__input"></div>
</div>
</div>
`;
exports[`max-count prop 1`] = `
<div class="van-uploader">
<div class="van-uploader__wrapper">
<div class="van-uploader__preview">
<div class="van-image van-uploader__preview-image"><img src="data:image/test" class="van-image__img" style="object-fit: cover;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o" style="font-size: 22px;">
<!----></i></div>
</div><i class="van-icon van-icon-delete van-uploader__preview-delete">
<!----></i>
</div>
</div>
</div>
`;
exports[`preview not image file 1`] = `
<div class="van-uploader">
<div class="van-uploader__wrapper">
<div class="van-uploader__preview">
<div class="van-image van-uploader__preview-image">
<div class="van-image__error"><i class="van-icon van-icon-description van-uploader__file-icon" style="">
<!----></i>
<div class="van-uploader__file-name van-ellipsis">test.md</div>
</div>
</div><i class="van-icon van-icon-delete van-uploader__preview-delete">
<!----></i>
</div>
<div class="van-uploader__upload"><i class="van-icon van-icon-plus van-uploader__upload-icon">
<!----></i><input type="file" accept="image/*" class="van-uploader__input"></div>
</div>
</div>
`;
exports[`preview-size prop 1`] = `
<div class="van-uploader">
<div class="van-uploader__wrapper">
<div class="van-uploader__preview" style="">
<div class="van-image van-uploader__preview-image" style="width: 30px; height: 30px;"><img src="data:image/test" class="van-image__img" style="object-fit: cover;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o" style="font-size: 22px;">
<!----></i></div>
</div><i class="van-icon van-icon-delete van-uploader__preview-delete">
<!----></i>
</div>
<div class="van-uploader__upload" style="width: 30px; height: 30px;"><i class="van-icon van-icon-plus van-uploader__upload-icon">
<!----></i><input type="file" accept="image/*" class="van-uploader__input"></div>
</div>
</div>
`;
exports[`render preview image 1`] = `
<div class="van-uploader">
<div class="van-uploader__wrapper">
<div class="van-uploader__preview">
<div class="van-image van-uploader__preview-image"><img src="data:image/test" class="van-image__img" style="object-fit: cover;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o" style="font-size: 22px;">
<!----></i></div>
</div><i class="van-icon van-icon-delete van-uploader__preview-delete">
<!----></i>
</div>
<div class="van-uploader__upload"><i class="van-icon van-icon-plus van-uploader__upload-icon">
<!----></i><input type="file" accept="image/*" class="van-uploader__input"></div>
</div>
</div>
`;
exports[`render upload-text 1`] = `
<div class="van-uploader">
<div class="van-uploader__wrapper">
<div class="van-uploader__upload"><i class="van-icon van-icon-plus van-uploader__upload-icon">
<!----></i><span class="van-uploader__upload-text">Text</span><input type="file" accept="image/*" class="van-uploader__input"></div>
</div>
</div>
`;
+4
View File
@@ -0,0 +1,4 @@
import Demo from '../demo';
import demoTest from '../../../test/demo-test';
demoTest(Demo);
+302
View File
@@ -0,0 +1,302 @@
import Uploader from '..';
import { mount, later } from '../../../test/utils';
window.File = function () {
this.size = 10000;
};
const mockFileDataUrl = 'data:image/test';
const mockFile = new File([], '/Users');
const file = { target: { files: [mockFile] } };
const multiFile = { target: { files: [mockFile, mockFile] } };
window.FileReader = function () {
this.readAsText = function () {
this.onload &&
this.onload({
target: {
result: mockFileDataUrl
}
});
};
this.readAsDataURL = this.readAsText;
};
test('disabled', () => {
const afterRead = jest.fn();
const wrapper = mount(Uploader, {
propsData: {
disabled: true,
afterRead
}
});
wrapper.vm.onChange(file);
expect(afterRead).toHaveBeenCalledTimes(0);
});
it('read text', done => {
const wrapper = mount(Uploader, {
propsData: {
resultType: 'text',
afterRead: readFile => {
expect(readFile.content).toEqual(mockFileDataUrl);
done();
}
}
});
wrapper.vm.onChange(file);
});
it('set input name', done => {
const wrapper = mount(Uploader, {
propsData: {
name: 'uploader',
beforeRead: (readFile, detail) => {
expect(detail.name).toEqual('uploader');
return true;
},
afterRead: (readFile, detail) => {
expect(detail.name).toEqual('uploader');
done();
}
}
});
wrapper.vm.onChange(file);
});
it('unknown resultType', () => {
const afterRead = jest.fn();
const wrapper = mount(Uploader, {
propsData: {
resultType: 'xxxx',
afterRead
}
});
wrapper.vm.onChange(file);
expect(afterRead).toHaveBeenCalledTimes(0);
});
it('before read return false', () => {
const afterRead = jest.fn();
const wrapper = mount(Uploader, {
propsData: {
beforeRead: () => false,
afterRead
}
});
const input = wrapper.find('input');
wrapper.vm.onChange(file);
expect(afterRead).toHaveBeenCalledTimes(0);
expect(input.element.value).toEqual('');
});
it('before read return promise and resolve', async () => {
const afterRead = jest.fn();
const wrapper = mount(Uploader, {
propsData: {
beforeRead: () => new Promise(resolve => {
resolve();
}),
afterRead
}
});
wrapper.vm.onChange(file);
await later();
expect(afterRead).toHaveBeenCalledTimes(1);
});
it('before read return promise and reject', async () => {
const afterRead = jest.fn();
const wrapper = mount(Uploader, {
propsData: {
beforeRead: () => new Promise((resolve, reject) => {
reject();
}),
afterRead
}
});
const input = wrapper.find('input');
await later();
wrapper.vm.onChange(file);
expect(afterRead).toHaveBeenCalledTimes(0);
expect(input.element.value).toEqual('');
});
test('file size overlimit', async () => {
const wrapper = mount(Uploader, {
propsData: {
maxSize: 1
}
});
wrapper.vm.onChange(file);
wrapper.vm.onChange(multiFile);
await later();
expect(wrapper.emitted('oversize')[0]).toBeTruthy();
expect(wrapper.emitted('oversize')[1]).toBeTruthy();
wrapper.vm.maxSize = 100000;
wrapper.vm.onChange(multiFile);
await later();
expect(wrapper.emitted('oversize')[2]).toBeFalsy();
});
it('render upload-text', () => {
const wrapper = mount(Uploader, {
propsData: {
uploadText: 'Text'
}
});
expect(wrapper).toMatchSnapshot();
});
it('render preview image', async () => {
const wrapper = mount(Uploader, {
propsData: {
fileList: []
},
listeners: {
input(fileList) {
wrapper.setProps({ fileList });
}
}
});
wrapper.vm.onChange(file);
await later();
expect(wrapper).toMatchSnapshot();
});
it('disable preview image', async () => {
const wrapper = mount(Uploader, {
propsData: {
fileList: [],
previewImage: false
},
listeners: {
input(fileList) {
wrapper.setProps({ fileList });
}
}
});
wrapper.vm.onChange(file);
await later();
expect(wrapper).toMatchSnapshot();
});
it('max-count prop', async () => {
const wrapper = mount(Uploader, {
propsData: {
fileList: [],
maxCount: 1
},
listeners: {
input(fileList) {
wrapper.setProps({ fileList });
}
}
});
wrapper.vm.onChange(multiFile);
await later();
expect(wrapper).toMatchSnapshot();
});
it('preview-size prop', async () => {
const wrapper = mount(Uploader, {
propsData: {
fileList: [],
previewSize: 30
},
listeners: {
input(fileList) {
wrapper.setProps({ fileList });
}
}
});
wrapper.vm.onChange(file);
await later();
expect(wrapper).toMatchSnapshot();
});
it('delete preview image', async () => {
const wrapper = mount(Uploader, {
propsData: {
fileList: [],
previewSize: 30
},
listeners: {
input(fileList) {
wrapper.setProps({ fileList });
}
}
});
wrapper.vm.onChange(file);
await later();
wrapper.find('.van-uploader__preview-delete').trigger('click');
expect(wrapper.vm.fileList.length).toEqual(0);
expect(wrapper).toMatchSnapshot();
expect(wrapper.emitted('delete')[0]).toBeTruthy();
});
it('click to preview image', async () => {
const wrapper = mount(Uploader, {
propsData: {
fileList: [],
previewSize: 30
},
listeners: {
input(fileList) {
wrapper.setProps({ fileList });
}
}
});
wrapper.vm.onChange(file);
await later();
wrapper.find('.van-image').trigger('click');
const imagePreviewNode = document.querySelector('.van-image-preview');
expect(imagePreviewNode).toBeTruthy();
imagePreviewNode.remove();
});
it('preview not image file', async () => {
const wrapper = mount(Uploader, {
propsData: {
fileList: [{
content: 'data:application',
file: {
name: 'test.md'
}
}]
}
});
wrapper.find('img').trigger('error');
wrapper.find('.van-image').trigger('click');
expect(document.querySelector('.van-image-preview')).toBeFalsy();
expect(wrapper).toMatchSnapshot();
});
+27
View File
@@ -0,0 +1,27 @@
export function toArray(item) {
if (Array.isArray(item)) {
return item;
}
return [item];
}
export function readFile(file, resultType) {
return new Promise(resolve => {
const reader = new FileReader();
reader.onload = event => {
resolve(event.target.result);
};
if (resultType === 'dataUrl') {
reader.readAsDataURL(file);
} else if (resultType === 'text') {
reader.readAsText(file);
}
});
}
export function isOversize(files, maxSize) {
return toArray(files).some(file => file.size > maxSize);
}
+156
View File
@@ -0,0 +1,156 @@
# Uploader 文件上传
### 引入
``` javascript
import { Uploader } from 'vant';
Vue.use(Uploader);
```
## 代码演示
### 基础用法
文件上传完毕后会触发`after-read`回调函数,获取到对应的`file`对象
```html
<van-uploader :after-read="afterRead" />
```
```javascript
export default {
methods: {
afterRead(file) {
// 此时可以自行将文件上传至服务器
console.log(file);
}
}
};
```
### 图片预览
通过`v-model`可以绑定已经上传的图片列表,并展示图片列表的预览图
```html
<van-uploader v-model="fileList" multiple />
```
```javascript
export default {
data() {
return {
fileList: []
}
}
};
```
### 限制上传数量
通过`max-count`属性可以限制上传文件的数量,上传数量达到限制后,会自动隐藏上传区域
```html
<van-uploader
v-model="fileList"
multiple
:max-count="2"
/>
```
```javascript
export default {
data() {
return {
fileList: []
}
}
};
```
### 自定义上传样式
通过插槽可以自定义上传区域的样式
```html
<van-uploader>
<van-button icon="photo" type="primary">上传图片</van-button>
</van-uploader>
```
### 上传前校验
通过传入`beforeRead`函数可以在上传前进行校验,返回`true`表示校验通过,返回`false`表示校验失败。支持返回`Promise`进行异步校验
```html
<van-uploader :before-read="beforeRead" />
```
```js
export default {
methods: {
// 返回布尔值
beforeRead(file) {
if (file.type !== 'image/jpeg') {
Toast('请上传 jpg 格式图片');
return false;
}
return true;
},
// 返回 Promise
asyncBeforeRead(file) {
return new Promise((resolve, reject) => {
if (file.type !== 'image/jpeg') {
Toast('请上传 jpg 格式图片');
reject();
} else {
resolve();
}
});
}
}
}
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|------|------|------|------|------|
| name | 标识符,可以在回调函数的第二项参数中获取 | `String` | - | 1.6.13 |
| accept | 接受的文件类型 | `String` | `image/*` | - |
| preview-image | 是否在上传完成后展示预览图 | `Boolean` | `true` | 2.0.0 |
| preview-size | 预览图和上传区域的尺寸,默认单位为`px` | `String | Number` | `80px` | 2.0.0 |
| multiple | 是否开启图片多选,部分安卓机型不支持 | `Boolean` | `false` | 2.0.0 |
| disabled | 是否禁用文件上传 | `Boolean` | `false` | - |
| capture | 图片选取模式,可选值为`camera`(直接调起摄像头) | `String` | - | 2.0.0 |
| before-read | 文件读取前的回调函数,返回`false`可终止文件读取,支持返回`Promise` | `Function` | - | - |
| after-read | 文件读取完成后的回调函数 | `Function` | - | - |
| max-size | 文件大小限制,单位为`byte` | `Number` | - | - |
| max-count | 文件上传数量限制 | `Number` | - | 2.0.0 |
| result-type | 文件读取结果类型,可选值为`text` | `String` | `dataUrl` | - |
| upload-text | 上传区域文字提示 | `String` | - | 2.0.0 |
### Events
| 事件名 | 说明 | 回调参数 |
|------|------|------|
| oversize | 文件大小超过限制时触发 | 同`after-read` |
| delete | 删除文件预览时触发 | file: 被删除的文件对象 |
### Slots
| 名称 | 说明 |
|------|------|
| default | 自定义上传区域 |
### before-read、after-read 回调参数
| 参数名 | 说明 | 类型 |
|------|------|------|
| file | 文件解析后的 file 对象 | `Object` |
| detail | 额外信息,包含 name 字段 | `Object` |