| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| import React, { useState } from 'react';
|
| import { Card, Divider, Typography, Button } from '@douyinfe/semi-ui';
|
| import PropTypes from 'prop-types';
|
| import { useIsMobile } from '../../../hooks/common/useIsMobile';
|
| import { IconEyeOpened, IconEyeClosed } from '@douyinfe/semi-icons';
|
|
|
| const { Text } = Typography;
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| const CardPro = ({
|
| type = 'type1',
|
| className = '',
|
| children,
|
| // 各个区域的内容
|
| statsArea,
|
| descriptionArea,
|
| tabsArea,
|
| actionsArea,
|
| searchArea,
|
| paginationArea, // 新增分页区域
|
| // 卡片属性
|
| shadows = '',
|
| bordered = true,
|
| // 自定义样式
|
| style,
|
| // 国际化函数
|
| t = (key) => key,
|
| ...props
|
| }) => {
|
| const isMobile = useIsMobile();
|
| const [showMobileActions, setShowMobileActions] = useState(false);
|
|
|
| const toggleMobileActions = () => {
|
| setShowMobileActions(!showMobileActions);
|
| };
|
|
|
| const hasMobileHideableContent = actionsArea || searchArea;
|
|
|
| const renderHeader = () => {
|
| const hasContent =
|
| statsArea || descriptionArea || tabsArea || actionsArea || searchArea;
|
| if (!hasContent) return null;
|
|
|
| return (
|
| <div className='flex flex-col w-full'>
|
| {/* 统计信息区域 - 用于type2 */}
|
| {type === 'type2' && statsArea && <>{statsArea}</>}
|
|
|
| {}
|
| {(type === 'type1' || type === 'type3') && descriptionArea && (
|
| <>{descriptionArea}</>
|
| )}
|
|
|
| {}
|
| {((type === 'type1' || type === 'type3') && descriptionArea) ||
|
| (type === 'type2' && statsArea) ? (
|
| <Divider margin='12px' />
|
| ) : null}
|
|
|
| {}
|
| {type === 'type3' && tabsArea && <>{tabsArea}</>}
|
|
|
| {}
|
| {isMobile && hasMobileHideableContent && (
|
| <>
|
| <div className='w-full mb-2'>
|
| <Button
|
| onClick={toggleMobileActions}
|
| icon={showMobileActions ? <IconEyeClosed /> : <IconEyeOpened />}
|
| type='tertiary'
|
| size='small'
|
| theme='outline'
|
| block
|
| >
|
| {showMobileActions ? t('隐藏操作项') : t('显示操作项')}
|
| </Button>
|
| </div>
|
| </>
|
| )}
|
|
|
| {}
|
| <div
|
| className={`flex flex-col gap-2 ${isMobile && !showMobileActions ? 'hidden' : ''}`}
|
| >
|
| {}
|
| {(type === 'type1' || type === 'type3') &&
|
| actionsArea &&
|
| (Array.isArray(actionsArea) ? (
|
| actionsArea.map((area, idx) => (
|
| <React.Fragment key={idx}>
|
| {idx !== 0 && <Divider />}
|
| <div className='w-full'>{area}</div>
|
| </React.Fragment>
|
| ))
|
| ) : (
|
| <div className='w-full'>{actionsArea}</div>
|
| ))}
|
|
|
| {}
|
| {actionsArea && searchArea && <Divider />}
|
|
|
| {}
|
| {searchArea && <div className='w-full'>{searchArea}</div>}
|
| </div>
|
| </div>
|
| );
|
| };
|
|
|
| const headerContent = renderHeader();
|
|
|
|
|
| const renderFooter = () => {
|
| if (!paginationArea) return null;
|
|
|
| return (
|
| <div
|
| className={`flex w-full pt-4 border-t ${isMobile ? 'justify-center' : 'justify-between items-center'}`}
|
| style={{ borderColor: 'var(--semi-color-border)' }}
|
| >
|
| {paginationArea}
|
| </div>
|
| );
|
| };
|
|
|
| const footerContent = renderFooter();
|
|
|
| return (
|
| <Card
|
| className={`table-scroll-card !rounded-2xl ${className}`}
|
| title={headerContent}
|
| footer={footerContent}
|
| shadows={shadows}
|
| bordered={bordered}
|
| style={style}
|
| {...props}
|
| >
|
| {children}
|
| </Card>
|
| );
|
| };
|
|
|
| CardPro.propTypes = {
|
|
|
| type: PropTypes.oneOf(['type1', 'type2', 'type3']),
|
|
|
| className: PropTypes.string,
|
| style: PropTypes.object,
|
| shadows: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
|
| bordered: PropTypes.bool,
|
|
|
| statsArea: PropTypes.node,
|
| descriptionArea: PropTypes.node,
|
| tabsArea: PropTypes.node,
|
| actionsArea: PropTypes.oneOfType([
|
| PropTypes.node,
|
| PropTypes.arrayOf(PropTypes.node),
|
| ]),
|
| searchArea: PropTypes.node,
|
| paginationArea: PropTypes.node,
|
|
|
| children: PropTypes.node,
|
|
|
| t: PropTypes.func,
|
| };
|
|
|
| export default CardPro;
|
|
|