Sfoglia il codice sorgente

'添加组件FileIcon和FileStatus(未完成)'

main
郑州 3 anni fa
parent
commit
e7ac092e60
9 ha cambiato i file con 289 aggiunte e 0 eliminazioni
  1. +22
    -0
      docs/fileIcon.md
  2. +14
    -0
      docs/fileStatus.md
  3. +47
    -0
      src/components/FileIcon/FileIcon.tsx
  4. +4
    -0
      src/components/FileIcon/fileIcon.less
  5. +1
    -0
      src/components/FileIcon/index.ts
  6. +101
    -0
      src/components/FileStatus/FileStatus.less
  7. +91
    -0
      src/components/FileStatus/FileStatus.tsx
  8. +1
    -0
      src/components/FileStatus/index.ts
  9. +8
    -0
      src/components/Tooltip/index.tsx

+ 22
- 0
docs/fileIcon.md Vedi File

@@ -0,0 +1,22 @@

# 文件图标

```jsx
import React from 'react';
import FileIcon from '@/components/FileIcon';

export default () => (
<>
{/* 无法识别的的扩展名最终都会展示default */}
<FileIcon extension="unknown_extenstion" />
{/* 等价扩展名: doc, docx */}
<FileIcon extension="doc" />
{/* 等价扩展名: ppt, pptx */}
<FileIcon extension="ppt" />
{/* 等价扩展名: xls, xlsx */}
<FileIcon extension="xls" />
<FileIcon extension="zip" />
<FileIcon extension="folder" />
</>
)
```

+ 14
- 0
docs/fileStatus.md Vedi File

@@ -0,0 +1,14 @@
```jsx
import React from 'react';
import FileStatus from '@/components/FileStatus';

export default () => (
<>
<FileStatus type="upload" loadingState="loading" progress={30} />
<FileStatus type="upload" loadingState="complete" result="success" />
<FileStatus type="upload" loadingState="complete" result="fail" />
<FileStatus type="download" loadingState="complete" result="success" />
<FileStatus type="download" loadingState="complete" result="fail" />
</>
)
```

+ 47
- 0
src/components/FileIcon/FileIcon.tsx Vedi File

@@ -0,0 +1,47 @@
import React from 'react';
import { Image, ImageProps } from 'antd';
import classNames from 'classnames';
import { memo } from 'react';
import styles from './fileIcon.less';
import { memoize } from 'lodash';

interface FileIconProps extends ImageProps {
extension: string;
}

export default memo(function FileIcon(props: FileIconProps) {
const { className, extension, ...restProps } = props;
return (
<div className={classNames(styles.fileIcon, className)}>
<Image
preview={false}
{...restProps}
src={fileIconSrc(extension)}
fallback={fileIconSrc('default')}
/>
</div>
)
});


const fileIconSrc = memoize((extension: string) => {
const fixedExtension = matchFileImage(extension);
return `/assets/file_icon/${fixedExtension}.svg`;
})

export function matchFileImage(extension: string) {
switch (extension) {
case 'ppt':
case 'pptx':
return 'ppt';
case 'xls':
case 'xlsx':
return 'excel';
case 'doc':
case 'docx':
return 'word';
default:
return extension;
}
}


+ 4
- 0
src/components/FileIcon/fileIcon.less Vedi File

@@ -0,0 +1,4 @@
.fileIcon {
display: inline-flex;
align-items: center;
}

+ 1
- 0
src/components/FileIcon/index.ts Vedi File

@@ -0,0 +1 @@
export { default } from './FileIcon';

+ 101
- 0
src/components/FileStatus/FileStatus.less Vedi File

