Слияние кода завершено, страница обновится автоматически
import React, { ReactElement, ReactNode } from 'react';
import { action as mobxAction, computed, IReactionDisposer, observable, reaction, runInAction, toJS } from 'mobx';
import { observer } from 'mobx-react';
import classNames from 'classnames';
import omit from 'lodash/omit';
import noop from 'lodash/noop';
import isNil from 'lodash/isNil';
import isString from 'lodash/isString';
import isFunction from 'lodash/isFunction';
import { getConfig, Uploader } from 'choerodon-ui/dataset';
import { AttachmentValue, AttachmentFileProps } from 'choerodon-ui/dataset/configure';
import { UploaderProps } from 'choerodon-ui/dataset/uploader/Uploader';
import { DownloadAllMode } from 'choerodon-ui/dataset/data-set/enum';
import { AttachmentConfig } from 'choerodon-ui/lib/configure';
import { Size } from 'choerodon-ui/lib/_util/enum';
import Trigger from 'choerodon-ui/lib/trigger/Trigger';
import RcUpload from 'choerodon-ui/lib/rc-components/upload';
import { Action } from 'choerodon-ui/lib/trigger/enum';
import { pxToRem } from 'choerodon-ui/lib/_util/UnitConvertor';
import Button, { ButtonProps } from '../button/Button';
import { $l } from '../locale-context';
import { ButtonColor, FuncType } from '../button/enum';
import AttachmentList from './AttachmentList';
import AttachmentGroup from './AttachmentGroup';
import { FormField, FormFieldProps } from '../field/FormField';
import autobind from '../_util/autobind';
import Modal from '../modal';
import AttachmentFile, { FileLike } from '../data-set/AttachmentFile';
import { sortAttachments } from './utils';
import ObserverSelect from '../select/Select';
import BUILT_IN_PLACEMENTS from '../trigger-field/placements';
import attachmentStore from '../stores/AttachmentStore';
import { FieldType } from '../data-set/enum';
import { ValidationMessages } from '../validator/Validator';
import ValidationResult from '../validator/ValidationResult';
import { open } from '../modal-container/ModalContainer';
import Icon from '../icon';
import Dragger from './Dragger';
import { ShowHelp } from '../field/enum';
import { FIELD_SUFFIX } from '../form/utils';
import { showValidationMessage } from '../field/utils';
import { ShowValidation } from '../form/enum';
import { getIf } from '../data-set/utils';
import { ATTACHMENT_TARGET } from './Item';
import TemplateDownloadButton from './TemplateDownloadButton';
import { hide, show } from '../tooltip/singleton';
export type AttachmentListType = 'text' | 'picture' | 'picture-card';
export enum AttachmentButtonType {
download = 'download',
remove = 'remove',
history = 'history',
}
export type AttachmentButtons =
| AttachmentButtonType
| [AttachmentButtonType, ButtonProps]
| ReactElement<ButtonProps>;
export interface AttachmentProps extends FormFieldProps, ButtonProps, UploaderProps {
listType?: AttachmentListType;
viewMode?: 'none' | 'list' | 'popup';
sortable?: boolean;
pictureWidth?: number;
count?: number;
max?: number;
listLimit?: number;
showHistory?: boolean;
showSize?: boolean;
showValidation?: ShowValidation;
attachments?: (AttachmentFile | FileLike)[];
onAttachmentsChange?: (attachments: AttachmentFile[]) => void;
onRemove?: (attachment: AttachmentFile) => boolean | void;
getUUID?: () => Promise<string> | string;
downloadAll?: ButtonProps | boolean;
previewTarget?: string;
dragUpload?: boolean;
dragBoxRender?: ReactNode[];
template?: AttachmentValue;
buttons?: AttachmentButtons[];
__inGroup?: boolean;
getPreviewUrl?: (props: AttachmentFileProps) => (string | (() => string | Promise<string>) | undefined);
}
export type Sort = {
type: 'time' | 'name';
order: 'asc' | 'desc';
custom?: boolean;
};
const defaultSort: Sort = {
type: 'time',
order: 'asc',
custom: true,
};
@observer
export default class Attachment extends FormField<AttachmentProps> {
static displayName = 'Attachment';
static Dragger: typeof Dragger;
static defaultProps = {
...FormField.defaultProps,
suffixCls: 'attachment',
multiple: true,
sortable: true,
showSize: true,
downloadAll: true,
listType: 'text',
viewMode: 'list',
dragUpload: false,
};
// eslint-disable-next-line camelcase
static __IS_IN_CELL_EDITOR = true;
// eslint-disable-next-line camelcase
static __PRO_ATTACHMENT = true;
static Group = AttachmentGroup;
@observable sort?: Sort;
@observable popup?: boolean;
@observable dragState?: string;
uploader?: Uploader;
@observable tempAttachmentUUID?: string | undefined;
get help() {
return this.getDisplayProp('help');
}
@computed
get showAttachmentHelp() {
const defaultShowHelp = this.getContextConfig('showHelp');
const { viewMode } = this.props;
const { showHelp } = this;
const { formNode } = this.context;
if (showHelp === ShowHelp.none ||
(formNode && showHelp === ShowHelp.label) ||
(viewMode === 'popup' && (showHelp || defaultShowHelp) === ShowHelp.label && formNode)) {
return ShowHelp.none;
}
if (viewMode === 'popup') {
return ShowHelp.tooltip;
}
return ShowHelp.newLine;
}
get bucketName() {
return this.getProp('bucketName');
}
get bucketDirectory() {
return this.getProp('bucketDirectory');
}
get storageCode() {
return this.getProp('storageCode');
}
get fileKey() {
return this.getProp('fileKey') || this.getContextConfig('attachment').defaultFileKey;
}
get isPublic() {
return this.getProp('isPublic');
}
get attachments(): AttachmentFile[] | undefined {
const { field } = this;
if (field) {
return field.getAttachments(this.record, this.tempAttachmentUUID);
}
if (this.getValue()) {
return this.observableProps.attachments;
}
}
set attachments(attachments: AttachmentFile[] | undefined) {
runInAction(() => {
const { field } = this;
if (field) {
field.setAttachments(attachments, this.record, this.tempAttachmentUUID);
} else {
this.observableProps.attachments = attachments;
}
if (attachments) {
const { onAttachmentsChange } = this.props;
if (onAttachmentsChange) {
onAttachmentsChange(attachments);
}
}
});
}
get count(): number | undefined {
const { attachments, field } = this;
if (attachments) {
return attachments.length;
}
if (field) {
const attachmentCount = field.getAttachmentCount(this.record);
if (attachmentCount !== undefined) {
return attachmentCount;
}
}
const { count } = this.observableProps;
return count;
}
get defaultValidationMessages(): ValidationMessages {
const label = this.getProp('label');
return {
valueMissing: $l('Attachment', label ? 'value_missing' : 'value_missing_no_label', { label }),
};
}
get accept(): string[] | undefined {
return this.getProp('accept');
}
private reaction?: IReactionDisposer;
componentDidMount() {
super.componentDidMount();
this.fetchCount();
this.reaction = reaction(() => this.record, () => this.fetchCount());
}
componentDidUpdate(prevProps: AttachmentProps) {
const { value, viewMode } = this.props;
if (prevProps.value !== value || prevProps.viewMode !== viewMode) {
this.fetchCount();
}
}
componentWillUnmount() {
super.componentWillUnmount();
const { reaction } = this;
if (reaction) {
reaction();
delete this.reaction;
}
}
getFieldType(): FieldType {
return FieldType.attachment;
}
getObservableProps(props, context): any {
const { count, attachments } = props;
const { observableProps = { count, attachments } } = this;
return {
...super.getObservableProps(props, context),
count: count === undefined ? observableProps.count : count,
attachments: attachments ? attachments.map(attachment => {
if (attachment instanceof AttachmentFile) {
return attachment;
}
return new AttachmentFile(attachment);
}) : observableProps.attachments,
};
}
getValidAttachments(): AttachmentFile[] | undefined {
const { attachments } = this;
if (attachments) {
return attachments.filter(({ status }) => !status || ['success', 'done'].includes(status));
}
}
getValidatorProp(key: string) {
if (key === 'attachmentCount') {
const attachments = this.getValidAttachments();
const count = attachments ? attachments.length : this.count;
return count || 0;
}
return super.getValidatorProp(key);
}
fetchCount() {
const { field } = this;
const { viewMode } = this.props;
if (viewMode !== 'list' && isNil(this.count)) {
const value = this.getValue();
if (value) {
const { isPublic } = this;
if (field) {
field.fetchAttachmentCount(value, isPublic, this.record);
} else {
const { batchFetchCount } = this.getContextConfig('attachment');
if (batchFetchCount && !this.attachments) {
const { bucketName, bucketDirectory, storageCode } = this;
attachmentStore.fetchCountInBatch({
attachmentUUID: value,
bucketName,
bucketDirectory,
storageCode,
isPublic,
}).then(mobxAction((count) => {
this.observableProps.count = count || 0;
}));
}
}
}
}
}
@autobind
updateCacheCount() {
const attachmentUUID = this.getValue();
if (attachmentUUID && this.count) {
attachmentStore.updateCacheCount(attachmentUUID, this.count);
}
}
@autobind
handleDataSetLoad() {
this.fetchCount();
}
getOmitPropsKeys(): string[] {
return super.getOmitPropsKeys().concat([
'value',
'accept',
'action',
'data',
'headers',
'buttons',
'withCredentials',
'sortable',
'listType',
'viewMode',
'fileKey',
'fileSize',
'useChunk',
'chunkSize',
'chunkThreads',
'bucketName',
'bucketDirectory',
'storageCode',
'count',
'max',
'listLimit',
'dragBoxRender',
'dragUpload',
'showHistory',
'showSize',
'isPublic',
'downloadAll',
'attachments',
'onAttachmentsChange',
'beforeUpload',
'onUploadProgress',
'onUploadSuccess',
'onUploadError',
'onRemove',
'getPreviewUrl',
]);
}
isAcceptFile(attachment: AttachmentFile, accept: string[]): boolean {
const acceptTypes = accept.map(type => (
new RegExp(type.replace(/\./g, '\\.').replace(/\*/g, '.*'))
));
const { name, type } = attachment;
return acceptTypes.some(acceptType => acceptType.test(name) || acceptType.test(type));
}
async getAttachmentUUID(): Promise<string> {
this.autoCreate();
const oldAttachmentUUID = this.tempAttachmentUUID || this.getValue();
const attachmentUUID = oldAttachmentUUID || (await this.fetchAttachmentUUID());
if (attachmentUUID !== oldAttachmentUUID) {
runInAction(() => {
this.tempAttachmentUUID = attachmentUUID;
});
}
return attachmentUUID;
}
fetchAttachmentUUID(): Promise<string> | string {
const { getUUID = this.getContextConfig('attachment').getAttachmentUUID } = this.props;
if (!getUUID) {
throw new Error('no getAttachmentUUID hook in global configure.');
}
return getUUID({ isPublic: this.isPublic });
}
@mobxAction
async uploadAttachments(attachments: AttachmentFile[]): Promise<void> {
const max = this.getProp('max');
if (max > 0 && (this.count || 0) + attachments.length > max) {
Modal.error($l('Attachment', 'file_list_max_length', { count: max }));
return;
}
const oldAttachments = this.attachments || [];
if (this.multiple) {
this.attachments = [...oldAttachments.slice(), ...attachments];
} else {
oldAttachments.forEach(attachment => this.doRemove(attachment));
this.attachments = [...attachments];
}
try {
await Promise.all(attachments.map((attachment) => this.upload(attachment)));
} finally {
this.changeOrder();
}
}
@autobind
async uploadAttachment(attachment: AttachmentFile) {
await this.upload(attachment);
if (attachment.status === 'success') {
this.changeOrder();
}
}
getUploaderProps(): UploaderProps {
const { bucketName, bucketDirectory, storageCode, isPublic, fileKey, accept } = this;
const fileSize = this.getProp('fileSize');
const chunkSize = this.getProp('chunkSize');
const chunkThreads = this.getProp('chunkThreads');
const useChunk = this.getProp('useChunk');
const {
action, data, headers, withCredentials,
beforeUpload, onUploadProgress, onUploadSuccess, onUploadError,
} = this.props;
return {
accept, action, data, headers, fileKey, withCredentials, bucketName, bucketDirectory, storageCode, isPublic,
fileSize, chunkSize, chunkThreads, useChunk, beforeUpload, onUploadProgress, onUploadSuccess, onUploadError,
};
}
async upload(attachment: AttachmentFile) {
try {
const uploader = getIf(this, 'uploader', () => {
return new Uploader(
{},
{
getConfig: this.getContextConfig as typeof getConfig,
},
);
});
uploader.setProps(this.getUploaderProps());
const result = await uploader.upload(attachment, this.attachments || [attachment], this.tempAttachmentUUID);
if (result === false) {
this.removeAttachment(attachment);
} else {
runInAction(() => {
const { tempAttachmentUUID } = this;
if (attachment.status === 'success') {
this.updateCacheCount();
if (tempAttachmentUUID) {
this.tempAttachmentUUID = undefined;
this.setValue(tempAttachmentUUID);
}
} else {
this.checkValidity();
}
});
}
} catch (e) {
this.removeAttachment(attachment);
throw e;
}
}
getOtherProps(): any {
const otherProps = super.getOtherProps();
otherProps.onClick = this.handleClick;
return otherProps;
}
processFiles(files: File[], attachmentUUID: string): AttachmentFile[] {
return files.map((file, index: number) => new AttachmentFile({
uid: this.getUid(index),
url: URL.createObjectURL(file),
name: file.name,
size: file.size,
type: file.type,
lastModified: file.lastModified,
originFileObj: file,
creationDate: new Date(),
attachmentUUID,
}));
}
@autobind
@mobxAction
handleChange(e) {
const { target } = e;
if (target.value) {
const files: File[] = [...target.files];
target.value = '';
this.getAttachmentUUID().then((uuid) => {
this.uploadAttachments(this.processFiles(files, uuid));
});
}
}
doRemove(attachment: AttachmentFile): Promise<any> | undefined {
const { onRemove: onAttachmentRemove = noop } = this.props;
return Promise.resolve(onAttachmentRemove(attachment)).then(mobxAction(ret => {
if (ret !== false) {
const { onRemove } = this.getContextConfig('attachment');
if (onRemove) {
if (attachment.status === 'error' || attachment.invalid) {
return this.removeAttachment(attachment);
}
const attachmentUUID = this.getValue();
if (attachmentUUID) {
const { bucketName, bucketDirectory, storageCode, isPublic, multiple } = this;
attachment.status = 'deleting';
return onRemove({ attachment, attachmentUUID, bucketName, bucketDirectory, storageCode, isPublic }, multiple)
.then(mobxAction((result) => {
if (result !== false) {
this.removeAttachment(attachment);
}
attachment.status = 'done';
}))
.catch(mobxAction(() => {
attachment.status = 'done';
}));
}
}
}
}));
}
@autobind
handleHistory(attachment: AttachmentFile, attachmentUUID: string) {
const { renderHistory } = this.getContextConfig('attachment') as AttachmentConfig;
if (renderHistory) {
const { bucketName, bucketDirectory, storageCode } = this;
open({
title: $l('Attachment', 'operation_records'),
children: renderHistory({
attachment,
attachmentUUID,
bucketName,
bucketDirectory,
storageCode,
}),
cancelButton: false,
okText: $l('Modal', 'close'),
drawer: true,
});
}
}
@autobind
handleRemove(attachment: AttachmentFile): Promise<any> | undefined {
return this.doRemove(attachment);
}
@autobind
@mobxAction
handleAttachmentsChange(attachments: AttachmentFile[] | undefined) {
this.observableProps.attachments = attachments;
}
@autobind
handleFetchAttachment(fetchProps: { bucketName?: string; bucketDirectory?: string; storageCode?: string; attachmentUUID: string; isPublic?: boolean; }) {
const { field } = this;
if (field) {
field.fetchAttachments(fetchProps, this.record);
} else {
const { fetchList } = this.getContextConfig('attachment');
if (fetchList) {
fetchList(fetchProps).then((results) => {
this.attachments = results.map(file => new AttachmentFile(file));
});
}
}
}
@autobind
handlePreview() {
this.setPopup(false);
}
@mobxAction
removeAttachment(attachment: AttachmentFile): undefined {
const { attachments } = this;
if (attachments) {
const index = attachments.indexOf(attachment);
if (index !== -1) {
attachments.splice(index, 1);
this.attachments = attachments;
this.checkValidity();
this.updateCacheCount();
}
}
return undefined;
}
handleDragUpload = (file: File, files: File[]) => {
if (files.indexOf(file) === files.length - 1) {
this.getAttachmentUUID().then((uuid) => {
this.uploadAttachments(this.processFiles(files, uuid));
});
}
return false;
};
@autobind
handleClick(e) {
const { element } = this;
if (element) {
element.click();
}
const { onClick } = this.props;
if (onClick) {
onClick(e);
}
}
getUid(index: number): string {
const { prefixCls } = this;
return `${prefixCls}-${Date.now()}-${index}`;
}
renderHeaderLabel() {
const { viewMode } = this.props;
if (this.hasFloatLabel || viewMode === 'popup') {
const label = this.getLabel();
if (label) {
const { prefixCls } = this;
return (
<span className={classNames(`${prefixCls}-header-label`, { [`${prefixCls}-required`]: this.getProp('required') })}>
{label}
</span>
);
}
}
}
isDisabled(): boolean {
if (super.isDisabled()) {
return true;
}
const max = this.getProp('max');
if (max) {
const { count = 0 } = this;
return count >= max;
}
return false;
}
isValid(): boolean {
const { attachments } = this;
if (attachments && attachments.some(({ status, invalid }) => invalid || status === 'error')) {
return false;
}
return super.isValid();
}
renderTemplateDownloadButton(): ReactElement<ButtonProps> | undefined {
if (!this.readOnly) {
const template = this.getProp('template');
if (template) {
const { bucketName, bucketDirectory, storageCode, isPublic, attachmentUUID } = template;
if (attachmentUUID) {
const { previewTarget = ATTACHMENT_TARGET, viewMode, color, funcType = viewMode === 'popup' ? FuncType.flat : FuncType.link } = this.props;
return (
<TemplateDownloadButton
attachmentUUID={attachmentUUID}
bucketName={bucketName}
bucketDirectory={bucketDirectory}
storageCode={storageCode}
isPublic={isPublic}
target={previewTarget}
funcType={funcType}
color={color}
/>
);
}
}
}
}
renderUploadBtn(isCardButton: boolean, label?: ReactNode): ReactElement<ButtonProps> {
const {
count = 0,
multiple,
prefixCls,
accept,
props: {
children, viewMode,
},
} = this;
const buttonProps = this.getOtherProps();
const { ref, style, name, onChange, ...rest } = buttonProps;
const max = this.getProp('max');
const uploadProps = {
multiple,
accept: accept ? accept.join(',') : undefined,
name: name || this.fileKey,
type: 'file',
ref,
onChange,
};
const width = isCardButton ? pxToRem(this.getPictureWidth()) : undefined;
const countText = multiple && (max ? `${count}/${max}` : count) || undefined;
return isCardButton ? (
<Button
funcType={FuncType.link}
key="upload-btn"
icon="add"
{...rest}
className={classNames(`${prefixCls}-card-button`, this.getClassName())}
style={{ ...style, width, height: width }}
>
<div>{children || $l('Attachment', 'upload_picture')}</div>
{countText ? <div>{countText}</div> : undefined}
<input key="upload" {...uploadProps} style={{ width: 0, height: 0, display: 'block' }} />
</Button>
) : (
<Button
funcType={viewMode === 'popup' ? FuncType.flat : FuncType.link}
key="upload-btn"
icon="file_upload"
color={this.valid ? ButtonColor.primary : ButtonColor.red}
{...rest}
className={viewMode === 'popup' ? this.getMergedClassNames() : this.getClassName()}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
>
{children || $l('Attachment', 'upload_attachment')}{label && <>({label})</>} {countText}
<input key="upload" {...uploadProps} style={{ width: 0, height: 0, display: 'block' }} />
</Button>
);
}
showTooltip(e): boolean {
if (this.showValidation === ShowValidation.tooltip) {
const message = this.getTooltipValidationMessage();
if (message) {
showValidationMessage(e, message, this.context.getTooltipTheme('validation'), this.context.getTooltipPlacement('validation'), this.getContextConfig);
return true;
}
}
return false;
}
renderViewButton(label?: ReactNode): ReactElement<ButtonProps> {
const { children, multiple, viewMode } = this.props;
const rest = this.getOtherProps();
return (
<Button
funcType={viewMode === 'popup' ? FuncType.flat : FuncType.link}
key="view-btn"
icon="attach_file"
color={ButtonColor.primary}
{...omit(rest, ['ref'])}
className={this.getMergedClassNames()}
>
{children || $l('Attachment', 'view_attachment')}{label && <>({label})</>} {multiple && this.count || undefined}
</Button>
);
}
@mobxAction
handleSort(sort: Sort) {
this.sort = sort;
}
@autobind
@mobxAction
handleOrderChange(props) {
const { attachments } = props;
this.attachments = attachments;
this.changeOrder();
}
@mobxAction
changeOrder() {
this.sort = {
...defaultSort,
...this.sort,
custom: true,
};
const { sortable } = this.props;
if (sortable) {
const { onOrderChange } = this.getContextConfig('attachment');
if (onOrderChange) {
const attachmentUUID = this.getValue();
if (attachmentUUID) {
const attachments = this.getValidAttachments();
if (attachments) {
const { bucketName, bucketDirectory, storageCode, isPublic } = this;
onOrderChange({
bucketName,
bucketDirectory,
storageCode,
attachments,
attachmentUUID,
isPublic,
});
}
}
}
}
}
@autobind
getSortSelectPopupContainer() {
return this.wrapper;
}
renderSorter(): ReactNode {
const { sortable, viewMode } = this.props;
if (sortable) {
const { prefixCls, attachments } = this;
if (attachments && attachments.length > 1) {
const { type, order } = this.sort || defaultSort;
return (
<>
<ObserverSelect
value={type}
onChange={(newType) => this.handleSort({ type: newType, order, custom: false })}
clearButton={false}
isFlat
popupPlacement="bottomRight"
getPopupContainer={viewMode === 'popup' ? this.getSortSelectPopupContainer : undefined}
size={Size.small}
border={false}
>
<ObserverSelect.Option value="time">{$l('Attachment', 'by_upload_time')}</ObserverSelect.Option>
<ObserverSelect.Option value="name">{$l('Attachment', 'by_name')}</ObserverSelect.Option>
</ObserverSelect>
<Button
funcType={FuncType.link}
className={classNames(`${prefixCls}-order-icon`, order)}
onClick={() => this.handleSort({ type, order: order === 'asc' ? 'desc' : 'asc', custom: false })}
/>
</>
);
}
}
}
renderUploadList(uploadButton?: ReactNode) {
const {
listType, sortable, listLimit, showHistory, showSize, previewTarget, buttons, getPreviewUrl, disabled,
} = this.props;
let mergeButtons:AttachmentButtons[] = [AttachmentButtonType.download, AttachmentButtonType.remove];
if (buttons) {
mergeButtons = [...mergeButtons, ...buttons];
}
if (showHistory) {
mergeButtons.unshift(AttachmentButtonType.history);
}
const { attachments } = this;
const attachmentUUID = this.tempAttachmentUUID || this.getValue();
if (attachmentUUID || uploadButton || (attachments && attachments.length)) {
const { bucketName, bucketDirectory, storageCode, readOnly, isPublic } = this;
const width = this.getPictureWidth();
return (
<AttachmentList
prefixCls={`${this.prefixCls}-list`}
pictureWidth={width}
listType={listType}
attachments={sortAttachments(attachments, this.sort || defaultSort)}
bucketName={bucketName}
bucketDirectory={bucketDirectory}
storageCode={storageCode}
attachmentUUID={attachmentUUID}
uploadButton={uploadButton}
sortable={sortable}
showSize={showSize}
readOnly={readOnly}
disabled={disabled}
isPublic={isPublic}
limit={readOnly ? listLimit : undefined}
previewTarget={previewTarget}
onHistory={showHistory ? this.handleHistory : undefined}
onRemove={this.handleRemove}
onUpload={this.uploadAttachment}
onOrderChange={this.handleOrderChange}
onFetchAttachments={this.handleFetchAttachment}
onAttachmentsChange={this.handleAttachmentsChange}
onPreview={this.handlePreview}
record={this.record}
buttons={mergeButtons}
getPreviewUrl={getPreviewUrl}
/>
);
}
}
renderHeader(uploadBtn?: ReactNode) {
const { prefixCls, count, props: { downloadAll, viewMode, __inGroup } } = this;
const label = (!__inGroup || count) && this.renderHeaderLabel();
const buttons: ReactNode[] = [];
if (uploadBtn) {
buttons.push(
uploadBtn,
this.renderTemplateDownloadButton(),
);
}
const { downloadAllMode = DownloadAllMode.readOnly } = this.getContextConfig('attachment');
if ((downloadAllMode === DownloadAllMode.readOnly && this.readOnly) || (downloadAllMode === DownloadAllMode.always)) {
if (this.count) {
if (downloadAll) {
const { getDownloadAllUrl } = this.getContextConfig('attachment');
if (getDownloadAllUrl) {
const attachmentUUID = this.getValue();
if (attachmentUUID) {
const { bucketName, bucketDirectory, storageCode, isPublic } = this;
const downloadAllUrl = getDownloadAllUrl({
bucketName,
bucketDirectory,
storageCode,
attachmentUUID,
isPublic,
});
if (downloadAllUrl) {
const downProps: ButtonProps = {
key: 'download',
icon: 'get_app',
funcType: FuncType.link,
href: isString(downloadAllUrl) ? downloadAllUrl : undefined,
onClick: isFunction(downloadAllUrl) ? downloadAllUrl : undefined,
target: '_blank',
color: ButtonColor.primary,
children: $l('Attachment', 'download_all'),
};
buttons.push(<Button {...downProps} {...downloadAll} />);
}
}
}
}
} else if (viewMode !== 'popup' && !__inGroup) {
const viewProps: ButtonProps = {
key: 'view',
funcType: FuncType.link,
disabled: true,
children: $l('Attachment', 'no_attachments'),
};
buttons.push(
<Button {...viewProps} />,
);
}
}
const hasButton = buttons.length;
const sorter = this.renderSorter();
const divider = !__inGroup && label && hasButton ? <span key="divider" className={`${prefixCls}-header-divider`} /> : undefined;
if (label || divider || hasButton || sorter) {
return (
<div className={`${prefixCls}-header`}>
{label}
{divider}
<div className={`${prefixCls}-header-buttons`}>
{buttons}
</div>
{sorter}
</div>
);
}
}
@autobind
renderWrapper(): ReactNode {
const { prefixCls } = this;
return (
<div className={`${prefixCls}-popup-inner`} ref={this.wrapperReference}>
{this.renderWrapperList()}
</div>
);
}
@autobind
getTooltipValidationMessage(): ReactNode {
const validationMessage = this.renderValidationResult();
if (!this.isValidationMessageHidden(validationMessage)) {
return validationMessage;
}
}
renderValidationResult(validationResult?: ValidationResult): ReactNode {
const message = super.renderValidationResult(validationResult);
if (message) {
return (
<div className={`${this.prefixCls}-validation-message`}>
{message}
</div>
);
}
}
renderEmpty() {
const { viewMode } = this.props;
if (viewMode === 'popup' && !this.count) {
return (
<div className={`${this.prefixCls}-empty`}>
{this.getContextConfig('renderEmpty')('Attachment')}
</div>
);
}
}
getWrapperClassNames() {
const {
prefixCls,
props: { className, size },
} = this;
return classNames(
`${prefixCls}-wrapper`,
className,
{
[`${prefixCls}-sm`]: size === Size.small,
[`${prefixCls}-lg`]: size === Size.large,
},
);
}
renderWrapperList(uploadBtn?: ReactNode) {
const { prefixCls, props: { viewMode, listType, __inGroup } } = this;
const isCard = listType === 'picture-card';
const classes = [`${prefixCls}-list-wrapper`];
if (viewMode !== 'popup') {
const wrapperClassName = this.getWrapperClassNames();
if (wrapperClassName) {
classes.push(wrapperClassName);
}
}
return (
<div className={classes.join(' ')}>
{viewMode !== 'popup' && this.renderDragUploadArea()}
{this.renderHeader(!isCard && uploadBtn)}
{!__inGroup && viewMode !== 'popup' && this.renderHelp()}
{!__inGroup && this.showValidation === ShowValidation.newLine && this.renderValidationResult()}
{!__inGroup && this.renderEmpty()}
{viewMode !== 'none' && this.renderUploadList(isCard && uploadBtn)}
</div>
);
}
getPictureWidth() {
const { pictureWidth, listType } = this.props;
return pictureWidth || (listType === 'picture-card' ? 100 : 48);
}
@autobind
handleHelpMouseEnter(e) {
const { getTooltipTheme } = this.context;
const { helpTooltipProps, help } = this;
let helpTooltipCls = `${this.getContextConfig('proPrefixCls')}-tooltip-popup-help`;
if (helpTooltipProps && helpTooltipProps.popupClassName) {
helpTooltipCls = helpTooltipCls.concat(' ', helpTooltipProps.popupClassName)
}
show(e.currentTarget, {
title: help,
theme: getTooltipTheme('help'),
placement: 'bottom', // 保留 bottom 原效果
...helpTooltipProps,
popupClassName: helpTooltipCls,
});
}
handleHelpMouseLeave() {
hide();
}
renderHelp(): ReactNode {
const { help, showAttachmentHelp } = this;
if (!help || showAttachmentHelp === ShowHelp.none) return;
switch (showAttachmentHelp) {
case ShowHelp.tooltip:
return (
<Icon
type="help"
style={{ fontSize: '0.14rem', color: '#8c8c8c' }}
onMouseEnter={this.handleHelpMouseEnter}
onMouseLeave={this.handleHelpMouseLeave}
/>
);
default:
return (
<div key="help" className={`${this.getContextProPrefixCls(FIELD_SUFFIX)}-help`}>
{toJS(help)}
</div>
);
}
}
get showValidation() {
const { viewMode, showValidation = viewMode === 'popup' ? ShowValidation.tooltip : ShowValidation.newLine } = this.props;
const { context: { showValidation: ctxShowValidation = showValidation } } = this;
return ctxShowValidation;
}
@autobind
handlePopupHiddenChange(hidden) {
this.setPopup(!hidden);
}
@mobxAction
setPopup(popup) {
this.popup = popup;
}
@mobxAction
setDragState(state) {
this.dragState = state;
}
@autobind
handleFileDrop(e) {
this.setDragState(e.type);
}
renderDefaultDragBox() {
const { prefixCls, help } = this;
return (
<div className={`${prefixCls}-drag-box`}>
<p className={`${prefixCls}-drag-box-icon`}>
<Icon type="inbox" />
</p>
<p className={`${prefixCls}-drag-box-text`}>{$l('Attachment', 'file_type_mismatch')}</p>
<p className={`${prefixCls}-drag-box-hint`}>
{help}
</p>
</div>
);
}
renderDragUploadArea() {
const { dragUpload, dragBoxRender, readOnly } = this.props;
const { prefixCls, accept } = this;
if (dragUpload) {
const isDisabled = this.isDisabled() || readOnly;
const dragCls = classNames(prefixCls, {
[`${prefixCls}-drag`]: true,
[`${prefixCls}-drag-hover`]: this.dragState === 'dragover',
[`${prefixCls}-drag-disabled`]: isDisabled,
});
const rcUploadProps = {
...this.props,
disabled: isDisabled,
accept: accept ? accept.join(',') : undefined,
beforeUpload: this.handleDragUpload,
prefixCls,
};
return (
<div
className={dragCls}
onDrop={this.handleFileDrop}
onDragOver={this.handleFileDrop}
onDragLeave={this.handleFileDrop}
>
<RcUpload {...rcUploadProps} className={`${prefixCls}-btn`}>
{dragBoxRender || this.renderDefaultDragBox()}
</RcUpload>
</div>
);
}
return undefined;
}
render() {
const { viewMode, listType, hidden } = this.props;
const { readOnly, prefixCls } = this;
if (viewMode === 'popup') {
const label = this.hasFloatLabel && this.getLabel();
return (
<>
<Trigger
prefixCls={prefixCls}
popupContent={this.renderWrapper}
action={[Action.hover, Action.focus]}
builtinPlacements={BUILT_IN_PLACEMENTS}
popupPlacement="bottomLeft"
popupHidden={hidden || !this.popup}
onPopupHiddenChange={this.handlePopupHiddenChange}
>
{this.renderDragUploadArea()}
{readOnly ? this.renderViewButton(label) : this.renderUploadBtn(false, label)}
</Trigger>
{this.renderTemplateDownloadButton()}
{this.renderHelp()}
{this.showValidation === ShowValidation.newLine && this.renderValidationResult()}
</>
);
}
return this.renderWrapperList(readOnly ? undefined : this.renderUploadBtn(listType === 'picture-card'));
}
}
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарий ( 0 )