| | |
| | | const NbCard = (props) => { |
| | | const { tasks, total, ...rset } = props; |
| | | const translate = useTranslate(); |
| | | |
| | | // 状态管理屏幕高度 |
| | | const [screenHeight, setScreenHeight] = React.useState(window.innerHeight); |
| | | const [containerHeight, setContainerHeight] = React.useState('100vh'); |
| | | |
| | | const display = 'display'; |
| | | // 监听窗口大小变化 |
| | | React.useEffect(() => { |
| | | const handleResize = () => { |
| | | setScreenHeight(window.innerHeight); |
| | | }; |
| | | |
| | | window.addEventListener('resize', handleResize); |
| | | |
| | | // 计算容器高度(减去可能的头部导航栏高度) |
| | | const calculateContainerHeight = () => { |
| | | const headerHeight = 64; // 假设头部导航栏高度为64px |
| | | const padding = 16; // 上下边距 |
| | | return `calc(${screenHeight}px - ${headerHeight + padding * 2}px)`; |
| | | }; |
| | | |
| | | setContainerHeight(calculateContainerHeight()); |
| | | |
| | | return () => { |
| | | window.removeEventListener('resize', handleResize); |
| | | }; |
| | | }, [screenHeight]); |
| | | |
| | | return ( |
| | | <> |
| | | <Box sx={{ |
| | | height: containerHeight, // 动态计算的高度 |
| | | maxHeight: 690, //containerHeight, |
| | | display: 'flex', |
| | | flexDirection: 'column', |
| | | minHeight: '400px', // 最小高度保证 |
| | | // overflowY: 'auto' |
| | | }}> |
| | | <CardWithIcon |
| | | icon={CommentIcon} |
| | | title={translate('page.dashboard.pending_reviews')} |
| | | subtitle={total} |
| | | {...rset} |
| | | sx={{ |
| | | flex: 1, |
| | | display: 'flex', |
| | | flexDirection: 'column', |
| | | height: '100%', |
| | | minHeight: 0, |
| | | }} |
| | | > |
| | | <List sx={{ display }}> |
| | | {tasks?.map((record) => ( |
| | | <ListItem key={record.id} disablePadding> |
| | | <ListItemButton |
| | | alignItems="flex-start" |
| | | component={Link} |
| | | to={`/task/${record.id}`} |
| | | > |
| | | <Grid container item md={12}> |
| | | <Box sx={{ display: 'flex' }}> |
| | | <Box sx={{ display: 'flex', padding: '1em' }}> |
| | | <Typography color="textSecondary">{translate("table.field.task.taskCode")}:</Typography> |
| | | <Typography color="textSecondary">{record?.taskCode}</Typography> |
| | | </Box> |
| | | </Box> |
| | | <Box sx={{ display: 'flex' }}> |
| | | <Box sx={{ display: 'flex', padding: '1em' }}> |
| | | <Typography color="textSecondary">{translate("table.field.task.taskType")}:</Typography> |
| | | <Typography color="textSecondary" maxWidth="200" overflow="hidden">{record?.taskType$}</Typography> |
| | | </Box> |
| | | </Box> |
| | | <Box sx={{ display: 'flex' }}> |
| | | <Box sx={{ display: 'flex', padding: '1em' }}> |
| | | <Typography color="textSecondary">{translate("table.field.task.taskStatus")}:</Typography> |
| | | <Typography color="textSecondary">{record?.taskStatus$}</Typography> |
| | | </Box> |
| | | </Box> |
| | | <Box sx={{ display: 'flex' }}> |
| | | <Box sx={{ display: 'flex', padding: '1em' }}> |
| | | <Typography color="textSecondary">{translate("table.field.task.startTime")}:</Typography> |
| | | <Typography color="textSecondary">{record?.createTime}</Typography> |
| | | </Box> |
| | | </Box> |
| | | </Grid> |
| | | </ListItemButton> |
| | | <Spacer /> |
| | | </ListItem> |
| | | ))} |
| | | </List> |
| | | <Box flexGrow={1}> </Box> |
| | | {/* <Button |
| | | sx={{ borderRadius: 0 }} |
| | | component={Link} |
| | | to="/task" |
| | | size="small" |
| | | color="primary" |
| | | > |
| | | <Box p={1} sx={{ color: 'primary.main' }}> |
| | | {translate('pos.dashboard.all_reviews')} |
| | | {/* 内容容器 - 自适应高度 */} |
| | | <Box sx={{ |
| | | flex: 1, |
| | | display: 'flex', |
| | | flexDirection: 'column', |
| | | minHeight: 0, |
| | | height: '100%', |
| | | }}> |
| | | {/* 列表区域 - 自动滚动 */} |
| | | <Box sx={{ |
| | | flex: 1, |
| | | overflow: 'auto', |
| | | minHeight: 0, |
| | | '&::-webkit-scrollbar': { |
| | | width: '6px', |
| | | }, |
| | | '&::-webkit-scrollbar-track': { |
| | | background: 'transparent', |
| | | }, |
| | | '&::-webkit-scrollbar-thumb': { |
| | | background: '#ccc', |
| | | borderRadius: '3px', |
| | | '&:hover': { |
| | | background: '#aaa', |
| | | } |
| | | } |
| | | }}> |
| | | <List sx={{ py: 0, minHeight: 'min-content' }}> |
| | | {tasks?.length > 0 ? ( |
| | | tasks.map((record) => ( |
| | | <ListItem |
| | | key={record.id} |
| | | disablePadding |
| | | sx={{ |
| | | borderBottom: '1px solid', |
| | | borderColor: 'divider', |
| | | '&:last-child': { |
| | | borderBottom: 'none', |
| | | } |
| | | }} |
| | | > |
| | | <ListItemButton |
| | | component={Link} |
| | | to={`/task/${record.id}`} |
| | | sx={{ |
| | | py: 1.5, |
| | | px: 2, |
| | | '&:hover': { |
| | | backgroundColor: 'action.hover', |
| | | } |
| | | }} |
| | | > |
| | | <Grid container spacing={1}> |
| | | {/* 第一行:任务编码和类型 */} |
| | | <Grid item xs={12} sx={{ display: 'flex', alignItems: 'center' }}> |
| | | <Box sx={{ |
| | | display: 'flex', |
| | | alignItems: 'center', |
| | | minWidth: 0, |
| | | flex: 1, |
| | | mr: 2 |
| | | }}> |
| | | <Typography |
| | | variant="body2" |
| | | sx={{ |
| | | fontWeight: 600, |
| | | color: 'primary.main', |
| | | minWidth: '80px', |
| | | flexShrink: 0, |
| | | }} |
| | | > |
| | | {translate("table.field.task.taskCode")}: |
| | | </Typography> |
| | | <Typography |
| | | variant="body2" |
| | | sx={{ |
| | | color: 'text.primary', |
| | | fontWeight: 500, |
| | | overflow: 'hidden', |
| | | textOverflow: 'ellipsis', |
| | | whiteSpace: 'nowrap', |
| | | }} |
| | | > |
| | | {record?.taskCode} |
| | | </Typography> |
| | | </Box> |
| | | |
| | | <Box sx={{ |
| | | display: 'flex', |
| | | alignItems: 'center', |
| | | minWidth: 0, |
| | | flex: 1 |
| | | }}> |
| | | <Typography |
| | | variant="body2" |
| | | sx={{ |
| | | fontWeight: 600, |
| | | color: 'primary.main', |
| | | minWidth: '80px', |
| | | flexShrink: 0, |
| | | }} |
| | | > |
| | | {translate("table.field.task.taskType")}: |
| | | </Typography> |
| | | <Typography |
| | | variant="body2" |
| | | sx={{ |
| | | color: 'text.primary', |
| | | overflow: 'hidden', |
| | | textOverflow: 'ellipsis', |
| | | whiteSpace: 'nowrap', |
| | | }} |
| | | title={record?.taskType$} |
| | | > |
| | | {record?.taskType$} |
| | | </Typography> |
| | | </Box> |
| | | </Grid> |
| | | |
| | | {/* 第二行:状态和时间 */} |
| | | <Grid item xs={12} sx={{ display: 'flex', alignItems: 'center' }}> |
| | | <Box sx={{ |
| | | display: 'flex', |
| | | alignItems: 'center', |
| | | minWidth: 0, |
| | | flex: 1, |
| | | mr: 2 |
| | | }}> |
| | | <Typography |
| | | variant="body2" |
| | | sx={{ |
| | | fontWeight: 600, |
| | | color: 'primary.main', |
| | | minWidth: '80px', |
| | | flexShrink: 0, |
| | | }} |
| | | > |
| | | {translate("table.field.task.taskStatus")}: |
| | | </Typography> |
| | | <Typography |
| | | variant="body2" |
| | | sx={{ |
| | | color: getStatusColor(record?.taskStatus$), |
| | | fontWeight: 600, |
| | | px: 1, |
| | | py: 0.5, |
| | | borderRadius: 1, |
| | | backgroundColor: getStatusBackground(record?.taskStatus$), |
| | | fontSize: '0.75rem', |
| | | }} |
| | | > |
| | | {record?.taskStatus$} |
| | | </Typography> |
| | | </Box> |
| | | |
| | | <Box sx={{ |
| | | display: 'flex', |
| | | alignItems: 'center', |
| | | minWidth: 0, |
| | | flex: 1 |
| | | }}> |
| | | <Typography |
| | | variant="body2" |
| | | sx={{ |
| | | fontWeight: 600, |
| | | color: 'primary.main', |
| | | minWidth: '80px', |
| | | flexShrink: 0, |
| | | }} |
| | | > |
| | | {translate("table.field.task.startTime")}: |
| | | </Typography> |
| | | <Typography |
| | | variant="body2" |
| | | sx={{ |
| | | color: 'text.secondary', |
| | | fontFamily: 'monospace', |
| | | fontSize: '0.875rem', |
| | | }} |
| | | > |
| | | {formatDateTime(record?.createTime)} |
| | | </Typography> |
| | | </Box> |
| | | </Grid> |
| | | </Grid> |
| | | </ListItemButton> |
| | | </ListItem> |
| | | )) |
| | | ) : ( |
| | | // 空状态提示 - 居中显示 |
| | | <Box sx={{ |
| | | display: 'flex', |
| | | alignItems: 'center', |
| | | justifyContent: 'center', |
| | | height: '100%', |
| | | minHeight: 120, |
| | | color: 'text.secondary' |
| | | }}> |
| | | <Typography variant="body2"> |
| | | {translate('common.noData')} |
| | | </Typography> |
| | | </Box> |
| | | )} |
| | | </List> |
| | | </Box> |
| | | </Button> */} |
| | | |
| | | {/* 底部按钮区域 - 固定在底部 */} |
| | | {tasks?.length > 0 && ( |
| | | <Box sx={{ |
| | | borderTop: '1px solid', |
| | | borderColor: 'divider', |
| | | flexShrink: 0, |
| | | mt: 'auto', |
| | | }}> |
| | | <Button |
| | | component={Link} |
| | | to="/task" |
| | | size="small" |
| | | color="primary" |
| | | fullWidth |
| | | sx={{ |
| | | borderRadius: 0, |
| | | py: 1, |
| | | fontSize: '0.875rem', |
| | | fontWeight: 500, |
| | | }} |
| | | > |
| | | {translate('pos.dashboard.all_reviews')} |
| | | </Button> |
| | | </Box> |
| | | )} |
| | | </Box> |
| | | </CardWithIcon> |
| | | </> |
| | | </Box> |
| | | ); |
| | | }; |
| | | |
| | | const Spacer = () => <span style={{ width: '1em', }} />; |
| | | // 状态颜色映射 |
| | | const getStatusColor = (status) => { |
| | | const statusMap = { |
| | | 'pending': 'warning.main', |
| | | 'completed': 'success.main', |
| | | 'processing': 'info.main', |
| | | 'cancelled': 'error.main', |
| | | 'failed': 'error.main', |
| | | }; |
| | | return statusMap[status?.toLowerCase()] || 'text.secondary'; |
| | | }; |
| | | |
| | | // 状态背景色 |
| | | const getStatusBackground = (status) => { |
| | | const backgroundMap = { |
| | | 'pending': 'warning.light', |
| | | 'completed': 'success.light', |
| | | 'processing': 'info.light', |
| | | 'cancelled': 'error.light', |
| | | 'failed': 'error.light', |
| | | }; |
| | | return backgroundMap[status?.toLowerCase()] || 'grey.100'; |
| | | }; |
| | | |
| | | export default NbCard; |
| | | // 格式化日期时间 |
| | | const formatDateTime = (dateTime) => { |
| | | if (!dateTime) return '-'; |
| | | try { |
| | | return new Date(dateTime).toLocaleDateString('zh-CN', { |
| | | month: '2-digit', |
| | | day: '2-digit', |
| | | hour: '2-digit', |
| | | minute: '2-digit' |
| | | }); |
| | | } catch { |
| | | return dateTime; |
| | | } |
| | | }; |
| | | |
| | | export default NbCard; |