oak-general-business/src/components/extraFile/gallery/web.tsx

339 lines
12 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import {
Space,
Upload,
UploadFile,
Tag,
Button,
Table,
} from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { file2Obj } from 'antd/es/upload/utils'
import { RcFile } from 'antd/es/upload/interface';
import classNames from 'classnames';
import Style from './web.module.less';
import { WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '../../../general-app-domain';
import useFeatures from '../../../hooks/useFeatures';
interface NewUploadFile extends UploadFile {
id?: string;
}
type Theme = 'file' | 'image' | 'image-flow' | 'custom';
type ListType = 'text' | 'picture' | 'picture-card';
function getListType(theme: Theme): ListType {
const themeMap: Record<Theme, ListType> = {
file: 'text',
image: 'picture-card',
'image-flow': 'picture',
custom: 'text',
};
return themeMap[theme];
}
export default function render(
props: WebComponentProps<
EntityDict,
'extraFile',
true,
{
accept?: string;
maxNumber?: number;
multiple?: boolean;
draggable?: boolean;
theme?: Theme;
tips?: string;
beforeUpload?: (file: File) => Promise<boolean>;
disabled?: boolean;
style?: Record<string, string>;
className?: string;
directory?: boolean;
onPreview?: (file: UploadFile<any>) => void;
onDownload?: (file: UploadFile<any>) => void;
showUploadList?: boolean;
children?: JSX.Element;
files?: EntityDict['extraFile']['OpSchema'][];
disableInsert?: boolean;
},
{
onPickByWeb: (
files: UploadFile[],
callback?: (file: any, status: string) => void
) => void;
onDeleteByWeb: (file: UploadFile) => void;
}
>
) {
const {
accept = 'image/*',
maxNumber = 20,
multiple = maxNumber !== 1,
draggable = false,
theme = 'image',
tips,
beforeUpload,
disabled,
style,
className,
directory = false,
onPreview,
onDownload,
children,
showUploadList = true,
files,
disableInsert,
} = props.data;
const { onPickByWeb, onDeleteByWeb } = props.methods;
const features = useFeatures();
const [newFiles, setNewFiles] = useState<
EntityDict['extraFile']['OpSchema'][]
>([]);
const [newUploadFiles, setNewUploadFiles] = useState([] as NewUploadFile[]);
const listType = getListType(theme);
useEffect(() => {
if (files && files.length > 0) {
setNewFiles(files);
} else {
setNewFiles([]);
}
}, [files]);
const extraFileToUploadFile = (
extraFile: EntityDict['extraFile']['OpSchema'],
): NewUploadFile => {
const filename =
extraFile.filename +
(extraFile.extension ? `.${extraFile.extension}` : '');
return {
id: extraFile.id,
url: features.extraFile.getUrl(extraFile),
thumbUrl: features.extraFile.getUrl(extraFile),
name: filename,
fileName: filename,
size: extraFile.size!,
type: extraFile.fileType,
uid: extraFile.id, //upload 组件需要uid来维护fileList
// status: 'done',
};
}
const setNewUploadFilesByStatus = (
file: EntityDict['extraFile']['Schema'],
status: string
) => {
const { filename, size, id } = file;
const file2 = newUploadFiles.find(
(ele: NewUploadFile) =>
ele.name?.includes(filename) && ele.size === size
);
if (file2) {
Object.assign(file2, {
status,
id,
});
}
setNewUploadFiles(newUploadFiles);
};
const customDelete = (index: number) => {
const arr = [...newUploadFiles];
arr.splice(index, 1);
setNewUploadFiles(arr);
};
const getUploadButton = () => {
if (children) {
return children;
}
if (listType === 'picture-card') {
return (
<div>
<PlusOutlined />
<div style={{ marginTop: 8 }}></div>
</div>
);
}
return <Button type="default"></Button>;
};
return (
<Space
direction="vertical"
className={Style['oak-upload']}
style={{ width: '100%' }}
>
<Upload
className={classNames(Style['oak-upload__upload'], className)}
style={style}
disabled={disabled}
directory={directory}
showUploadList={showUploadList}
beforeUpload={async (file) => {
if (typeof beforeUpload === 'function') {
const result = await beforeUpload(file);
if (result) {
return false;
}
}
return false;
}}
multiple={multiple}
maxCount={maxNumber}
accept={accept}
listType={listType}
fileList={
theme === 'custom'
? []
: newFiles?.map((ele) =>
extraFileToUploadFile(ele)
)
}
onChange={({ file, fileList, event }) => {
// id不存在就是file对象
if (!(file as NewUploadFile).id) {
if (theme !== 'custom') {
onPickByWeb([file2Obj(file as RcFile)]);
} else {
setNewUploadFiles([file2Obj(file as RcFile)]);
}
}
}}
onRemove={(file) => {
onDeleteByWeb(file);
}}
onPreview={onPreview}
onDownload={onDownload}
>
{disableInsert ? null : getUploadButton()}
</Upload>
{tips && (
<small className={Style['oak-upload__tips']}>{tips}</small>
)}
{theme === 'custom' && (
<>
<Table
dataSource={newUploadFiles || []}
rowKey="id"
columns={[
{
align: 'center',
dataIndex: 'tableIndex',
title: '序号',
render: (value, record, index) => index + 1,
width: 100,
},
{
dataIndex: 'name',
title: '文件名',
},
{
dataIndex: 'size',
title: '文件大小',
render: (value, record, index) => {
return features.extraFile.formatBytes(
value as number
);
},
},
{
dataIndex: 'status',
title: '状态',
render: (value, record, index) => {
let cpn: any;
switch (value) {
case 'success':
cpn = (
<Tag color="success">
success
</Tag>
);
break;
case 'uploading':
cpn = (
<Tag color="processing">
uploading
</Tag>
);
break;
default:
cpn = (
<Tag color="warning">
waiting
</Tag>
);
break;
}
return cpn;
},
},
{
dataIndex: 'op',
width: 300,
title: '操作',
align: 'center',
render: (value, record, index) => {
// 只处理state的文件 这时候可以直接删除
return (
<>
{!record.id && (
<Button
type="link"
onClick={() => {
customDelete(index);
}}
>
</Button>
)}
</>
);
},
fixed: 'right',
},
]}
/>
<div
style={{ display: 'flex', justifyContent: 'flex-end' }}
>
<Space>
<Button
danger
type="default"
onClick={() => setNewUploadFiles([])}
>
</Button>
<Button
type="primary"
onClick={() => {
onPickByWeb(
newUploadFiles,
(file: any, status: string) => {
setNewUploadFilesByStatus(
file,
status
);
}
);
}}
>
</Button>
</Space>
</div>
</>
)}
</Space>
);
}