File 支持 功能

This commit is contained in:
liaoxuezhi 2019-07-25 14:36:29 +08:00
parent 7cd10dc0f9
commit a69b8211f4
3 changed files with 67 additions and 22 deletions

View File

@ -1,6 +1,6 @@
### File
文件输入amis 也默认处理了图片存储,提交给 API 的是文件的下载地址
用来负责文件上传,文件上传成功后会返回文件地址,这个文件地址会作为这个表单项的值,整个表单提交的时候,其实提交的是文件地址,文件上传已经在这个控件中完成了
- `type` 请设置成 `file`
- `reciever` 默认 `/api/upload/file` 如果想自己存储,请设置此选项。(PS: 如果想存自己的 bos, 系统配置中可以直接填写自己的 bos 配置。)
@ -29,3 +29,5 @@
"maxSize": 1048576
}
```
如果不希望 File 控件接管上传,可以配置 `asBlob` 或者 `asBase64` 这两个属性二选一采用这种方式后File 控件不再自己上传了,而是直接把文件数据作为表单项的值,文件内容会在 Form 表单提交的接口里面一起带上。

View File

@ -89,9 +89,32 @@ export default function(schema) {
data,
config
}) => {
let hasFile = function(data) {
return Object.keys(data).some(key => {
let value = data[key];
return value instanceof File || Array.isArray(value) && value.length && value[0] instanceof File;
});
}
if (data && data instanceof FormData) {
// config.headers = config.headers || {};
// config.headers['Content-Type'] = 'multipart/form-data';
} else if (hasFile(data)) {
const fd = new FormData();
Object.keys(data).forEach(key => {
const value = data[key];
if (value instanceof File) {
fd.append(key, value, value.name);
} else if (Array.isArray(value) && value.length && value[0] instanceof File) {
value.forEach(value => fd.append(`${key}[]`, value, value.name));
} else {
// todo
fd.append(key, value);
}
});
data = fd;
} else if (data
&& typeof data !== 'string'
&& !(data instanceof Blob)

View File

@ -11,6 +11,7 @@ import {mapLimit} from 'async';
import ImageControl from './Image';
import {Payload} from '../../types';
import { filter } from '../../utils/tpl';
import Alert from '../../components/Alert2';
export interface FileProps extends FormControlProps {
btnClassName: string;
@ -43,19 +44,20 @@ export interface FileProps extends FormControlProps {
[propName:string]: string;
};
asBase64?: boolean;
asBlob?: boolean;
resetValue?: string;
};
export interface FileX extends File {
state?: 'init' | 'error' | 'pending' | 'uploading' | 'uploaded' | 'invalid';
state?: 'init' | 'error' | 'pending' | 'uploading' | 'uploaded' | 'invalid' | 'ready';
}
export interface FileValue {
filename?: string;
value?:string;
value?: any;
name?: string;
url?: string;
state: 'init' | 'error' | 'pending' | 'uploading' | 'uploaded' | 'invalid';
state: 'init' | 'error' | 'pending' | 'uploading' | 'uploaded' | 'invalid' | 'ready';
[propName:string]: any;
};
@ -93,7 +95,8 @@ export default class FileControl extends React.Component<FileProps, FileState> {
'pending': "等待上传",
'uploading': "上传中",
'error': "上传出错",
'uploaded': "已上传"
'uploaded': "已上传",
"ready": ""
},
asBase64: false
};
@ -106,7 +109,12 @@ export default class FileControl extends React.Component<FileProps, FileState> {
let file:FileValue | FileX | undefined = files && typeof value === 'string'
? find(files, item => (item as FileValue).value === value)
: undefined;
return value ? {
return value ? value instanceof File ? {
state: 'ready',
value: value,
name: value.name,
url: ''
} : {
...(typeof value === 'string' ? {
state: file && file.state ? file.state : 'init',
value,
@ -314,7 +322,8 @@ export default class FileControl extends React.Component<FileProps, FileState> {
startChunkApi,
chunkApi,
finishChunkApi,
asBase64
asBase64,
asBlob
} = this.props;
if (asBase64) {
@ -322,14 +331,22 @@ export default class FileControl extends React.Component<FileProps, FileState> {
reader.readAsDataURL(file);
reader.onload = () => {
cb(null, file, {
value: reader.result,
value: reader.result as string,
name: file.name,
url: '',
state: 'uploaded'
state: 'ready'
});
}
reader.onerror = (error:any) => cb(error.message);
return;
} else if (asBlob) {
setTimeout(() => cb(null, file, {
name: file.name,
value: file,
url: '',
state: 'ready'
}), 4);
return;
}
let fn = useChunk === 'auto' && chunkSize && file.size > chunkSize || useChunk === true ? this.uploadBigFile : this.uploadFile;
@ -384,17 +401,18 @@ export default class FileControl extends React.Component<FileProps, FileState> {
extractValue,
valueField,
delimiter,
resetValue
resetValue,
asBlob
} = this.props;
const files = this.state.files.filter(file => file.state == 'uploaded' || file.state == 'init');
const files = this.state.files.filter(file => ~['uploaded', 'init', 'ready'].indexOf(file.state as string));
let value:any = multiple ? files : files[0];
if (value) {
if (joinValues) {
value = Array.isArray(value) ? value.map((item: any) => item[valueField || 'value']).join(delimiter || ',') : value[valueField || 'value'];
} else if (extractValue) {
if (extractValue || asBlob) {
value = Array.isArray(value) ? value.map((item: any) => item[valueField || 'value']) : value[valueField || 'value'];
} else if (joinValues) {
value = Array.isArray(value) ? value.map((item: any) => item[valueField || 'value']).join(delimiter || ',') : value[valueField || 'value'];
}
} else {
value = typeof resetValue === 'undefined' ? '' : resetValue;
@ -592,7 +610,9 @@ export default class FileControl extends React.Component<FileProps, FileState> {
autoUpload,
stateTextMap,
hideUploadButton,
className
className,
asBlob,
joinValues
} = this.props;
let {
files,
@ -605,10 +625,9 @@ export default class FileControl extends React.Component<FileProps, FileState> {
return (
<div className={cx('amis-file-control', className)}>
{error ? (
<div>
<p className="help-block text-danger inline">{error}</p>
<a className="btn btn-link" onClick={this.clearError}><i className="fa fa-times" /></a>
</div>
<Alert level="danger" showCloseButton onClose={this.clearError}>
{error}
</Alert>
) : null}
{files && files.length ? (
@ -618,6 +637,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
<a
className="text-danger pull-right"
onClick={() => this.removeFile(file, key)}
href="javascript:void 0"
><i className="fa fa-times" /></a>
<span className="pull-right text-muted text-xs m-r-sm">{stateTextMap && stateTextMap[file.state as string] || ''}</span>
<i className="fa fa-file fa-fw m-r-xs" />