@alifd/next
Version:
A configurable component library for web built on React.
690 lines (579 loc) • 21.7 kB
JavaScript
'use strict';
exports.__esModule = true;
var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);
var _objectWithoutProperties2 = require('babel-runtime/helpers/objectWithoutProperties');
var _objectWithoutProperties3 = _interopRequireDefault(_objectWithoutProperties2);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _class, _temp, _initialiseProps;
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _classnames = require('classnames');
var _classnames2 = _interopRequireDefault(_classnames);
var _reactLifecyclesCompat = require('react-lifecycles-compat');
var _util = require('../util');
var _icon = require('../icon');
var _icon2 = _interopRequireDefault(_icon);
var _base = require('./base');
var _base2 = _interopRequireDefault(_base);
var _index = require('./runtime/index');
var _index2 = _interopRequireDefault(_index);
var _html5Uploader = require('./runtime/html5-uploader');
var _html5Uploader2 = _interopRequireDefault(_html5Uploader);
var _list = require('./list');
var _list2 = _interopRequireDefault(_list);
var _util2 = require('./util');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var noop = _util.func.noop;
/**
* Upload
*/
var Upload = (_temp = _class = function (_Base) {
(0, _inherits3.default)(Upload, _Base);
function Upload(props) {
(0, _classCallCheck3.default)(this, Upload);
var _this = (0, _possibleConstructorReturn3.default)(this, _Base.call(this, props));
_initialiseProps.call(_this);
var value = void 0;
if ('value' in props) {
value = props.value;
} else {
value = props.defaultValue;
}
_this.state = {
value: !Array.isArray(value) ? [] : value,
uploading: false
};
return _this;
}
Upload.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) {
// 上传中不允许做受控修改
if ('value' in nextProps && nextProps.value !== prevState.value && !prevState.uploading) {
return {
value: !Array.isArray(nextProps.value) ? [] : nextProps.value
};
}
return null;
};
/**
* 对外暴露API, 添加文件
* @param files
*/
Upload.prototype.selectFiles = function selectFiles(files) {
var filesArr = files.length ? Array.prototype.slice.call(files) : [files];
this.onSelect(filesArr);
};
Upload.prototype.uploadFiles = function uploadFiles(files) {
// NOTE: drag上传,当鼠标松开的时候回执行 onDrop,但此时onChange还没出发所以 value=[], 必须提前标识上传中
this.state.uploading = true;
var fileList = files.filter(function (file) {
if (file.state === 'selected') {
file.state = 'uploading';
return true;
}
return false;
}).map(function (file) {
return file.originFileObj;
});
fileList.length && this.uploaderRef.startUpload(fileList);
};
/**
* 对外暴露api,控制文件上传
*/
Upload.prototype.startUpload = function startUpload() {
this.uploadFiles(this.state.value);
};
Upload.prototype.replaceFiles = function replaceFiles(old, current) {
var targetItem = (0, _util2.getFileItem)(old, this.state.value);
if (!targetItem) {
return;
}
current.uid = old.uid;
targetItem.originFileObj = current;
};
// 替换掉队列里面的文件
Upload.prototype.isUploading = function isUploading() {
return this.state.uploading;
};
/**
* 删除文件
* @param {File} file
* @return {void}
*/
/**
* 取消上传
* @param {File} file
* @return {void}
*/
Upload.prototype.render = function render() {
var _classNames, _classNames2;
var _props = this.props,
listType = _props.listType,
prefix = _props.prefix,
dragable = _props.dragable,
shape = _props.shape,
className = _props.className,
style = _props.style,
useDataURL = _props.useDataURL,
disabled = _props.disabled,
limit = _props.limit,
closable = _props.closable,
beforeUpload = _props.beforeUpload,
readonly = _props.readonly,
onRemove = _props.onRemove,
onCancel = _props.onCancel,
onPreview = _props.onPreview,
list = _props.list,
extraRender = _props.extraRender,
progressProps = _props.progressProps,
rtl = _props.rtl,
isPreview = _props.isPreview,
renderPreview = _props.renderPreview,
name = _props.name,
_props$fileKeyName = _props.fileKeyName,
fileKeyName = _props$fileKeyName === undefined ? name : _props$fileKeyName,
fileNameRender = _props.fileNameRender,
actionRender = _props.actionRender,
previewOnFileName = _props.previewOnFileName,
others = (0, _objectWithoutProperties3.default)(_props, ['listType', 'prefix', 'dragable', 'shape', 'className', 'style', 'useDataURL', 'disabled', 'limit', 'closable', 'beforeUpload', 'readonly', 'onRemove', 'onCancel', 'onPreview', 'list', 'extraRender', 'progressProps', 'rtl', 'isPreview', 'renderPreview', 'name', 'fileKeyName', 'fileNameRender', 'actionRender', 'previewOnFileName']);
var cls = (0, _classnames2.default)((_classNames = {}, _classNames[prefix + 'upload'] = true, _classNames[prefix + 'upload-dragable'] = dragable, _classNames[prefix + 'disabled'] = disabled, _classNames[prefix + 'readonly'] = readonly, _classNames[className] = className, _classNames));
var isExceedLimit = this.state.value.length >= limit;
var innerCls = (0, _classnames2.default)((_classNames2 = {}, _classNames2[prefix + 'upload-inner'] = true, _classNames2[prefix + 'hidden'] = isExceedLimit, _classNames2));
var children = this.props.children;
if (shape === 'card') {
var _classNames3;
var cardCls = (0, _classnames2.default)((_classNames3 = {}, _classNames3[prefix + 'upload-card'] = true, _classNames3[prefix + 'disabled'] = disabled, _classNames3));
children = _react2.default.createElement(
'div',
{ className: cardCls },
_react2.default.createElement(_icon2.default, { size: 'large', type: 'add', className: prefix + 'upload-add-icon' }),
_react2.default.createElement(
'div',
{ tabIndex: '0', role: 'button', className: prefix + 'upload-text' },
children
)
);
}
if (isPreview) {
if (typeof renderPreview === 'function') {
var _classNames4;
var previewCls = (0, _classnames2.default)((_classNames4 = {}, _classNames4[prefix + 'form-preview'] = true, _classNames4[className] = !!className, _classNames4));
return _react2.default.createElement(
'div',
{ style: style, className: previewCls },
renderPreview(this.state.value, this.props)
);
}
if (listType) {
return _react2.default.createElement(_list2.default, { isPreview: true, listType: listType, style: style, className: className, value: this.state.value });
}
return null;
}
// disabled 状态下把 remove函数替换成禁止 remove的函数
var onRemoveFunc = disabled ? _util.func.prevent : onRemove;
var otherAttributes = _util.obj.pickAttrsWith(this.props, 'data-');
return _react2.default.createElement(
'div',
(0, _extends3.default)({ className: cls, style: style }, otherAttributes),
_react2.default.createElement(
_index2.default,
(0, _extends3.default)({}, others, {
name: fileKeyName,
beforeUpload: beforeUpload,
dragable: dragable,
disabled: disabled || isExceedLimit,
className: innerCls,
onSelect: this.onSelect,
onDrop: this.onDrop,
onProgress: this.onProgress,
onSuccess: this.onSuccess,
onError: this.onError,
ref: this.saveUploaderRef
}),
children
),
listType || list ? _react2.default.createElement(_list2.default, {
useDataURL: useDataURL,
fileNameRender: fileNameRender,
actionRender: actionRender,
uploader: this,
listType: listType,
value: this.state.value,
closable: closable,
onRemove: onRemoveFunc,
progressProps: progressProps,
onCancel: onCancel,
onPreview: onPreview,
extraRender: extraRender,
rtl: rtl,
previewOnFileName: previewOnFileName
}) : null
);
};
return Upload;
}(_base2.default), _class.displayName = 'Upload', _class.propTypes = (0, _extends3.default)({}, _html5Uploader2.default.propTypes, _list2.default.propTypes, {
/**
* 样式前缀
*/
prefix: _propTypes2.default.string.isRequired,
/**
* 上传的地址
*/
action: _propTypes2.default.string,
/**
* 文件列表
*/
value: _propTypes2.default.array,
/**
* 默认文件列表
*/
defaultValue: _propTypes2.default.array,
/**
* 上传按钮形状
*/
shape: _propTypes2.default.oneOf(['card']),
/**
* 上传列表的样式
* @enumdesc 文字, 图文, 卡片
*/
listType: _propTypes2.default.oneOf(['text', 'image', 'card']),
list: _propTypes2.default.any,
/**
* 文件名字段
*/
name: _propTypes2.default.string,
/**
* 上传额外传参
*/
data: _propTypes2.default.oneOfType([_propTypes2.default.object, _propTypes2.default.func]),
/**
* 数据格式化函数,配合自定义 action 使用,参数为服务器的响应数据,详见 [formatter](#formater)
* @param {Object} response 返回
* @param {File} file 文件对象
*/
formatter: _propTypes2.default.func,
/**
* 最大文件上传个数
*/
limit: _propTypes2.default.number,
/**
* 设置上传超时,单位ms
*/
timeout: _propTypes2.default.number,
/**
* 可选参数,是否支持拖拽上传,`ie10+` 支持。
*/
dragable: _propTypes2.default.bool,
closable: _propTypes2.default.bool,
/**
* 可选参数,是否本地预览
*/
useDataURL: _propTypes2.default.bool,
/**
* 可选参数,是否禁用上传功能
*/
disabled: _propTypes2.default.bool,
/**
* 选择文件回调
*/
onSelect: _propTypes2.default.func,
/**
* 上传中
*/
onProgress: _propTypes2.default.func,
/**
* 上传文件改变时的状态
* @param {Object} info 文件事件对象
*/
onChange: _propTypes2.default.func,
/**
* 可选参数,上传成功回调函数,参数为请求下响应信息以及文件
* @param {Object} file 文件
* @param {Array<Object>} value 值
*/
onSuccess: _propTypes2.default.func,
/**
* 可选参数, 用于校验文件,afterSelect仅在 autoUpload=false 的时候生效,autoUpload=true时,可以使用beforeUpload完全可以替代该功能.
* @param {Object} file
* @returns {Boolean} 返回false会阻止上传,其他则表示正常
*/
afterSelect: _propTypes2.default.func,
/**
* 移除文件回调函数
* @param {Object} file 文件
* @returns {Boolean|Promise} 返回 false、Promise.resolve(false)、 Promise.reject() 将阻止文件删除
*/
onRemove: _propTypes2.default.func,
/**
* 可选参数,上传失败回调函数,参数为上传失败的信息、响应信息以及文件
* @param {Object} file 出错的文件
* @param {Array} value 当前值
*/
onError: _propTypes2.default.func,
/**
* 可选参数, 详见 [beforeUpload](#beforeUpload)
* @param {Object} file 所有文件
* @param {Object} options 参数
* @returns {Boolean|Object|Promise} 返回值作用见demo
*/
beforeUpload: _propTypes2.default.func,
/**
* 放文件
*/
onDrop: _propTypes2.default.func,
/**
* 自定义class
*/
className: _propTypes2.default.string,
/**
* 自定义内联样式
*/
style: _propTypes2.default.object,
/**
* 子元素
*/
children: _propTypes2.default.node,
/**
* 自动上传
*/
autoUpload: _propTypes2.default.bool,
/**
* 自定义上传方法
* @param {Object} option
* @return {Object} object with abort method
*/
request: _propTypes2.default.func,
/**
* 透传给Progress props
*/
progressProps: _propTypes2.default.object,
rtl: _propTypes2.default.bool,
/**
* 是否为预览态
*/
isPreview: _propTypes2.default.bool,
/**
* 预览态模式下渲染的内容
* @param {number} value 评分值
*/
renderPreview: _propTypes2.default.func,
/**
* 文件对象的 key name
* @version 1.21
*/
fileKeyName: _propTypes2.default.string,
/**
* list 的自定义文件名渲染
* @param {Object} file 文件
* @return {Node} react node
*/
fileNameRender: _propTypes2.default.func,
/**
* 操作区域额外渲染
* @param {Object} file 文件
* @return {Node} react node
*/
actionRender: _propTypes2.default.func,
/**
* 点击文件名时触发 onPreview
* @version 1.24
*/
previewOnFileName: _propTypes2.default.bool
}), _class.defaultProps = (0, _extends3.default)({}, _html5Uploader2.default.defaultProps, {
prefix: 'next-',
limit: Infinity,
autoUpload: true,
closable: true,
onSelect: noop,
onProgress: noop,
onChange: noop,
onSuccess: noop,
onRemove: noop,
onError: noop,
onDrop: noop,
beforeUpload: noop,
afterSelect: noop,
previewOnFileName: false
}), _initialiseProps = function _initialiseProps() {
var _this2 = this;
this.onSelect = function (files) {
var _props2 = _this2.props,
autoUpload = _props2.autoUpload,
afterSelect = _props2.afterSelect,
onSelect = _props2.onSelect,
limit = _props2.limit;
// 总数
var total = _this2.state.value.length + files.length;
// 差额
var less = limit - _this2.state.value.length;
if (less <= 0) {
// 差额不足 则不上传
return;
}
var fileList = files.map(function (file) {
var objFile = (0, _util2.fileToObject)(file);
objFile.state = 'selected';
return objFile;
});
// 默认全量上传
var uploadFiles = fileList;
var discardFiles = [];
if (total > limit) {
// 全量上传总数会超过limit 但是 还有差额
uploadFiles = fileList.slice(0, less);
discardFiles = fileList.slice(less);
}
var value = _this2.state.value.concat(fileList);
/* eslint-disable-next */
_this2.state.value = value;
if (autoUpload) {
_this2.uploadFiles(uploadFiles);
}
onSelect(uploadFiles, value);
discardFiles.forEach(function (file) {
// 丢弃的文件
var err = new Error(_util2.errorCode.EXCEED_LIMIT);
err.code = _util2.errorCode.EXCEED_LIMIT;
_this2.onError(err, null, file);
});
if (!autoUpload) {
uploadFiles.forEach(function (file) {
var isPassed = afterSelect(file);
_util.func.promiseCall(isPassed, _util.func.noop, function (error) {
_this2.onError(error, null, file); // TODO: handle error message
});
});
_this2.onChange(value, uploadFiles);
}
};
this.onDrop = function (files) {
_this2.onSelect(files);
_this2.props.onDrop(files);
};
this.replaceWithNewFile = function (old, current) {
var newFile = (0, _util2.fileToObject)(current);
newFile.state = 'selected';
var matchKey = old.uid !== undefined ? 'uid' : 'name';
var fileList = _this2.state.value;
for (var i = 0; i < fileList.length; i++) {
var item = fileList[i];
if (item[matchKey] === old[matchKey]) {
fileList.splice(i, 1, newFile);
break;
}
}
_this2.uploadFiles([newFile]);
return newFile;
};
this.onProgress = function (e, file) {
_this2.state.uploading = true;
var value = _this2.state.value;
var targetItem = (0, _util2.getFileItem)(file, value);
if (!targetItem) {
return;
}
(0, _extends3.default)(targetItem, {
state: 'uploading',
percent: e.percent
});
_this2.setState({
value: value
});
_this2.props.onProgress(value, targetItem);
};
this.onSuccess = function (response, file) {
var formatter = _this2.props.formatter;
if (formatter) {
response = formatter(response, file);
}
try {
if (typeof response === 'string') {
response = JSON.parse(response);
}
} catch (e) {
e.code = _util2.errorCode.RESPONSE_FAIL;
return _this2.onError(e, response, file);
}
if (response.success === false) {
var err = new Error(response.message || _util2.errorCode.RESPONSE_FAIL);
err.code = _util2.errorCode.RESPONSE_FAIL;
return _this2.onError(err, response, file);
}
var value = _this2.state.value;
var targetItem = (0, _util2.getFileItem)(file, value);
if (!targetItem) {
return;
}
(0, _extends3.default)(targetItem, {
state: 'done',
response: response,
url: response.url,
downloadURL: response.downloadURL || response.url // 下载地址(可选)
});
if (!_this2.props.useDataURL) {
targetItem.imgURL = response.imgURL || response.url; // 缩略图地址(可选)
}
_this2.updateUploadingState();
_this2.onChange(value, targetItem);
_this2.props.onSuccess(targetItem, value);
};
this.onError = function (err, response, file) {
var value = _this2.state.value;
var targetItem = (0, _util2.getFileItem)(file, value);
if (!targetItem) {
return;
}
(0, _extends3.default)(targetItem, {
state: 'error',
error: err,
response: response
});
_this2.updateUploadingState();
_this2.onChange(value, targetItem);
_this2.props.onError(targetItem, value);
};
this.removeFile = function (file) {
file.state = 'removed';
_this2.uploaderRef.abort(file); // 删除组件时调用组件的 `abort` 方法中断上传
var fileList = _this2.state.value;
var targetItem = (0, _util2.getFileItem)(file, fileList);
var index = fileList.indexOf(targetItem);
if (index !== -1) {
fileList.splice(index, 1);
_this2.onChange(fileList, targetItem);
}
};
this.updateUploadingState = function () {
var inProgress = _this2.state.value.some(function (i) {
return i.state === 'uploading';
});
if (!inProgress) {
_this2.state.uploading = false;
}
};
this.abort = function (file) {
var fileList = _this2.state.value;
var targetItem = (0, _util2.getFileItem)(file, fileList);
var index = fileList.indexOf(targetItem);
if (index !== -1) {
fileList.splice(index, 1);
_this2.onChange(fileList, targetItem);
}
_this2.uploaderRef.abort(file); // 取消上传时调用组件的 `abort` 方法中断上传
};
this.onChange = function (value, file) {
_this2.setState({
value: value
});
_this2.props.onChange(value, file);
};
}, _temp);
exports.default = (0, _reactLifecyclesCompat.polyfill)(Upload);
module.exports = exports['default'];