From 1bc33546a044cbc84dd9595c19dbcd9a4e309fc9 Mon Sep 17 00:00:00 2001
From: vincentlu <t1341870251@gmail.com>
Date: 星期六, 10 一月 2026 14:06:10 +0800
Subject: [PATCH] #

---
 zy-acs-flow/src/page/sta/StaPanel.jsx |  205 ++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 162 insertions(+), 43 deletions(-)

diff --git a/zy-acs-flow/src/page/sta/StaPanel.jsx b/zy-acs-flow/src/page/sta/StaPanel.jsx
index 1e8414a..b247569 100644
--- a/zy-acs-flow/src/page/sta/StaPanel.jsx
+++ b/zy-acs-flow/src/page/sta/StaPanel.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from "react";
+import React, { useState, useEffect, useMemo } from "react";
 import {
     Box,
     Card,
@@ -13,11 +13,16 @@
     Divider,
     CircularProgress,
     Stack,
+    FormControl,
+    InputLabel,
+    Select,
+    MenuItem,
 } from '@mui/material';
 import {
     useTranslate,
     useRecordContext,
     useDataProvider,
+    useGetMany,
 } from 'react-admin';
 import { format } from 'date-fns';
 import * as Common from '@/utils/common'
@@ -30,23 +35,60 @@
 ];
 
 const INFO_FIELDS = [
-    { labelKey: 'table.field.sta.uuid', valueKey: 'uuid' },
     { labelKey: 'table.field.sta.zoneId', valueKey: 'zoneId$' },
     { labelKey: 'table.field.sta.staType', valueKey: 'staType$' },
     { labelKey: 'table.field.sta.code', valueKey: 'code$' },
     { labelKey: 'table.field.sta.capacity', valueKey: 'capacity' },
-    { labelKey: 'table.field.sta.offset', valueKey: 'offset' },
     { labelKey: 'table.field.sta.rsvInCnt', valueKey: 'rsvInCnt' },
     { labelKey: 'table.field.sta.rsvOutCnt', valueKey: 'rsvOutCnt' },
-    { labelKey: 'table.field.sta.staSts', valueKey: 'staSts$' },
+    { labelKey: 'table.field.sta.offset', valueKey: 'offset' },
     { labelKey: 'table.field.sta.zpallet', valueKey: 'zpallet' },
 ];
