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

View File

@ -89,9 +89,32 @@ export default function(schema) {
data, data,
config 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) { if (data && data instanceof FormData) {
// config.headers = config.headers || {}; // config.headers = config.headers || {};
// config.headers['Content-Type'] = 'multipart/form-data'; // 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 } else if (data
&& typeof data !== 'string' && typeof data !== 'string'
&& !(data instanceof Blob) && !(data instanceof Blob)

View File

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