mirror of
https://gitee.com/baidu/amis.git
synced 2024-12-02 20:09:08 +08:00
File 支持 功能
This commit is contained in:
parent
7cd10dc0f9
commit
a69b8211f4
@ -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 表单提交的接口里面一起带上。
|
@ -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)
|
||||||
|
@ -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" />
|
||||||
|
Loading…
Reference in New Issue
Block a user