+
+const RESERVE_COLUMN_COUNT = 11;
+const RESERVE_TYPE_OPTIONS = ['IN', 'OUT'];
+const RESERVE_STATE_OPTIONS = ['reserved', 'waiting', 'confirmed', 'canceled', 'timeout'];
 const StaPanel = () => {
     const record = useRecordContext();
     const translate = useTranslate();
     const dataProvider = useDataProvider();
     const [reserves, setReserves] = useState([]);
     const [isReservesLoading, setIsReservesLoading] = useState(false);
+    const [typeFilter, setTypeFilter] = useState('');
+    const [stateFilter, setStateFilter] = useState('');
+    const taskIds = useMemo(() => extractIds(reserves, 'taskId'), [reserves]);
+    const segmentIds = useMemo(() => extractIds(reserves, 'segmentId'), [reserves]);
+    const agvIds = useMemo(() => extractIds(reserves, 'agvId'), [reserves]);
+
+    const { data: taskRecords = [] } = useGetMany('task', { ids: taskIds }, { enabled: taskIds.length > 0 });
+    const { data: segmentRecords = [] } = useGetMany('segment', { ids: segmentIds }, { enabled: segmentIds.length > 0 });
+    const { data: agvRecords = [] } = useGetMany('agv', { ids: agvIds }, { enabled: agvIds.length > 0 });
+
+    const taskLabelMap = useMemo(
+        () => createLabelMap(taskRecords, (item) => item.seqNum),
+        [taskRecords]
+    );
+    const segmentLabelMap = useMemo(
+        () => createLabelMap(segmentRecords, (item) => item.groupId + '-' + item.serial),
+        [segmentRecords]
+    );
+    const agvLabelMap = useMemo(
+        () => createLabelMap(agvRecords, (item) => item.uuid),
+        [agvRecords]
+    );
+
+    const filteredReserves = useMemo(() => {
+        if (!typeFilter && !stateFilter) {
+            return reserves;
+        }
+        return reserves.filter((reserve) => {
+            const matchesType =
+                !typeFilter || normalizeValueKey(reserve?.type) === normalizeValueKey(typeFilter);
+            const matchesState =
+                !stateFilter || normalizeValueKey(reserve?.state) === normalizeValueKey(stateFilter);
+            return matchesType && matchesState;
+        });
+    }, [reserves, typeFilter, stateFilter]);
 
     useEffect(() => {
         if (!record?.id) {
@@ -59,21 +101,18 @@
         setIsReservesLoading(true);
         dataProvider.getList('staReserve', {
             pagination: { page: 1, perPage: 10 },
-            sort: { field: 'updateTime', order: 'DESC' },
+            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);
-            });
+        }).then(({ data }) => {
+            if (!isMounted) return;
+            setReserves(data || []);
+        }).catch(() => {
+            if (!isMounted) return;
+            setReserves([]);
+        }).finally(() => {
+            if (!isMounted) return;
+            setIsReservesLoading(false);
+        });
 
         return () => {
             isMounted = false;
@@ -84,11 +123,11 @@
 
     return (
         <>
-            <Card sx={{ width: { xs: 320, sm: 560, md: 680, lg: 900 }, margin: 'auto', mt: .5, mb: .5 }}>
+            <Card sx={{ maxWidth: '80%', 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={{
+                            <Typography variant="subtitle2" gutterBottom align="left" sx={{
                                 maxWidth: { xs: '140px', sm: '220px', md: '300px', lg: '360px' },
                                 whiteSpace: 'nowrap',
                                 overflow: 'hidden',
@@ -96,19 +135,14 @@
                             }}>
                                 {Common.camelToPascalWithSpaces(translate('table.field.sta.staNo'))}: {record.staNo}
                             </Typography>
-                            <Typography variant="h6" gutterBottom align="right" >
-                                ID: {record.id}
-                            </Typography>
-                        </Grid>
-                    </Grid>
-                    <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 || '-'}
                             </Typography>
                         </Grid>
                     </Grid>
-                    <Box height={16}>&nbsp;</Box>
+
+                    <Box height={12}>&nbsp;</Box>
+
                     <Grid container spacing={2}>
                         {INFO_FIELDS.map(({ labelKey, valueKey }) => (
                             <Grid item xs={12} sm={6} md={4} key={labelKey}>
@@ -122,9 +156,9 @@
 
                     <Divider sx={{ my: 2 }} />
 
-                    <Typography variant="subtitle2" color="textSecondary" gutterBottom>
-                        {translate('common.field.status')} / {translate('table.field.sta.staNo')}
-                    </Typography>
+                    {/* <Typography variant="subtitle2" color="textSecondary" gutterBottom>
+                        {translate('common.field.status')}
+                    </Typography> */}
                     <Grid container spacing={2}>
                         {STATUS_FIELDS.map(({ key, labelKey }) => (
                             <Grid item xs={6} sm={3} key={key}>
@@ -132,29 +166,74 @@
                             </Grid>
                         ))}
                     </Grid>
-
                     <Divider sx={{ my: 2 }} />
 
                     <Box>
                         <Typography variant="subtitle1" gutterBottom>
                             {translate('menu.staReserve')}
                         </Typography>
+                        <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2} mb={2}>
+                            <FormControl size="small" sx={{ minWidth: 160 }}>
+                                <InputLabel id="sta-panel-reserve-type-label">
+                                    {translate('table.field.staReserve.type')}
+                                </InputLabel>
+                                <Select
+                                    labelId="sta-panel-reserve-type-label"
+                                    value={typeFilter}
+                                    label={translate('table.field.staReserve.type')}
+                                    onChange={(event) => setTypeFilter(event.target.value)}
+                                >
+                                    <MenuItem value="">
+                                        {translate('common.action.deselect')}
+                                    </MenuItem>
+                                    {RESERVE_TYPE_OPTIONS.map((option) => (
+                                        <MenuItem key={option} value={option}>
+                                            {formatReserveType(option, translate)}
+                                        </MenuItem>
+                                    ))}
+                                </Select>
+                            </FormControl>
+                            <FormControl size="small" sx={{ minWidth: 160 }}>
+                                <InputLabel id="sta-panel-reserve-state-label">
+                                    {translate('table.field.staReserve.state')}
+                                </InputLabel>
+                                <Select
+                                    labelId="sta-panel-reserve-state-label"
+                                    value={stateFilter}
+                                    label={translate('table.field.staReserve.state')}
+                                    onChange={(event) => setStateFilter(event.target.value)}
+                                >
+                                    <MenuItem value="">
+                                        {translate('common.action.deselect')}
+                                    </MenuItem>
+                                    {RESERVE_STATE_OPTIONS.map((option) => (
+                                        <MenuItem key={option} value={option}>
+                                            {formatReserveState(option, translate)}
+                                        </MenuItem>
+                                    ))}
+                                </Select>
+                            </FormControl>
+                        </Stack>
                         <Table size="small">
                             <TableHead>
                                 <TableRow>
-                                    <TableCell>{translate('table.field.staReserve.name')}</TableCell>
+                                    <TableCell>{translate('table.field.staReserve.uuid')}</TableCell>
+                                    <TableCell>{translate('table.field.staReserve.taskId')}</TableCell>
+                                    <TableCell>{translate('table.field.staReserve.segmentId')}</TableCell>
+                                    <TableCell>{translate('table.field.staReserve.agvId')}</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.expireTime')}</TableCell>
                                     <TableCell>{translate('table.field.staReserve.waitingAt')}</TableCell>
                                     <TableCell>{translate('table.field.staReserve.confirmedAt')}</TableCell>
+                                    <TableCell>{translate('table.field.staReserve.uniqKey')}</TableCell>
                                 </TableRow>
                             </TableHead>
                             <TableBody>
                                 {isReservesLoading && (
                                     <TableRow>
-                                        <TableCell colSpan={7}>
+                                        <TableCell colSpan={RESERVE_COLUMN_COUNT}>
                                             <Box display="flex" alignItems="center" gap={1}>
                                                 <CircularProgress size={16} />
                                                 <Typography variant="body2" color="textSecondary">
@@ -164,24 +243,28 @@
                                         </TableCell>
                                     </TableRow>
                                 )}
-                                {!isReservesLoading && reserves.length === 0 && (
+                                {!isReservesLoading && filteredReserves.length === 0 && (
                                     <TableRow>
-                                        <TableCell colSpan={7}>
+                                        <TableCell colSpan={RESERVE_COLUMN_COUNT}>
                                             <Typography variant="body2" color="textSecondary">
                                                 {translate('ra.navigation.no_results')}
                                             </Typography>
                                         </TableCell>
                                     </TableRow>
                                 )}
-                                {reserves.map((reserve) => (
+                                {filteredReserves.map((reserve) => (
                                     <TableRow key={reserve.id}>
-                                        <TableCell>{reserve.name || '-'}</TableCell>
+                                        <TableCell>{reserve.uuid || '-'}</TableCell>
+                                        <TableCell>{getReferenceLabel(taskLabelMap, reserve.taskId)}</TableCell>
+                                        <TableCell>{getReferenceLabel(segmentLabelMap, reserve.segmentId)}</TableCell>
+                                        <TableCell>{getReferenceLabel(agvLabelMap, reserve.agvId)}</TableCell>
                                         <TableCell>{formatReserveType(reserve.type, translate)}</TableCell>
                                         <TableCellRight>{reserve.qty ?? '-'}</TableCellRight>
                                         <TableCell>{formatReserveState(reserve.state, translate)}</TableCell>
-                                        <TableCell>{reserve.agvId$ || reserve.agvId || '-'}</TableCell>
+                                        <TableCell>{formatDateTime(reserve.expireTime)}</TableCell>
                                         <TableCell>{formatDateTime(reserve.waitingAt)}</TableCell>
                                         <TableCell>{formatDateTime(reserve.confirmedAt)}</TableCell>
+                                        <TableCell>{reserve.uniqKey || '-'}</TableCell>
                                     </TableRow>
                                 ))}
                             </TableBody>
@@ -200,7 +283,7 @@
             <Typography variant="caption" color="textSecondary">
                 {Common.camelToPascalWithSpaces(translate(labelKey))}
             </Typography>
-            <Typography variant="body2" fontWeight={600}>
+            <Typography variant="body2" fontWeight={400}>
                 {formatInfoValue(value)}
             </Typography>
         </Stack>
@@ -240,7 +323,7 @@
                     height: 14,
                     borderRadius: '50%',
                     backgroundColor: color,
-                    boxShadow: `0 0 6px ${color}`,
+                    boxShadow: `0 0 3px ${color}`,
                     border: '1px solid rgba(0,0,0,0.12)'
                 }} />
                 <Typography variant="body2">
@@ -258,7 +341,7 @@
     if (isFalsy(value)) {
         return '#9e9e9e';
     }
-    return '#ff9800';
+    return '#d40000ff';
 };
 
 const isTruthy = (value) => {
@@ -288,6 +371,37 @@
     return String(value);
 };
 
+const extractIds = (items, key) => {
+    if (!items || items.length === 0) return [];
+    const unique = new Set();
+    items.forEach((item) => {
+        const value = item?.[key];
+        if (value !== undefined && value !== null && value !== '') {
+            unique.add(value);
+        }
+    });
+    return Array.from(unique);
+};
+
+const createLabelMap = (records, getLabel) => {
+    if (!records || records.length === 0) return {};
+    return records.reduce((acc, record) => {
+        if (record?.id === undefined || record?.id === null) {
+            return acc;
+        }
+        const label = getLabel(record);
+        acc[record.id] = label ?? record.id;
+        return acc;
+    }, {});
+};
+
+const getReferenceLabel = (map, id) => {
+    if (id === undefined || id === null || id === '') {
+        return '-';
+    }
+    return map?.[id] || '-';
+};
+
 const formatReserveType = (value, translate) =>
     formatReserveEnum(value, translate, 'type');
 
@@ -296,6 +410,11 @@
         cancelled: 'canceled',
     });
 
+const getReserveRelationValue = (reserve, key) => {
+    if (!reserve) return '-';
+    return reserve[`${key}$`] || reserve[key] || '-';
+};
+
 const formatReserveEnum = (value, translate, enumType, overrides = {}) => {
     const normalized = normalizeValueKey(value);
     if (!normalized) {

--
Gitblit v1.9.1