From 3060671bbaa7fe72dd47b7c7b30e385cfdf11802 Mon Sep 17 00:00:00 2001
From: vincentlu <t1341870251@gmail.com>
Date: 星期日, 04 一月 2026 14:36:13 +0800
Subject: [PATCH] #
---
zy-acs-flow/src/page/sta/StaPanel.jsx | 283 ++++++++++++++++++++++++++++++++++++++++++++++---------
1 files changed, 234 insertions(+), 49 deletions(-)
diff --git a/zy-acs-flow/src/page/sta/StaPanel.jsx b/zy-acs-flow/src/page/sta/StaPanel.jsx
index 13638dd..367bb32 100644
--- a/zy-acs-flow/src/page/sta/StaPanel.jsx
+++ b/zy-acs-flow/src/page/sta/StaPanel.jsx
@@ -1,31 +1,90 @@
-import React, { useState, useRef, useEffect, useMemo } from "react";
-import { Box, Card, CardContent, Grid, Typography, Tooltip } from '@mui/material';
+import React, { useState, useEffect } from "react";
+import {
+ Box,
+ Card,
+ CardContent,
+ Grid,
+ Typography,
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableRow,
+ Divider,
+ CircularProgress,
+ Stack,
+} from '@mui/material';
import {
useTranslate,
useRecordContext,
+ useDataProvider,
} from 'react-admin';
+import { format } from 'date-fns';
import PanelTypography from "../components/PanelTypography";
import * as Common from '@/utils/common'
+const STATUS_FIELDS = [
+ { key: 'autoing', labelKey: 'table.field.sta.autoing' },
+ { key: 'loading', labelKey: 'table.field.sta.loading' },
+ { key: 'inEnable', labelKey: 'table.field.sta.inEnable' },
+ { key: 'outEnable', labelKey: 'table.field.sta.outEnable' },
+];
+
const StaPanel = () => {
const record = useRecordContext();
- if (!record) return null;
const translate = useTranslate();
+ const dataProvider = useDataProvider();
+ const [reserves, setReserves] = useState([]);
+ const [isReservesLoading, setIsReservesLoading] = useState(false);
+
+ useEffect(() => {
+ if (!record?.id) {
+ setReserves([]);
+ setIsReservesLoading(false);
+ return;
+ }
+
+ let isMounted = true;
+ setIsReservesLoading(true);
+ dataProvider.getList('staReserve', {
+ pagination: { page: 1, perPage: 10 },
+ sort: { field: 'updateTime', order: 'DESC' },
+ filter: { staId: record.id },
+ })
+ .then(({ data }) => {
+ if (!isMounted) return;
+ setReserves(data || []);
+ })
+ .catch(() => {
+ if (!isMounted) return;
+ setReserves([]);
+ })
+ .finally(() => {
+ if (!isMounted) return;
+ setIsReservesLoading(false);
+ });
+
+ return () => {
+ isMounted = false;
+ };
+ }, [record?.id, dataProvider]);
+
+ if (!record) return null;
+
return (
<>
- <Card sx={{ width: { xs: 300, sm: 500, md: 600, lg: 800 }, margin: 'auto' }}>
+ <Card sx={{ width: { xs: 320, sm: 560, md: 680, lg: 900 }, margin: 'auto', mt: .5, mb: .5 }}>
<CardContent>
<Grid container spacing={2}>
<Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}>
<Typography variant="h6" gutterBottom align="left" sx={{
- maxWidth: { xs: '100px', sm: '180px', md: '260px', lg: '360px' },
+ maxWidth: { xs: '140px', sm: '220px', md: '300px', lg: '360px' },
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}>
{Common.camelToPascalWithSpaces(translate('table.field.sta.staNo'))}: {record.staNo}
</Typography>
- {/* inherit, primary, secondary, textPrimary, textSecondary, error */}
<Typography variant="h6" gutterBottom align="right" >
ID: {record.id}
</Typography>
@@ -34,96 +93,222 @@
<Grid container spacing={2}>
<Grid item xs={12} container alignContent="flex-end">
<Typography variant="caption" color="textSecondary" sx={{ wordWrap: 'break-word', wordBreak: 'break-all' }}>
- {Common.camelToPascalWithSpaces(translate('common.field.memo'))}:{record.memo}
+ {Common.camelToPascalWithSpaces(translate('common.field.memo'))}:{record.memo || '-'}
</Typography>
</Grid>
</Grid>
- <Box height={20}> </Box>
+ <Box height={16}> </Box>
<Grid container spacing={2}>
<Grid item xs={6}>
<PanelTypography
- title="table.field.sta.uuid"
+ title="table.field.sta.uuid"
property={record.uuid}
/>
</Grid>
<Grid item xs={6}>
<PanelTypography
- title="table.field.sta.zoneId"
+ title="table.field.sta.zoneId"
property={record.zoneId$}
/>
</Grid>
<Grid item xs={6}>
<PanelTypography
- title="table.field.sta.staNo"
- property={record.staNo}
- />
- </Grid>
- <Grid item xs={6}>
- <PanelTypography
- title="table.field.sta.name"
- property={record.name}
- />
- </Grid>
- <Grid item xs={6}>
- <PanelTypography
- title="table.field.sta.staType"
+ title="table.field.sta.staType"
property={record.staType$}
/>
</Grid>
<Grid item xs={6}>
<PanelTypography
- title="table.field.sta.code"
+ title="table.field.sta.code"
property={record.code$}
/>
</Grid>
<Grid item xs={6}>
<PanelTypography
- title="table.field.sta.offset"
+ title="table.field.sta.capacity"
+ property={record.capacity}
+ />
+ </Grid>
+ <Grid item xs={6}>
+ <PanelTypography
+ title="table.field.sta.offset"
property={record.offset}
/>
</Grid>
<Grid item xs={6}>
<PanelTypography
- title="table.field.sta.autoing"
- property={record.autoing}
+ title="table.field.sta.rsvInCnt"
+ property={record.rsvInCnt}
/>
</Grid>
<Grid item xs={6}>
<PanelTypography
- title="table.field.sta.loading"
- property={record.loading}
+ title="table.field.sta.rsvOutCnt"
+ property={record.rsvOutCnt}
/>
</Grid>
<Grid item xs={6}>
<PanelTypography
- title="table.field.sta.inEnable"
- property={record.inEnable}
- />
- </Grid>
- <Grid item xs={6}>
- <PanelTypography
- title="table.field.sta.outEnable"
- property={record.outEnable}
- />
- </Grid>
- <Grid item xs={6}>
- <PanelTypography
- title="table.field.sta.zpallet"
- property={record.zpallet}
- />
- </Grid>
- <Grid item xs={6}>
- <PanelTypography
- title="table.field.sta.staSts"
+ title="table.field.sta.staSts"
property={record.staSts$}
/>
</Grid>
-
+ <Grid item xs={6}>
+ <PanelTypography
+ title="table.field.sta.zpallet"
+ property={record.zpallet}
+ />
+ </Grid>
</Grid>
+
+ <Divider sx={{ my: 2 }} />
+
+ <Typography variant="subtitle2" color="textSecondary" gutterBottom>
+ {translate('common.field.status')} / {translate('table.field.sta.staNo')}
+ </Typography>
+ <Grid container spacing={2}>
+ {STATUS_FIELDS.map(({ key, labelKey }) => (
+ <Grid item xs={6} sm={3} key={key}>
+ <StatusIndicator labelKey={labelKey} value={record[key]} />
+ </Grid>
+ ))}
+ </Grid>
+
+ <Divider sx={{ my: 2 }} />
+
+ <Box>
+ <Typography variant="subtitle1" gutterBottom>
+ {translate('menu.staReserve')}
+ </Typography>
+ <Table size="small">
+ <TableHead>
+ <TableRow>
+ <TableCell>{translate('table.field.staReserve.name')}</TableCell>
+ <TableCell>{translate('table.field.staReserve.type')}</TableCell>
+ <TableCellRight>{translate('table.field.staReserve.qty')}</TableCellRight>
+ <TableCell>{translate('table.field.staReserve.state')}</TableCell>
+ <TableCell>{translate('table.field.staReserve.agvId')}</TableCell>
+ <TableCell>{translate('table.field.staReserve.waitingAt')}</TableCell>
+ <TableCell>{translate('table.field.staReserve.confirmedAt')}</TableCell>
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {isReservesLoading && (
+ <TableRow>
+ <TableCell colSpan={7}>
+ <Box display="flex" alignItems="center" gap={1}>
+ <CircularProgress size={16} />
+ <Typography variant="body2" color="textSecondary">
+ {translate('common.loading', { _: 'Loading...' })}
+ </Typography>
+ </Box>
+ </TableCell>
+ </TableRow>
+ )}
+ {!isReservesLoading && reserves.length === 0 && (
+ <TableRow>
+ <TableCell colSpan={7}>
+ <Typography variant="body2" color="textSecondary">
+ No reservations
+ </Typography>
+ </TableCell>
+ </TableRow>
+ )}
+ {reserves.map((reserve) => (
+ <TableRow key={reserve.id}>
+ <TableCell>{reserve.name || '-'}</TableCell>
+ <TableCell>{reserve.type || '-'}</TableCell>
+ <TableCellRight>{reserve.qty ?? '-'}</TableCellRight>
+ <TableCell>{reserve.state || '-'}</TableCell>
+ <TableCell>{reserve.agvId$ || reserve.agvId || '-'}</TableCell>
+ <TableCell>{formatDateTime(reserve.waitingAt)}</TableCell>
+ <TableCell>{formatDateTime(reserve.confirmedAt)}</TableCell>
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </Box>
</CardContent>
</Card >
</>
);
};
+const StatusIndicator = ({ labelKey, value }) => {
+ const translate = useTranslate();
+ const color = getIndicatorColor(value);
+ return (
+ <Stack spacing={0.5}>
+ <Typography variant="caption" color="textSecondary">
+ {Common.camelToPascalWithSpaces(translate(labelKey))}
+ </Typography>
+ <Box display="flex" alignItems="center" gap={1}>
+ <Box sx={{
+ width: 14,
+ height: 14,
+ borderRadius: '50%',
+ backgroundColor: color,
+ boxShadow: `0 0 6px ${color}`,
+ border: '1px solid rgba(0,0,0,0.12)'
+ }} />
+ <Typography variant="body2">
+ {formatIndicatorValue(value)}
+ </Typography>
+ </Box>
+ </Stack>
+ );
+};
+
+const getIndicatorColor = (value) => {
+ if (isTruthy(value)) {
+ return '#2e7d32';
+ }
+ if (isFalsy(value)) {
+ return '#9e9e9e';
+ }
+ return '#ff9800';
+};
+
+const isTruthy = (value) => {
+ if (typeof value === 'boolean') return value;
+ if (typeof value === 'number') return value > 0;
+ if (typeof value === 'string') {
+ const normalized = value.toLowerCase();
+ return ['1', 'true', 'y', 'yes', 'open', 'enable'].includes(normalized);
+ }
+ return false;
+};
+
+const isFalsy = (value) => {
+ if (typeof value === 'boolean') return !value;
+ if (typeof value === 'number') return value === 0;
+ if (typeof value === 'string') {
+ const normalized = value.toLowerCase();
+ return ['0', 'false', 'n', 'no', 'close', 'disable'].includes(normalized);
+ }
+ return false;
+};
+
+const formatIndicatorValue = (value) => {
+ if (value === null || value === undefined || value === '') {
+ return '-';
+ }
+ return String(value);
+};
+
+const formatDateTime = (value) => {
+ if (!value) return '-';
+ try {
+ const date = new Date(value);
+ if (Number.isNaN(date.getTime())) {
+ return value;
+ }
+ return format(date, 'MM-dd HH:mm');
+ } catch (error) {
+ return value;
+ }
+};
+
+const TableCellRight = (props) => <TableCell align="right" {...props} />;
+
export default StaPanel;
--
Gitblit v1.9.1