@@ -0,0 +1,101 @@
.fileStatus {
display: flex;
align-items: center;
flex-direction: row;
background: #FFFFFF;
box-shadow: 0px 1px 2px 0px rgba(102, 110, 115, 0.3);
border-radius: 4px;
height: 76px;
padding: 12px;
.left {
width: 228px;
flex: 0 0 228px;
margin-right: 10px;
}
.mid {
flex: 1;
text-align: center;
}
.right {
flex: 0 0 80px;
margin-left: 10px;
.button {
color: @primary-color;
font-size: 12px;
}
}
}
.left {
height: 100%;
.icon { width:36px; margin-right: 12px;}
.content {
display: inline-flex;
width: calc(100% - 36px - 12px);
height: 100%;
vertical-align: top;
flex-direction: column;
justify-content: space-between;
.fileName {
flex: none;
font-size: 14px;
color: rgba(#000, 0.8);
.textOverflow();
}
.subContent {
display: flex;
flex-direction: row;
font-size: 12px;
color: rgba(#000, 0.6);
.filePath {
flex: 1;
margin-right: 8px;
.textOverflow();
}
.modifyInfo {
flex: none;
}
}
}
}

.loadDesc {
.font();
.font {
font-size: 12px;
color: rgba(#0f0f0f, 0.8);
}
:global {
.ant-progress-text {
.font();
width: 50px;
}
.ant-progress-show-info .ant-progress-outer {
margin-right: -58px;
padding-right: 58px;
}
}

.stateIcon {
vertical-align: sub;
margin-right: 8px;
font-size: 16px;
&.success { color: #51DCB6; }
&.error {color: #D6243A; }
}
}
.time {
display: inline-block;
margin-left: 8px;
font-size: 12px;
color: rgba(#0f0f0f, 0.6);
transform: scale(0.83);
}



.textOverflow {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}


+ 91
- 0
src/components/FileStatus/FileStatus.tsx Vedi File

@@ -0,0 +1,91 @@
import classNames from 'classnames';
import dayjs from 'dayjs';
import React, { CSSProperties, useMemo } from 'react';
import { memo } from 'react';
import FileIcon from '../FileIcon';
import ATooltip from '../Tooltip';
import styles from './FileStatus.less';
import { CloseCircleFilled, CheckCircleFilled } from '@ant-design/icons';
import { Progress, Button } from 'antd';

interface FileStatusProps {
className?: string;
style?: CSSProperties;
}


export default function FileStatus(props: FileStatusProps & LoadDescProps) {
const { className, style, ...restProps } = props;
return (
<div className={classNames(styles.fileStatus, className)} style={style}>
<div className={styles.left}>
<FileIcon className={styles.icon} extension="folder" />
<div className={styles.content}>
<ATooltip placement="top" title="123">
<div className={styles.fileName}>文件名称文件名称文件名称文件名称文件名称文件名称文件名称文件名称文件名称文件名称文件名称文件名称</div>
</ATooltip>
<div className={styles.subContent}>
<ATooltip placement="bottom" title="456">
<div className={styles.filePath}>文件路径文件路径文件路径文件路径文件路径文件路径</div>
</ATooltip>
<div className={styles.modifyInfo}>XX创建/XX同步</div>
</div>
</div>
</div>
<div className={styles.mid}>
<LoadDesc {...restProps} />
{
restProps.loadingState === 'complete'
? <Time time="2020-01-01 18:24:56" />
: null
}
</div>
<div className={styles.right}>
{/* 查看1: 已下载 文件打开文件夹 */}
{/* 查看2: 未下载 且 已删除 文件跳转到web端 */}
{/* 下载: 未下载 且 未删除 文件 */}
{/* 重新下载: 下载失败时出现 */}
{/* 重新上传: 上传失败时出现 */}
{/* <Button type="link" className={styles.button} size="small">重新上传</Button> */}
<Button type="link" className={styles.button} size="small">暂停</Button>
<Button type="link" className={styles.button} size="small">取消</Button>
</div>
</div>
)
}

const Time = memo((props: { time: string }) => (
<span className={styles.time}>{dayjs(props.time).format('HH:mm:ss')}</span>
));

interface LoadDescProps {
type: 'upload' | 'download';
loadingState: 'loading' | 'complete';
result?: 'success' | 'fail';
progress?: number;
}

function LoadDesc(props: LoadDescProps) {
const { result, type, loadingState, progress } = props;
const keywords = type === 'upload' ? '上传' : '下载';
const [resultText, icon] = useMemo(() => {
if (result === 'success') { return ['成功', <CheckCircleFilled className={classNames(styles.stateIcon, styles.success)} />]; }
if (result === 'fail') { return ['失败', <CloseCircleFilled className={classNames(styles.stateIcon, styles.error)} />]; }
return ['', null];
}, [result]);
return (
<span className={styles.loadDesc}>
{
loadingState === 'loading'
? <Progress percent={progress} format={() => `${keywords}中...`} />
: (
<>
{icon}
{keywords}
{resultText}!
</>
)
}
</span>
)
}

+ 1
- 0
src/components/FileStatus/index.ts Vedi File

@@ -0,0 +1 @@
export { default } from './FileStatus';

+ 8
- 0
src/components/Tooltip/index.tsx Vedi File

@@ -0,0 +1,8 @@
import React from 'react';
import { Tooltip, TooltipProps } from 'antd';

export default function ATooltip(props: TooltipProps) {
return (
<Tooltip color="#fff" overlayInnerStyle={{ color: "rgba(0, 0, 0, 0.53)" }} {...props} />
)
}

Caricamento…
Annulla
Salva